Z|[-+][0-9]{2}:[0-9]{2})
- )?
- )?
- )?
- """, re.VERBOSE)
-
-##
-# An object that represents Adobe XMP metadata.
-class XmpInformation(PdfObject):
-
- def __init__(self, stream):
- self.stream = stream
- docRoot = parseString(self.stream.getData())
- self.rdfRoot = docRoot.getElementsByTagNameNS(RDF_NAMESPACE, "RDF")[0]
- self.cache = {}
-
- def writeToStream(self, stream, encryption_key):
- self.stream.writeToStream(stream, encryption_key)
-
- def getElement(self, aboutUri, namespace, name):
- for desc in self.rdfRoot.getElementsByTagNameNS(RDF_NAMESPACE, "Description"):
- if desc.getAttributeNS(RDF_NAMESPACE, "about") == aboutUri:
- attr = desc.getAttributeNodeNS(namespace, name)
- if attr != None:
- yield attr
- for element in desc.getElementsByTagNameNS(namespace, name):
- yield element
-
- def getNodesInNamespace(self, aboutUri, namespace):
- for desc in self.rdfRoot.getElementsByTagNameNS(RDF_NAMESPACE, "Description"):
- if desc.getAttributeNS(RDF_NAMESPACE, "about") == aboutUri:
- for i in range(desc.attributes.length):
- attr = desc.attributes.item(i)
- if attr.namespaceURI == namespace:
- yield attr
- for child in desc.childNodes:
- if child.namespaceURI == namespace:
- yield child
-
- def _getText(self, element):
- text = ""
- for child in element.childNodes:
- if child.nodeType == child.TEXT_NODE:
- text += child.data
- return text
-
- def _converter_string(value):
- return value
-
- def _converter_date(value):
- m = iso8601.match(value)
- year = int(m.group("year"))
- month = int(m.group("month") or "1")
- day = int(m.group("day") or "1")
- hour = int(m.group("hour") or "0")
- minute = int(m.group("minute") or "0")
- second = decimal.Decimal(m.group("second") or "0")
- seconds = second.to_integral(decimal.ROUND_FLOOR)
- milliseconds = (second - seconds) * 1000000
- tzd = m.group("tzd") or "Z"
- dt = datetime.datetime(year, month, day, hour, minute, seconds, milliseconds)
- if tzd != "Z":
- tzd_hours, tzd_minutes = [int(x) for x in tzd.split(":")]
- tzd_hours *= -1
- if tzd_hours < 0:
- tzd_minutes *= -1
- dt = dt + datetime.timedelta(hours=tzd_hours, minutes=tzd_minutes)
- return dt
- _test_converter_date = staticmethod(_converter_date)
-
- def _getter_bag(namespace, name, converter):
- def get(self):
- cached = self.cache.get(namespace, {}).get(name)
- if cached:
- return cached
- retval = []
- for element in self.getElement("", namespace, name):
- bags = element.getElementsByTagNameNS(RDF_NAMESPACE, "Bag")
- if len(bags):
- for bag in bags:
- for item in bag.getElementsByTagNameNS(RDF_NAMESPACE, "li"):
- value = self._getText(item)
- value = converter(value)
- retval.append(value)
- ns_cache = self.cache.setdefault(namespace, {})
- ns_cache[name] = retval
- return retval
- return get
-
- def _getter_seq(namespace, name, converter):
- def get(self):
- cached = self.cache.get(namespace, {}).get(name)
- if cached:
- return cached
- retval = []
- for element in self.getElement("", namespace, name):
- seqs = element.getElementsByTagNameNS(RDF_NAMESPACE, "Seq")
- if len(seqs):
- for seq in seqs:
- for item in seq.getElementsByTagNameNS(RDF_NAMESPACE, "li"):
- value = self._getText(item)
- value = converter(value)
- retval.append(value)
- else:
- value = converter(self._getText(element))
- retval.append(value)
- ns_cache = self.cache.setdefault(namespace, {})
- ns_cache[name] = retval
- return retval
- return get
-
- def _getter_langalt(namespace, name, converter):
- def get(self):
- cached = self.cache.get(namespace, {}).get(name)
- if cached:
- return cached
- retval = {}
- for element in self.getElement("", namespace, name):
- alts = element.getElementsByTagNameNS(RDF_NAMESPACE, "Alt")
- if len(alts):
- for alt in alts:
- for item in alt.getElementsByTagNameNS(RDF_NAMESPACE, "li"):
- value = self._getText(item)
- value = converter(value)
- retval[item.getAttribute("xml:lang")] = value
- else:
- retval["x-default"] = converter(self._getText(element))
- ns_cache = self.cache.setdefault(namespace, {})
- ns_cache[name] = retval
- return retval
- return get
-
- def _getter_single(namespace, name, converter):
- def get(self):
- cached = self.cache.get(namespace, {}).get(name)
- if cached:
- return cached
- value = None
- for element in self.getElement("", namespace, name):
- if element.nodeType == element.ATTRIBUTE_NODE:
- value = element.nodeValue
- else:
- value = self._getText(element)
- break
- if value != None:
- value = converter(value)
- ns_cache = self.cache.setdefault(namespace, {})
- ns_cache[name] = value
- return value
- return get
-
- ##
- # Contributors to the resource (other than the authors). An unsorted
- # array of names.
- # Stability: Added in v1.12, will exist for all future v1.x releases.
- dc_contributor = property(_getter_bag(DC_NAMESPACE, "contributor", _converter_string))
-
- ##
- # Text describing the extent or scope of the resource.
- #
Stability: Added in v1.12, will exist for all future v1.x releases.
- dc_coverage = property(_getter_single(DC_NAMESPACE, "coverage", _converter_string))
-
- ##
- # A sorted array of names of the authors of the resource, listed in order
- # of precedence.
- #
Stability: Added in v1.12, will exist for all future v1.x releases.
- dc_creator = property(_getter_seq(DC_NAMESPACE, "creator", _converter_string))
-
- ##
- # A sorted array of dates (datetime.datetime instances) of signifigance to
- # the resource. The dates and times are in UTC.
- #
Stability: Added in v1.12, will exist for all future v1.x releases.
- dc_date = property(_getter_seq(DC_NAMESPACE, "date", _converter_date))
-
- ##
- # A language-keyed dictionary of textual descriptions of the content of the
- # resource.
- #
Stability: Added in v1.12, will exist for all future v1.x releases.
- dc_description = property(_getter_langalt(DC_NAMESPACE, "description", _converter_string))
-
- ##
- # The mime-type of the resource.
- #
Stability: Added in v1.12, will exist for all future v1.x releases.
- dc_format = property(_getter_single(DC_NAMESPACE, "format", _converter_string))
-
- ##
- # Unique identifier of the resource.
- #
Stability: Added in v1.12, will exist for all future v1.x releases.
- dc_identifier = property(_getter_single(DC_NAMESPACE, "identifier", _converter_string))
-
- ##
- # An unordered array specifying the languages used in the resource.
- #
Stability: Added in v1.12, will exist for all future v1.x releases.
- dc_language = property(_getter_bag(DC_NAMESPACE, "language", _converter_string))
-
- ##
- # An unordered array of publisher names.
- #
Stability: Added in v1.12, will exist for all future v1.x releases.
- dc_publisher = property(_getter_bag(DC_NAMESPACE, "publisher", _converter_string))
-
- ##
- # An unordered array of text descriptions of relationships to other
- # documents.
- #
Stability: Added in v1.12, will exist for all future v1.x releases.
- dc_relation = property(_getter_bag(DC_NAMESPACE, "relation", _converter_string))
-
- ##
- # A language-keyed dictionary of textual descriptions of the rights the
- # user has to this resource.
- #
Stability: Added in v1.12, will exist for all future v1.x releases.
- dc_rights = property(_getter_langalt(DC_NAMESPACE, "rights", _converter_string))
-
- ##
- # Unique identifier of the work from which this resource was derived.
- #
Stability: Added in v1.12, will exist for all future v1.x releases.
- dc_source = property(_getter_single(DC_NAMESPACE, "source", _converter_string))
-
- ##
- # An unordered array of descriptive phrases or keywrods that specify the
- # topic of the content of the resource.
- #
Stability: Added in v1.12, will exist for all future v1.x releases.
- dc_subject = property(_getter_bag(DC_NAMESPACE, "subject", _converter_string))
-
- ##
- # A language-keyed dictionary of the title of the resource.
- #
Stability: Added in v1.12, will exist for all future v1.x releases.
- dc_title = property(_getter_langalt(DC_NAMESPACE, "title", _converter_string))
-
- ##
- # An unordered array of textual descriptions of the document type.
- #
Stability: Added in v1.12, will exist for all future v1.x releases.
- dc_type = property(_getter_bag(DC_NAMESPACE, "type", _converter_string))
-
- ##
- # An unformatted text string representing document keywords.
- #
Stability: Added in v1.12, will exist for all future v1.x releases.
- pdf_keywords = property(_getter_single(PDF_NAMESPACE, "Keywords", _converter_string))
-
- ##
- # The PDF file version, for example 1.0, 1.3.
- #
Stability: Added in v1.12, will exist for all future v1.x releases.
- pdf_pdfversion = property(_getter_single(PDF_NAMESPACE, "PDFVersion", _converter_string))
-
- ##
- # The name of the tool that created the PDF document.
- #
Stability: Added in v1.12, will exist for all future v1.x releases.
- pdf_producer = property(_getter_single(PDF_NAMESPACE, "Producer", _converter_string))
-
- ##
- # The date and time the resource was originally created. The date and
- # time are returned as a UTC datetime.datetime object.
- #
Stability: Added in v1.12, will exist for all future v1.x releases.
- xmp_createDate = property(_getter_single(XMP_NAMESPACE, "CreateDate", _converter_date))
-
- ##
- # The date and time the resource was last modified. The date and time
- # are returned as a UTC datetime.datetime object.
- #
Stability: Added in v1.12, will exist for all future v1.x releases.
- xmp_modifyDate = property(_getter_single(XMP_NAMESPACE, "ModifyDate", _converter_date))
-
- ##
- # The date and time that any metadata for this resource was last
- # changed. The date and time are returned as a UTC datetime.datetime
- # object.
- #
Stability: Added in v1.12, will exist for all future v1.x releases.
- xmp_metadataDate = property(_getter_single(XMP_NAMESPACE, "MetadataDate", _converter_date))
-
- ##
- # The name of the first known tool used to create the resource.
- #
Stability: Added in v1.12, will exist for all future v1.x releases.
- xmp_creatorTool = property(_getter_single(XMP_NAMESPACE, "CreatorTool", _converter_string))
-
- ##
- # The common identifier for all versions and renditions of this resource.
- #
Stability: Added in v1.12, will exist for all future v1.x releases.
- xmpmm_documentId = property(_getter_single(XMPMM_NAMESPACE, "DocumentID", _converter_string))
-
- ##
- # An identifier for a specific incarnation of a document, updated each
- # time a file is saved.
- #
Stability: Added in v1.12, will exist for all future v1.x releases.
- xmpmm_instanceId = property(_getter_single(XMPMM_NAMESPACE, "InstanceID", _converter_string))
-
- def custom_properties(self):
- if not hasattr(self, "_custom_properties"):
- self._custom_properties = {}
- for node in self.getNodesInNamespace("", PDFX_NAMESPACE):
- key = node.localName
- while True:
- # see documentation about PDFX_NAMESPACE earlier in file
- idx = key.find(u"\u2182")
- if idx == -1:
- break
- key = key[:idx] + chr(int(key[idx+1:idx+5], base=16)) + key[idx+5:]
- if node.nodeType == node.ATTRIBUTE_NODE:
- value = node.nodeValue
- else:
- value = self._getText(node)
- self._custom_properties[key] = value
- return self._custom_properties
-
- ##
- # Retrieves custom metadata properties defined in the undocumented pdfx
- # metadata schema.
- #
Stability: Added in v1.12, will exist for all future v1.x releases.
- # @return Returns a dictionary of key/value items for custom metadata
- # properties.
- custom_properties = property(custom_properties)
-
-
+import re
+import datetime
+import decimal
+from generic import PdfObject
+from xml.dom import getDOMImplementation
+from xml.dom.minidom import parseString
+
+RDF_NAMESPACE = "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+DC_NAMESPACE = "http://purl.org/dc/elements/1.1/"
+XMP_NAMESPACE = "http://ns.adobe.com/xap/1.0/"
+PDF_NAMESPACE = "http://ns.adobe.com/pdf/1.3/"
+XMPMM_NAMESPACE = "http://ns.adobe.com/xap/1.0/mm/"
+
+# What is the PDFX namespace, you might ask? I might ask that too. It's
+# a completely undocumented namespace used to place "custom metadata"
+# properties, which are arbitrary metadata properties with no semantic or
+# documented meaning. Elements in the namespace are key/value-style storage,
+# where the element name is the key and the content is the value. The keys
+# are transformed into valid XML identifiers by substituting an invalid
+# identifier character with \u2182 followed by the unicode hex ID of the
+# original character. A key like "my car" is therefore "my\u21820020car".
+#
+# \u2182, in case you're wondering, is the unicode character
+# \u{ROMAN NUMERAL TEN THOUSAND}, a straightforward and obvious choice for
+# escaping characters.
+#
+# Intentional users of the pdfx namespace should be shot on sight. A
+# custom data schema and sensical XML elements could be used instead, as is
+# suggested by Adobe's own documentation on XMP (under "Extensibility of
+# Schemas").
+#
+# Information presented here on the /pdfx/ schema is a result of limited
+# reverse engineering, and does not constitute a full specification.
+PDFX_NAMESPACE = "http://ns.adobe.com/pdfx/1.3/"
+
+iso8601 = re.compile("""
+ (?P[0-9]{4})
+ (-
+ (?P[0-9]{2})
+ (-
+ (?P[0-9]+)
+ (T
+ (?P[0-9]{2}):
+ (?P[0-9]{2})
+ (:(?P[0-9]{2}(.[0-9]+)?))?
+ (?PZ|[-+][0-9]{2}:[0-9]{2})
+ )?
+ )?
+ )?
+ """, re.VERBOSE)
+
+##
+# An object that represents Adobe XMP metadata.
+class XmpInformation(PdfObject):
+
+ def __init__(self, stream):
+ self.stream = stream
+ docRoot = parseString(self.stream.getData())
+ self.rdfRoot = docRoot.getElementsByTagNameNS(RDF_NAMESPACE, "RDF")[0]
+ self.cache = {}
+
+ def writeToStream(self, stream, encryption_key):
+ self.stream.writeToStream(stream, encryption_key)
+
+ def getElement(self, aboutUri, namespace, name):
+ for desc in self.rdfRoot.getElementsByTagNameNS(RDF_NAMESPACE, "Description"):
+ if desc.getAttributeNS(RDF_NAMESPACE, "about") == aboutUri:
+ attr = desc.getAttributeNodeNS(namespace, name)
+ if attr != None:
+ yield attr
+ for element in desc.getElementsByTagNameNS(namespace, name):
+ yield element
+
+ def getNodesInNamespace(self, aboutUri, namespace):
+ for desc in self.rdfRoot.getElementsByTagNameNS(RDF_NAMESPACE, "Description"):
+ if desc.getAttributeNS(RDF_NAMESPACE, "about") == aboutUri:
+ for i in range(desc.attributes.length):
+ attr = desc.attributes.item(i)
+ if attr.namespaceURI == namespace:
+ yield attr
+ for child in desc.childNodes:
+ if child.namespaceURI == namespace:
+ yield child
+
+ def _getText(self, element):
+ text = ""
+ for child in element.childNodes:
+ if child.nodeType == child.TEXT_NODE:
+ text += child.data
+ return text
+
+ def _converter_string(value):
+ return value
+
+ def _converter_date(value):
+ m = iso8601.match(value)
+ year = int(m.group("year"))
+ month = int(m.group("month") or "1")
+ day = int(m.group("day") or "1")
+ hour = int(m.group("hour") or "0")
+ minute = int(m.group("minute") or "0")
+ second = decimal.Decimal(m.group("second") or "0")
+ seconds = second.to_integral(decimal.ROUND_FLOOR)
+ milliseconds = (second - seconds) * 1000000
+ tzd = m.group("tzd") or "Z"
+ dt = datetime.datetime(year, month, day, hour, minute, seconds, milliseconds)
+ if tzd != "Z":
+ tzd_hours, tzd_minutes = [int(x) for x in tzd.split(":")]
+ tzd_hours *= -1
+ if tzd_hours < 0:
+ tzd_minutes *= -1
+ dt = dt + datetime.timedelta(hours=tzd_hours, minutes=tzd_minutes)
+ return dt
+ _test_converter_date = staticmethod(_converter_date)
+
+ def _getter_bag(namespace, name, converter):
+ def get(self):
+ cached = self.cache.get(namespace, {}).get(name)
+ if cached:
+ return cached
+ retval = []
+ for element in self.getElement("", namespace, name):
+ bags = element.getElementsByTagNameNS(RDF_NAMESPACE, "Bag")
+ if len(bags):
+ for bag in bags:
+ for item in bag.getElementsByTagNameNS(RDF_NAMESPACE, "li"):
+ value = self._getText(item)
+ value = converter(value)
+ retval.append(value)
+ ns_cache = self.cache.setdefault(namespace, {})
+ ns_cache[name] = retval
+ return retval
+ return get
+
+ def _getter_seq(namespace, name, converter):
+ def get(self):
+ cached = self.cache.get(namespace, {}).get(name)
+ if cached:
+ return cached
+ retval = []
+ for element in self.getElement("", namespace, name):
+ seqs = element.getElementsByTagNameNS(RDF_NAMESPACE, "Seq")
+ if len(seqs):
+ for seq in seqs:
+ for item in seq.getElementsByTagNameNS(RDF_NAMESPACE, "li"):
+ value = self._getText(item)
+ value = converter(value)
+ retval.append(value)
+ else:
+ value = converter(self._getText(element))
+ retval.append(value)
+ ns_cache = self.cache.setdefault(namespace, {})
+ ns_cache[name] = retval
+ return retval
+ return get
+
+ def _getter_langalt(namespace, name, converter):
+ def get(self):
+ cached = self.cache.get(namespace, {}).get(name)
+ if cached:
+ return cached
+ retval = {}
+ for element in self.getElement("", namespace, name):
+ alts = element.getElementsByTagNameNS(RDF_NAMESPACE, "Alt")
+ if len(alts):
+ for alt in alts:
+ for item in alt.getElementsByTagNameNS(RDF_NAMESPACE, "li"):
+ value = self._getText(item)
+ value = converter(value)
+ retval[item.getAttribute("xml:lang")] = value
+ else:
+ retval["x-default"] = converter(self._getText(element))
+ ns_cache = self.cache.setdefault(namespace, {})
+ ns_cache[name] = retval
+ return retval
+ return get
+
+ def _getter_single(namespace, name, converter):
+ def get(self):
+ cached = self.cache.get(namespace, {}).get(name)
+ if cached:
+ return cached
+ value = None
+ for element in self.getElement("", namespace, name):
+ if element.nodeType == element.ATTRIBUTE_NODE:
+ value = element.nodeValue
+ else:
+ value = self._getText(element)
+ break
+ if value != None:
+ value = converter(value)
+ ns_cache = self.cache.setdefault(namespace, {})
+ ns_cache[name] = value
+ return value
+ return get
+
+ ##
+ # Contributors to the resource (other than the authors). An unsorted
+ # array of names.
+ # Stability: Added in v1.12, will exist for all future v1.x releases.
+ dc_contributor = property(_getter_bag(DC_NAMESPACE, "contributor", _converter_string))
+
+ ##
+ # Text describing the extent or scope of the resource.
+ #
Stability: Added in v1.12, will exist for all future v1.x releases.
+ dc_coverage = property(_getter_single(DC_NAMESPACE, "coverage", _converter_string))
+
+ ##
+ # A sorted array of names of the authors of the resource, listed in order
+ # of precedence.
+ #
Stability: Added in v1.12, will exist for all future v1.x releases.
+ dc_creator = property(_getter_seq(DC_NAMESPACE, "creator", _converter_string))
+
+ ##
+ # A sorted array of dates (datetime.datetime instances) of signifigance to
+ # the resource. The dates and times are in UTC.
+ #
Stability: Added in v1.12, will exist for all future v1.x releases.
+ dc_date = property(_getter_seq(DC_NAMESPACE, "date", _converter_date))
+
+ ##
+ # A language-keyed dictionary of textual descriptions of the content of the
+ # resource.
+ #
Stability: Added in v1.12, will exist for all future v1.x releases.
+ dc_description = property(_getter_langalt(DC_NAMESPACE, "description", _converter_string))
+
+ ##
+ # The mime-type of the resource.
+ #
Stability: Added in v1.12, will exist for all future v1.x releases.
+ dc_format = property(_getter_single(DC_NAMESPACE, "format", _converter_string))
+
+ ##
+ # Unique identifier of the resource.
+ #
Stability: Added in v1.12, will exist for all future v1.x releases.
+ dc_identifier = property(_getter_single(DC_NAMESPACE, "identifier", _converter_string))
+
+ ##
+ # An unordered array specifying the languages used in the resource.
+ #
Stability: Added in v1.12, will exist for all future v1.x releases.
+ dc_language = property(_getter_bag(DC_NAMESPACE, "language", _converter_string))
+
+ ##
+ # An unordered array of publisher names.
+ #
Stability: Added in v1.12, will exist for all future v1.x releases.
+ dc_publisher = property(_getter_bag(DC_NAMESPACE, "publisher", _converter_string))
+
+ ##
+ # An unordered array of text descriptions of relationships to other
+ # documents.
+ #
Stability: Added in v1.12, will exist for all future v1.x releases.
+ dc_relation = property(_getter_bag(DC_NAMESPACE, "relation", _converter_string))
+
+ ##
+ # A language-keyed dictionary of textual descriptions of the rights the
+ # user has to this resource.
+ #
Stability: Added in v1.12, will exist for all future v1.x releases.
+ dc_rights = property(_getter_langalt(DC_NAMESPACE, "rights", _converter_string))
+
+ ##
+ # Unique identifier of the work from which this resource was derived.
+ #
Stability: Added in v1.12, will exist for all future v1.x releases.
+ dc_source = property(_getter_single(DC_NAMESPACE, "source", _converter_string))
+
+ ##
+ # An unordered array of descriptive phrases or keywrods that specify the
+ # topic of the content of the resource.
+ #
Stability: Added in v1.12, will exist for all future v1.x releases.
+ dc_subject = property(_getter_bag(DC_NAMESPACE, "subject", _converter_string))
+
+ ##
+ # A language-keyed dictionary of the title of the resource.
+ #
Stability: Added in v1.12, will exist for all future v1.x releases.
+ dc_title = property(_getter_langalt(DC_NAMESPACE, "title", _converter_string))
+
+ ##
+ # An unordered array of textual descriptions of the document type.
+ #
Stability: Added in v1.12, will exist for all future v1.x releases.
+ dc_type = property(_getter_bag(DC_NAMESPACE, "type", _converter_string))
+
+ ##
+ # An unformatted text string representing document keywords.
+ #
Stability: Added in v1.12, will exist for all future v1.x releases.
+ pdf_keywords = property(_getter_single(PDF_NAMESPACE, "Keywords", _converter_string))
+
+ ##
+ # The PDF file version, for example 1.0, 1.3.
+ #
Stability: Added in v1.12, will exist for all future v1.x releases.
+ pdf_pdfversion = property(_getter_single(PDF_NAMESPACE, "PDFVersion", _converter_string))
+
+ ##
+ # The name of the tool that created the PDF document.
+ #
Stability: Added in v1.12, will exist for all future v1.x releases.
+ pdf_producer = property(_getter_single(PDF_NAMESPACE, "Producer", _converter_string))
+
+ ##
+ # The date and time the resource was originally created. The date and
+ # time are returned as a UTC datetime.datetime object.
+ #
Stability: Added in v1.12, will exist for all future v1.x releases.
+ xmp_createDate = property(_getter_single(XMP_NAMESPACE, "CreateDate", _converter_date))
+
+ ##
+ # The date and time the resource was last modified. The date and time
+ # are returned as a UTC datetime.datetime object.
+ #
Stability: Added in v1.12, will exist for all future v1.x releases.
+ xmp_modifyDate = property(_getter_single(XMP_NAMESPACE, "ModifyDate", _converter_date))
+
+ ##
+ # The date and time that any metadata for this resource was last
+ # changed. The date and time are returned as a UTC datetime.datetime
+ # object.
+ #
Stability: Added in v1.12, will exist for all future v1.x releases.
+ xmp_metadataDate = property(_getter_single(XMP_NAMESPACE, "MetadataDate", _converter_date))
+
+ ##
+ # The name of the first known tool used to create the resource.
+ #
Stability: Added in v1.12, will exist for all future v1.x releases.
+ xmp_creatorTool = property(_getter_single(XMP_NAMESPACE, "CreatorTool", _converter_string))
+
+ ##
+ # The common identifier for all versions and renditions of this resource.
+ #
Stability: Added in v1.12, will exist for all future v1.x releases.
+ xmpmm_documentId = property(_getter_single(XMPMM_NAMESPACE, "DocumentID", _converter_string))
+
+ ##
+ # An identifier for a specific incarnation of a document, updated each
+ # time a file is saved.
+ #
Stability: Added in v1.12, will exist for all future v1.x releases.
+ xmpmm_instanceId = property(_getter_single(XMPMM_NAMESPACE, "InstanceID", _converter_string))
+
+ def custom_properties(self):
+ if not hasattr(self, "_custom_properties"):
+ self._custom_properties = {}
+ for node in self.getNodesInNamespace("", PDFX_NAMESPACE):
+ key = node.localName
+ while True:
+ # see documentation about PDFX_NAMESPACE earlier in file
+ idx = key.find(u"\u2182")
+ if idx == -1:
+ break
+ key = key[:idx] + chr(int(key[idx+1:idx+5], base=16)) + key[idx+5:]
+ if node.nodeType == node.ATTRIBUTE_NODE:
+ value = node.nodeValue
+ else:
+ value = self._getText(node)
+ self._custom_properties[key] = value
+ return self._custom_properties
+
+ ##
+ # Retrieves custom metadata properties defined in the undocumented pdfx
+ # metadata schema.
+ #
Stability: Added in v1.12, will exist for all future v1.x releases.
+ # @return Returns a dictionary of key/value items for custom metadata
+ # properties.
+ custom_properties = property(custom_properties)
+
+
From 438232f6bef8e0b2d1a089dddf0c91685e1da001 Mon Sep 17 00:00:00 2001
From: Olivier Dony
Date: Mon, 10 Jan 2011 16:19:12 +0100
Subject: [PATCH 03/50] [FIX] res_lang: sanitize time_format for python's
strftime
POSIX's strftime has a number of 'compound' formats for quick-formatting of time spans (e.g. %T for %H:%M:%S) which Python's own strftime does not support => error when trying to use them
manually expand those compound formats to their atoms
refs:
http://pubs.opengroup.org/onlinepubs/009695399/functions/strftime.html
http://docs.python.org/library/time.html#time.strftime
lp bug: https://launchpad.net/bugs/698134 fixed
lp bug: https://launchpad.net/bugs/697833 fixed
bzr revid: odo@openerp.com-20110110151912-ysu06271z40hxxho
---
bin/addons/base/res/res_lang.py | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/bin/addons/base/res/res_lang.py b/bin/addons/base/res/res_lang.py
index abaa81ed357..d19e9389884 100644
--- a/bin/addons/base/res/res_lang.py
+++ b/bin/addons/base/res/res_lang.py
@@ -66,17 +66,29 @@ class lang(osv.osv):
def fix_xa0(s):
+ """Fix badly-encoded non-breaking space Unicode character from locale.localeconv(),
+ coercing to utf-8, as some platform seem to output localeconv() in their system
+ encoding, e.g. Windows-1252"""
if s == '\xa0':
return '\xc2\xa0'
return s
+ def fix_time_format(format):
+ """Python's strftime does not support all the compound formats
+ from C's strftime, as returned by locale.nl_langinfo().
+ Under Ubuntu at least, we encounter %T or %r for some locales."""
+ format = format.replace('%T', '%H:%M:%S')\
+ .replace('%r', '%I:%M:%S %p')\
+ .replace('%R', '%H:%M')
+ return str(format)
+
lang_info = {
'code': lang,
'iso_code': iso_lang,
'name': lang_name,
'translatable': 1,
'date_format' : str(locale.nl_langinfo(locale.D_FMT).replace('%y', '%Y')),
- 'time_format' : str(locale.nl_langinfo(locale.T_FMT)),
+ 'time_format' : fix_time_format(locale.nl_langinfo(locale.T_FMT)),
'decimal_point' : fix_xa0(str(locale.localeconv()['decimal_point'])),
'thousands_sep' : fix_xa0(str(locale.localeconv()['thousands_sep'])),
}
From 849e49a2b4a5882c7cdb81558d03897e8fdaf289 Mon Sep 17 00:00:00 2001
From: Fabien Pinckaers
Date: Mon, 10 Jan 2011 18:03:28 +0100
Subject: [PATCH 04/50] [IMP] index on partner's ref
bzr revid: fp@tinyerp.com-20110110170328-85g072pod3v2x3d9
---
bin/addons/base/res/ir_property.py | 8 +++++++-
bin/addons/base/res/partner/partner.py | 4 ++--
bin/osv/fields.py | 1 +
3 files changed, 10 insertions(+), 3 deletions(-)
diff --git a/bin/addons/base/res/ir_property.py b/bin/addons/base/res/ir_property.py
index 43451f824a1..6ccbf06187e 100644
--- a/bin/addons/base/res/ir_property.py
+++ b/bin/addons/base/res/ir_property.py
@@ -136,6 +136,7 @@ class ir_property(osv.osv):
return super(ir_property, self).create(cr, uid, self._update_values(cr, uid, None, values), context=context)
def get_by_record(self, cr, uid, record, context=None):
+ print 'record type', record.type
if record.type in ('char', 'text'):
return record.value_text
elif record.type == 'float':
@@ -157,13 +158,18 @@ class ir_property(osv.osv):
return False
def get(self, cr, uid, name, model, res_id=False, context={}):
+ print 'Property Get', name, model
domain = self._get_domain(cr, uid, name, model, context=context)
if domain is not None:
domain = [('res_id', '=', res_id)] + domain
nid = self.search(cr, uid, domain, context=context)
+ print 'DOMAIN', domain, nid
if not nid: return False
record = self.browse(cr, uid, nid[0], context=context)
- return self.get_by_record(cr, uid, record, context=context)
+ print 'RECORD', record
+ res = self.get_by_record(cr, uid, record, context=context)
+ print 'Result', res
+ return res
return False
def _get_domain_default(self, cr, uid, prop_name, model, context=None):
diff --git a/bin/addons/base/res/partner/partner.py b/bin/addons/base/res/partner/partner.py
index 8a6a6197ab3..d7f5b11a71b 100644
--- a/bin/addons/base/res/partner/partner.py
+++ b/bin/addons/base/res/partner/partner.py
@@ -113,9 +113,9 @@ class res_partner(osv.osv):
'name': fields.char('Name', size=128, required=True, select=True),
'date': fields.date('Date', select=1),
'title': fields.many2one('res.partner.title','Partner Form'),
- 'parent_id': fields.many2one('res.partner','Parent Partner', select=2),
+ 'parent_id': fields.many2one('res.partner','Parent Partner'),
'child_ids': fields.one2many('res.partner', 'parent_id', 'Partner Ref.'),
- 'ref': fields.char('Reference', size=64),
+ 'ref': fields.char('Reference', size=64, select=1),
'lang': fields.selection(_lang_get, 'Language', size=5, help="If the selected language is loaded in the system, all documents related to this partner will be printed in this language. If not, it will be english."),
'user_id': fields.many2one('res.users', 'Salesman', help='The internal user that is in charge of communicating with this partner if any.'),
'vat': fields.char('VAT',size=32 ,help="Value Added Tax number. Check the box if the partner is subjected to the VAT. Used by the VAT legal statement."),
diff --git a/bin/osv/fields.py b/bin/osv/fields.py
index 6f720ea4669..5173b7e29cf 100644
--- a/bin/osv/fields.py
+++ b/bin/osv/fields.py
@@ -983,6 +983,7 @@ class property(function):
prop = obj.pool.get('ir.property')
domain = [('fields_id.model', '=', obj._name), ('fields_id.name','in',prop_name), ('res_id','=',False)]
ids = prop.search(cr, uid, domain, context=context)
+ print 'GOT IDS', ids, 'FOR', prop_name
replaces = {}
default_value = {}.fromkeys(prop_name, False)
for prop_rec in prop.browse(cr, uid, ids, context=context):
From db0f75705c99e2fa2f61ca125b6e3a891bb14203 Mon Sep 17 00:00:00 2001
From: "aag (OpenERP)"
Date: Fri, 7 Jan 2011 17:45:07 +0530
Subject: [PATCH 07/50] [ADD] purchase_double_validation:Add yml for Double
validation
bzr revid: aag@tinyerp.co.in-20110107121507-yqcn0cusopw2fu57
---
.../purchase_double_validation/__openerp__.py | 3 +
.../test/purchase_double_validation_test.yml | 117 ++++++++++++++++++
2 files changed, 120 insertions(+)
create mode 100644 addons/purchase_double_validation/test/purchase_double_validation_test.yml
diff --git a/addons/purchase_double_validation/__openerp__.py b/addons/purchase_double_validation/__openerp__.py
index ca21dfc6077..18f5b4a80b9 100644
--- a/addons/purchase_double_validation/__openerp__.py
+++ b/addons/purchase_double_validation/__openerp__.py
@@ -35,6 +35,9 @@
'purchase_double_validation_installer.xml'
],
'demo_xml': [],
+ 'test': [
+ 'test/purchase_double_validation_test.yml',
+ ],
'installable': True,
'active': False,
diff --git a/addons/purchase_double_validation/test/purchase_double_validation_test.yml b/addons/purchase_double_validation/test/purchase_double_validation_test.yml
new file mode 100644
index 00000000000..ecc8d08a493
--- /dev/null
+++ b/addons/purchase_double_validation/test/purchase_double_validation_test.yml
@@ -0,0 +1,117 @@
+-
+ I Create new limit amount from Configure Limit Amount for Purchase wizard.
+-
+ !record {model: purchase.double.validation.installer, id: purchase_double_validation_installer_1}:
+ limit_amount: 3000
+-
+ Then I set the Limit Amount
+-
+ !python {model: purchase.double.validation.installer}: |
+ self.execute(cr, uid, [ref("purchase_double_validation_installer_1")])
+-
+ In order to test the purchase double validation flow,I start by creating a new product 'MOB1'
+-
+ !record {model: product.product, id: product_product_mob1}:
+ categ_id: 'product.product_category_3'
+ cost_method: standard
+ mes_type: fixed
+ name: MOB1
+ price_margin: 2.0
+ procure_method: make_to_stock
+ property_stock_inventory: stock.location_inventory
+ property_stock_procurement: stock.location_procurement
+ property_stock_production: stock.location_production
+ seller_delay: '1'
+ standard_price: 2000.0
+ supply_method: buy
+ type: product
+ uom_id: product.product_uom_unit
+ uom_po_id: product.product_uom_unit
+ volume: 0.0
+ warranty: 0.0
+ weight: 0.0
+ weight_net: 0.0
+-
+ In order to test the purchase double validation flow,I create a new record where "invoice_method" is From Order.
+-
+ Test for purchase double validation in which Total > = Limit Amount.
+-
+ I create purchase order for MOB1 for 3 quantity.
+-
+ !record {model: purchase.order, id: purchase_order_po11}:
+ company_id: base.main_company
+ date_order: '2011-01-1'
+ invoice_method: order
+ location_id: stock.stock_location_stock
+ order_line:
+ - date_planned: '2011-01-11'
+ name: MOB1
+ price_unit: 2000.0
+ product_id: 'product_product_mob1'
+ product_qty: 3.0
+ product_uom: product.product_uom_unit
+ state: draft
+ partner_address_id: base.res_partner_address_7
+ partner_id: base.res_partner_4
+ pricelist_id: purchase.list0
+-
+ Initially purchase order is in the draft state.
+-
+ !assert {model: purchase.order, id: purchase_order_po11}:
+ - state == 'draft'
+-
+ I confirm the purchase order for MOB1.
+-
+ !workflow {model: purchase.order, action: purchase_confirm, ref: purchase_order_po11}
+-
+ I check that the order which was initially in the draft state has transmit to confirm state for double validation.
+-
+ !assert {model: purchase.order, id: purchase_order_po11}:
+ - state == 'confirmed'
+-
+ I Approved purchase order by Supplier for MOB1.
+-
+ !workflow {model: purchase.order, action: purchase_approve, ref: purchase_order_po11}
+-
+ I check that the order which was initially in the confirmed state has transmit to approved state.
+-
+ !assert {model: purchase.order, id: purchase_order_po11}:
+ - state == 'approved'
+-
+ Test for purchase double validation in which Total < Limit Amount.
+-
+ I create purchase order for MOB1 for 1 quantity.
+-
+ !record {model: purchase.order, id: purchase_order_po12}:
+ company_id: base.main_company
+ date_order: '2011-01-02'
+ invoice_method: order
+ location_id: stock.stock_location_stock
+ order_line:
+ - date_planned: '2011-01-12'
+ name: MOB1
+ price_unit: 2000.0
+ product_id: 'product_product_mob1'
+ product_qty: 1.0
+ product_uom: product.product_uom_unit
+ state: draft
+ partner_address_id: base.res_partner_address_7
+ partner_id: base.res_partner_4
+ pricelist_id: purchase.list0
+-
+ Initially purchase order is in the draft state.
+-
+ !assert {model: purchase.order, id: purchase_order_po12}:
+ - state == 'draft'
+-
+ I confirm the purchase order for MOB1.
+-
+ !workflow {model: purchase.order, action: purchase_confirm, ref: purchase_order_po12}
+-
+ I check that the order which was initially in the draft state has transmit to approved state.
+-
+ !assert {model: purchase.order, id: purchase_order_po12}:
+ - state == 'approved'
+
+
+
\ No newline at end of file
From 43cba5ee71faa5303b76f28059aa89d74a74f885 Mon Sep 17 00:00:00 2001
From: "ksa (Open ERP)"
Date: Mon, 10 Jan 2011 14:57:13 +0530
Subject: [PATCH 08/50] [FIX]:Custom Reports do not display any values
lp bug: https://launchpad.net/bugs/697554 fixed
bzr revid: ksa@tinyerp.co.in-20110110092713-ni6bjztuw45nmmfh
---
.../base_report_creator.py | 115 +++++++++---------
1 file changed, 60 insertions(+), 55 deletions(-)
diff --git a/addons/base_report_creator/base_report_creator.py b/addons/base_report_creator/base_report_creator.py
index 773458fe3cb..5727646f01c 100644
--- a/addons/base_report_creator/base_report_creator.py
+++ b/addons/base_report_creator/base_report_creator.py
@@ -23,33 +23,16 @@ from osv import fields, osv
from tools.translate import _
from tools import ustr
-class report_creator(osv.osv):
+class report_result(osv.osv):
"""
Report Creator
"""
- _name = "base_report_creator.report"
+ _name = "base_report_creator_report.result"
_description = "Report"
model_set_id = False
#
# Should request only used fields
#
- def export_data(self, cr, uid, ids, fields_to_export, context=None):
- if context is None:
- context = {}
- data_l = self.read(cr, uid, ids, ['sql_query'], context=context)
- final_datas = []
- #start Loop
- for record in data_l:
- datas = []
- for key in fields_to_export:
- value = record.get(key,'')
- if isinstance(value, tuple):
- datas.append(ustr(value[1]))
- else:
- datas.append(ustr(value))
- final_datas += [datas]
- #End Loop
- return {'datas': final_datas}
def fields_get(self, cr, user, fields=None, context=None):
"""
@@ -64,9 +47,9 @@ class report_creator(osv.osv):
data = context and context.get('report_id', False) or False
if (not context) or 'report_id' not in context:
- return super(report_creator, self).fields_get(cr, user, fields, context)
+ return super(report_result, self).fields_get(cr, user, fields, context)
if data:
- report = self.browse(cr, user, data)
+ report = self.pool.get('base_report_creator.report').browse(cr, user, data,context=context)
models = {}
#Start Loop
for model in report.model_ids:
@@ -80,12 +63,8 @@ class report_creator(osv.osv):
i += 1
else:
fields['column_count'] = {'readonly': True, 'type': 'integer', 'string': 'Count', 'size': 64, 'name': 'column_count'}
-
return fields
- #
- # Should Call self.fields_get !
- #
def fields_view_get(self, cr, user, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
"""
Overrides orm field_view_get.
@@ -98,8 +77,8 @@ class report_creator(osv.osv):
data = context and context.get('report_id', False) or False
if (not context) or 'report_id' not in context:
- return super(report_creator, self).fields_view_get(cr, user, view_id, view_type, context, toolbar=toolbar, submenu=submenu)
- report = self.browse(cr, user, data)
+ return super(report_result, self).fields_view_get(cr, user, view_id, view_type, context, toolbar=toolbar, submenu=submenu)
+ report = self.pool.get('base_report_creator.report').browse(cr, user, context.get('report_id'), context=context)
models = {}
for model in report.model_ids:
models[model.model] = self.pool.get(model.model).fields_get(cr, user, context=context)
@@ -192,31 +171,24 @@ class report_creator(osv.osv):
"""
if context is None:
context = {}
- data = context.get('report_id', False)
- res = super(report_creator, self).read(cr, user, ids, fields, context, load)
- if (not context) or 'report_id' not in context:
- return res
- wp = ''
- for data in res:
- if not data.get('sql_query'):
- return res
- if self.model_set_id:
- wp = [self._id_get(cr, user, data, context) + (' in (%s)' % (','.join(map(lambda x: "'" + str(x) + "'", ids))))]
- cr.execute(data['sql_query'])
- res = cr.dictfetchall()
- fields_get = self.fields_get(cr, user, None, context)
- for r in res:
- for k in r:
- r[k] = r[k] or False
- field_dict = fields_get.get(k)
- field_type = field_dict and field_dict.get('type', False) or False
- if field_type and field_type == 'many2one':
- if r[k] == False:
- continue
- related_name = self.pool.get(field_dict.get('relation')).name_get(cr, user, [r[k]], context)[0]
+ report_id = context.get('active_id', False)
+ report = self.pool.get('base_report_creator.report').browse(cr, user, context.get('report_id'), context=context)
+ sql_qry = report.sql_query
+ cr.execute(sql_qry)
+ result = cr.dictfetchall()
+ fields_get = self.fields_get(cr, user, None, context)
+ for r in result:
+ for k in r:
+ r[k] = r[k] or False
+ field_dict = fields_get.get(k)
+ field_type = field_dict and field_dict.get('type', False) or False
+ if field_type and field_type == 'many2one':
+ if r[k] == False:
+ continue
+ if r[k]:
+ related_name = self.pool.get(field_dict.get('relation')).name_get(cr, user, [r[k]], context)
r[k] = related_name
-
- return res
+ return result
def search(self, cr, user, args, offset=0, limit=None, order=None, context=None, count=False):
"""
@@ -231,9 +203,9 @@ class report_creator(osv.osv):
context_id = context.get('report_id', False)
if (not context) or 'report_id' not in context:
- return super(report_creator, self).search(cr, user, args, offset, limit, order, context, count)
+ return {}
if context_id:
- report = self.browse(cr, user, context_id)
+ report = self.pool.get('base_report_creator.report').browse(cr, user, context_id, context=context)
i = 0
fields = {}
for f in report.field_ids:
@@ -254,10 +226,42 @@ class report_creator(osv.osv):
ctx = context or {}
ctx['getid'] = True
sql_query = report.sql_query
- cr.execute(sql_query, newargs2)
+ cr.execute(sql_query) # TODO: FILTER
result = cr.fetchall()
return map(lambda x: x[0], result)
+report_result()
+
+
+class report_creator(osv.osv):
+ """
+ Report Creator
+ """
+ _name = "base_report_creator.report"
+ _description = "Report"
+ model_set_id = False
+ #
+ # Should request only used fields
+ #
+ def export_data(self, cr, uid, ids, fields_to_export, context=None):
+ if context is None:
+ context = {}
+ data_l = self.read(cr, uid, ids, ['sql_query'], context=context)
+ final_datas = []
+ #start Loop
+ for record in data_l:
+ datas = []
+ for key in fields_to_export:
+ value = record.get(key,'')
+ if isinstance(value, tuple):
+ datas.append(ustr(value[1]))
+ else:
+ datas.append(ustr(value))
+ final_datas += [datas]
+ #End Loop
+ return {'datas': final_datas}
+
+
def _path_get(self, cr, uid, models, filter_ids=[]):
"""
@param cr: the current row, from the database cursor,
@@ -465,7 +469,7 @@ class report_creator(osv.osv):
'name': rep.name,
'view_type': 'form',
'view_mode': view_mode,
- 'res_model': 'base_report_creator.report',
+ 'res_model': 'base_report_creator_report.result',
'type': 'ir.actions.act_window',
'context': "{'report_id':%d}" % (rep.id,),
'nodestroy': True,
@@ -596,3 +600,4 @@ class report_creator_filter(osv.osv):
}
report_creator_filter()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+
From 8ce6a08a7bf02dda963041cd68660a97baa4766c Mon Sep 17 00:00:00 2001
From: Yogesh Sakhreliya
Date: Mon, 10 Jan 2011 15:14:13 +0530
Subject: [PATCH 09/50] [FIX] Fields.function : Correct accessors passed to
fields of type integer and integer_big
bzr revid: ysa@tinyerp.com-20110110094413-3640rl39pj1r9v1t
---
bin/osv/fields.py | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/bin/osv/fields.py b/bin/osv/fields.py
index 6f720ea4669..a6ab3a97072 100644
--- a/bin/osv/fields.py
+++ b/bin/osv/fields.py
@@ -762,6 +762,11 @@ class function(_column):
self._symbol_f = boolean._symbol_f
self._symbol_set = boolean._symbol_set
+ if type in ['integer','integer_big']:
+ self._symbol_c = integer._symbol_c
+ self._symbol_f = integer._symbol_f
+ self._symbol_set = integer._symbol_set
+
def digits_change(self, cr):
if self.digits_compute:
t = self.digits_compute(cr)
From 0bffe9bf6996c8e363a9db7dd80a560797d26678 Mon Sep 17 00:00:00 2001
From: "ron@tinyerp.com" <>
Date: Mon, 10 Jan 2011 17:25:25 +0530
Subject: [PATCH 10/50] [FIX]stock:Backorder Cannot be create on Incoming
Shipment
lp bug: https://launchpad.net/bugs/697085 fixed
bzr revid: ron@tinyerp.com-20110110115525-ctcfqhgisj14abvo
---
addons/stock/wizard/stock_partial_picking.py | 172 +++++++++----------
1 file changed, 77 insertions(+), 95 deletions(-)
diff --git a/addons/stock/wizard/stock_partial_picking.py b/addons/stock/wizard/stock_partial_picking.py
index ad75c142a96..45a12208101 100644
--- a/addons/stock/wizard/stock_partial_picking.py
+++ b/addons/stock/wizard/stock_partial_picking.py
@@ -28,54 +28,71 @@ class stock_partial_picking(osv.osv_memory):
_name = "stock.partial.picking"
_description = "Partial Picking"
_columns = {
- 'date': fields.datetime('Date', required=True),
- 'product_moves_out' : fields.one2many('stock.move.memory.out', 'wizard_id', 'Moves'),
- 'product_moves_in' : fields.one2many('stock.move.memory.in', 'wizard_id', 'Moves'),
+ 'date': fields.datetime('Date', required=True),
+ 'product_moves_out' : fields.one2many('stock.move.memory.out', 'wizard_id', 'Moves'),
+ 'product_moves_in' : fields.one2many('stock.move.memory.in', 'wizard_id', 'Moves'),
}
- def __is_in(self,cr, uid, pick_ids):
+ def get_picking_type(self, cr, uid, picking, context=None):
+ picking_type = picking.type
+ for move in picking.move_lines:
+ if picking.type == 'in' and move.product_id.cost_method == 'average':
+ picking_type = 'in'
+ return picking_type
+
+ def default_get(self, cr, uid, fields, context=None):
+ """ To get default values for the object.
+ @param self: The object pointer.
+ @param cr: A database cursor
+ @param uid: ID of the user currently logged in
+ @param fields: List of fields for which we want default values
+ @param context: A standard dictionary
+ @return: A dictionary which of fields with values.
"""
- @return: True if one of the moves has as picking type 'in'
- """
- if not pick_ids:
- return False
+ if context is None:
+ context = {}
+
+ pick_obj = self.pool.get('stock.picking')
+ res = super(stock_partial_picking, self).default_get(cr, uid, fields, context=context)
+ picking_ids = context.get('active_ids', [])
+ if not picking_ids:
+ return res
+
+ result = []
+ for pick in pick_obj.browse(cr, uid, picking_ids, context=context):
+ pick_type = self.get_picking_type(cr, uid, pick, context=context)
+ for m in pick.move_lines:
+ if m.state in ('done', 'cancel'):
+ continue
+ result.append(self.__create_partial_picking_memory(m, pick_type))
+
+ if 'product_moves_in' in fields:
+ res.update({'product_moves_in': result})
+ if 'product_moves_out' in fields:
+ res.update({'product_moves_out': result})
+ if 'date' in fields:
+ res.update({'date': time.strftime('%Y-%m-%d %H:%M:%S')})
+ return res
+
+ def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
+ result = super(stock_partial_picking, self).fields_view_get(cr, uid, view_id, view_type, context, toolbar, submenu)
pick_obj = self.pool.get('stock.picking')
- pick_ids = pick_obj.search(cr, uid, [('id','in',pick_ids)])
-
-
- for pick in pick_obj.browse(cr, uid, pick_ids):
- for move in pick.move_lines:
- if pick.type == 'in' and move.product_id.cost_method == 'average':
- return True
- return False
-
- def __get_picking_type(self, cr, uid, pick_ids):
- if self.__is_in(cr, uid, pick_ids):
- return "product_moves_in"
- else:
- return "product_moves_out"
-
- def view_init(self, cr, uid, fields_list, context=None):
- res = super(stock_partial_picking, self).view_init(cr, uid, fields_list, context=context)
- return res
-
- def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False,submenu=False):
- result = super(stock_partial_picking, self).fields_view_get(cr, uid, view_id, view_type, context, toolbar,submenu)
-
-
picking_ids = context.get('active_ids', False)
- picking_type = self.__get_picking_type(cr, uid, picking_ids)
+
+ for pick in pick_obj.browse(cr, uid, picking_ids, context=context):
+ picking_type = self.get_picking_type(cr, uid, pick, context=context)
_moves_arch_lst = """