first commit

This commit is contained in:
root
2026-03-14 09:42:12 +00:00
commit 0adbd20c2c
10991 changed files with 1646955 additions and 0 deletions

11
modules/stock/__init__.py Normal file
View File

@@ -0,0 +1,11 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
__all__ = ['StockMixin']
def __getattr__(name):
if name == 'StockMixin':
from .move import StockMixin
return StockMixin
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,196 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from trytond.model import (
ModelSingleton, ModelSQL, ModelView, ValueMixin, fields)
from trytond.modules.company.model import (
CompanyMultiValueMixin, CompanyValueMixin)
from trytond.pool import Pool
from trytond.pyson import Eval, Id
sequences = ['shipment_in_sequence', 'shipment_in_return_sequence',
'shipment_out_sequence', 'shipment_out_return_sequence',
'shipment_internal_sequence', 'inventory_sequence']
shipment_internal_transit = fields.Many2One(
'stock.location', "Internal Shipment Transit", required=True,
domain=[
('type', '=', 'storage'),
('parent', '=', None),
],
help="The default location used for stock that is in transit between "
"warehouses.")
def default_func(field_name):
@classmethod
def default(cls, **pattern):
return getattr(
cls.multivalue_model(field_name),
'default_%s' % field_name, lambda: None)()
return default
def default_sequence(name):
@classmethod
def default(cls):
pool = Pool()
ModelData = pool.get('ir.model.data')
try:
return ModelData.get_id('stock', name)
except KeyError:
return None
return default
class Configuration(
ModelSingleton, ModelSQL, ModelView, CompanyMultiValueMixin):
__name__ = 'stock.configuration'
shipment_in_sequence = fields.MultiValue(fields.Many2One(
'ir.sequence', "Supplier Shipment Sequence", required=True,
domain=[
('company', 'in',
[Eval('context', {}).get('company', -1), None]),
('sequence_type', '=',
Id('stock', 'sequence_type_shipment_in')),
],
help="Used to generate the number given to supplier shipments."))
shipment_in_return_sequence = fields.MultiValue(fields.Many2One(
'ir.sequence', "Supplier Return Shipment Sequence", required=True,
domain=[
('company', 'in',
[Eval('context', {}).get('company', -1), None]),
('sequence_type', '=',
Id('stock', 'sequence_type_shipment_in_return')),
],
help="Used to generate the number given to supplier return "
"shipments."))
shipment_out_sequence = fields.MultiValue(fields.Many2One(
'ir.sequence', "Customer Shipment Sequence", required=True,
domain=[
('company', 'in',
[Eval('context', {}).get('company', -1), None]),
('sequence_type', '=',
Id('stock', 'sequence_type_shipment_out')),
],
help="Used to generate the number given to customer "
"shipments."))
shipment_out_return_sequence = fields.MultiValue(fields.Many2One(
'ir.sequence', "Customer Return Shipment Sequence", required=True,
domain=[
('company', 'in',
[Eval('context', {}).get('company', -1), None]),
('sequence_type', '=',
Id('stock', 'sequence_type_shipment_out_return')),
],
help="Used to generate the number given to customer return "
"shipments."))
shipment_internal_sequence = fields.MultiValue(fields.Many2One(
'ir.sequence', "Internal Shipment Sequence", required=True,
domain=[
('company', 'in',
[Eval('context', {}).get('company', -1), None]),
('sequence_type', '=',
Id('stock', 'sequence_type_shipment_internal')),
],
help="Used to generate the number given to internal "
"shipments."))
inventory_sequence = fields.MultiValue(fields.Many2One(
'ir.sequence', "Inventory Sequence", required=True,
domain=[
('company', 'in',
[Eval('context', {}).get('company', -1), None]),
('sequence_type', '=',
Id('stock', 'sequence_type_inventory')),
],
help="Used to generate the number given to inventories."))
shipment_internal_transit = fields.MultiValue(shipment_internal_transit)
@classmethod
def multivalue_model(cls, field):
pool = Pool()
if field in sequences:
return pool.get('stock.configuration.sequence')
if field == 'shipment_internal_transit':
return pool.get('stock.configuration.location')
return super().multivalue_model(field)
default_shipment_in_sequence = default_func('shipment_in_sequence')
default_shipment_in_return_sequence = default_func(
'shipment_in_return_sequence')
default_shipment_out_sequence = default_func('shipment_out_sequence')
default_shipment_out_return_sequence = default_func(
'shipment_out_return_sequence')
default_shipment_internal_sequence = default_func(
'shipment_internal_sequence')
default_inventory_sequence = default_func('inventory_sequence')
default_shipment_internal_transit = default_func(
'shipment_internal_transit')
class ConfigurationSequence(ModelSQL, CompanyValueMixin):
__name__ = 'stock.configuration.sequence'
shipment_in_sequence = fields.Many2One(
'ir.sequence', "Supplier Shipment Sequence", required=True,
domain=[
('company', 'in', [Eval('company', -1), None]),
('sequence_type', '=',
Id('stock', 'sequence_type_shipment_in')),
])
shipment_in_return_sequence = fields.Many2One(
'ir.sequence', "Supplier Return Shipment Sequence", required=True,
domain=[
('company', 'in', [Eval('company', -1), None]),
('sequence_type', '=',
Id('stock', 'sequence_type_shipment_in_return')),
])
shipment_out_sequence = fields.Many2One(
'ir.sequence', "Customer Shipment Sequence", required=True,
domain=[
('company', 'in', [Eval('company', -1), None]),
('sequence_type', '=',
Id('stock', 'sequence_type_shipment_out')),
])
shipment_out_return_sequence = fields.Many2One(
'ir.sequence', "Customer Return Shipment Sequence", required=True,
domain=[
('company', 'in', [Eval('company', -1), None]),
('sequence_type', '=',
Id('stock', 'sequence_type_shipment_out_return')),
])
shipment_internal_sequence = fields.Many2One(
'ir.sequence', "Internal Shipment Sequence", required=True,
domain=[
('company', 'in', [Eval('company', -1), None]),
('sequence_type', '=',
Id('stock', 'sequence_type_shipment_internal')),
])
inventory_sequence = fields.Many2One(
'ir.sequence', "Inventory Sequence", required=True,
domain=[
('company', 'in', [Eval('company', -1), None]),
('sequence_type', '=',
Id('stock', 'sequence_type_inventory')),
])
default_shipment_in_sequence = default_sequence('sequence_shipment_in')
default_shipment_in_return_sequence = default_sequence(
'sequence_shipment_in_return')
default_shipment_out_sequence = default_sequence('sequence_shipment_out')
default_shipment_out_return_sequence = default_sequence(
'sequence_shipment_out_return')
default_shipment_internal_sequence = default_sequence(
'sequence_shipment_internal')
default_inventory_sequence = default_sequence('sequence_inventory')
class ConfigurationLocation(ModelSQL, ValueMixin):
__name__ = 'stock.configuration.location'
shipment_internal_transit = shipment_internal_transit
@classmethod
def default_shipment_internal_transit(cls):
pool = Pool()
ModelData = pool.get('ir.model.data')
try:
return ModelData.get_id('stock', 'location_transit')
except KeyError:
return None

View File

@@ -0,0 +1,46 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<tryton>
<data>
<record model="ir.ui.view" id="stock_configuration_view_form">
<field name="model">stock.configuration</field>
<field name="type">form</field>
<field name="name">configuration_form</field>
</record>
<record model="ir.action.act_window" id="act_stock_configuration_form">
<field name="name">Configuration</field>
<field name="res_model">stock.configuration</field>
</record>
<record model="ir.action.act_window.view"
id="act_stock_configuration_view1">
<field name="sequence" eval="1"/>
<field name="view" ref="stock_configuration_view_form"/>
<field name="act_window" ref="act_stock_configuration_form"/>
</record>
<menuitem
parent="menu_configuration"
action="act_stock_configuration_form"
sequence="10"
id="menu_stock_configuration"
icon="tryton-list"/>
<record model="ir.model.access" id="access_configuration">
<field name="model">stock.configuration</field>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_configuration_admin">
<field name="model">stock.configuration</field>
<field name="group" ref="group_stock_admin"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
</data>
</tryton>

View File

@@ -0,0 +1,612 @@
<?xml version="1.0" encoding="UTF-8"?>
<office:document xmlns:css3t="http://www.w3.org/TR/css3-text/" xmlns:grddl="http://www.w3.org/2003/g/data-view#" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xforms="http://www.w3.org/2002/xforms" xmlns:dom="http://www.w3.org/2001/xml-events" xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" xmlns:ooow="http://openoffice.org/2004/writer" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:drawooo="http://openoffice.org/2010/draw" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" xmlns:tableooo="http://openoffice.org/2009/table" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:rpt="http://openoffice.org/2005/report" xmlns:formx="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:officeooo="http://openoffice.org/2009/office" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text">
<office:meta><meta:generator>LibreOffice/7.6.4.1$Linux_X86_64 LibreOffice_project/60$Build-1</meta:generator><meta:initial-creator>Bertrand Chenal</meta:initial-creator><meta:creation-date>2009-03-26T13:54:48</meta:creation-date><dc:date>2024-04-19T23:15:38.475520081</dc:date><meta:editing-cycles>61</meta:editing-cycles><meta:editing-duration>PT7H7M38S</meta:editing-duration><meta:document-statistic meta:table-count="1" meta:image-count="0" meta:object-count="0" meta:page-count="3" meta:paragraph-count="46" meta:word-count="103" meta:character-count="1103" meta:non-whitespace-character-count="1045"/><meta:user-defined meta:name="Info 1"/><meta:user-defined meta:name="Info 2"/><meta:user-defined meta:name="Info 3"/><meta:user-defined meta:name="Info 4"/></office:meta>
<office:settings>
<config:config-item-set config:name="ooo:view-settings">
<config:config-item config:name="ViewAreaTop" config:type="long">0</config:config-item>
<config:config-item config:name="ViewAreaLeft" config:type="long">0</config:config-item>
<config:config-item config:name="ViewAreaWidth" config:type="long">25269</config:config-item>
<config:config-item config:name="ViewAreaHeight" config:type="long">23973</config:config-item>
<config:config-item config:name="ShowRedlineChanges" config:type="boolean">true</config:config-item>
<config:config-item config:name="InBrowseMode" config:type="boolean">false</config:config-item>
<config:config-item-map-indexed config:name="Views">
<config:config-item-map-entry>
<config:config-item config:name="ViewId" config:type="string">view2</config:config-item>
<config:config-item config:name="ViewLeft" config:type="long">5955</config:config-item>
<config:config-item config:name="ViewTop" config:type="long">10248</config:config-item>
<config:config-item config:name="VisibleLeft" config:type="long">0</config:config-item>
<config:config-item config:name="VisibleTop" config:type="long">0</config:config-item>
<config:config-item config:name="VisibleRight" config:type="long">25268</config:config-item>
<config:config-item config:name="VisibleBottom" config:type="long">23971</config:config-item>
<config:config-item config:name="ZoomType" config:type="short">0</config:config-item>
<config:config-item config:name="ViewLayoutColumns" config:type="short">0</config:config-item>
<config:config-item config:name="ViewLayoutBookMode" config:type="boolean">false</config:config-item>
<config:config-item config:name="ZoomFactor" config:type="short">100</config:config-item>
<config:config-item config:name="IsSelectedFrame" config:type="boolean">false</config:config-item>
<config:config-item config:name="KeepRatio" config:type="boolean">false</config:config-item>
<config:config-item config:name="AnchoredTextOverflowLegacy" config:type="boolean">false</config:config-item>
<config:config-item config:name="LegacySingleLineFontwork" config:type="boolean">false</config:config-item>
<config:config-item config:name="ConnectorUseSnapRect" config:type="boolean">false</config:config-item>
<config:config-item config:name="IgnoreBreakAfterMultilineField" config:type="boolean">false</config:config-item>
</config:config-item-map-entry>
</config:config-item-map-indexed>
</config:config-item-set>
<config:config-item-set config:name="ooo:configuration-settings">
<config:config-item config:name="PrintRightPages" config:type="boolean">true</config:config-item>
<config:config-item config:name="PrintProspectRTL" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintLeftPages" config:type="boolean">true</config:config-item>
<config:config-item config:name="PrintPaperFromSetup" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintControls" config:type="boolean">true</config:config-item>
<config:config-item config:name="PrintProspect" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintBlackFonts" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintAnnotationMode" config:type="short">0</config:config-item>
<config:config-item config:name="PrintEmptyPages" config:type="boolean">true</config:config-item>
<config:config-item config:name="PrintSingleJobs" config:type="boolean">false</config:config-item>
<config:config-item config:name="AutoFirstLineIndentDisregardLineSpace" config:type="boolean">false</config:config-item>
<config:config-item config:name="HeaderSpacingBelowLastPara" config:type="boolean">false</config:config-item>
<config:config-item config:name="ProtectBookmarks" config:type="boolean">false</config:config-item>
<config:config-item config:name="ContinuousEndnotes" config:type="boolean">false</config:config-item>
<config:config-item config:name="DisableOffPagePositioning" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintTables" config:type="boolean">true</config:config-item>
<config:config-item config:name="SubtractFlysAnchoredAtFlys" config:type="boolean">true</config:config-item>
<config:config-item config:name="ApplyParagraphMarkFormatToNumbering" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintFaxName" config:type="string"/>
<config:config-item config:name="SurroundTextWrapSmall" config:type="boolean">false</config:config-item>
<config:config-item config:name="TreatSingleColumnBreakAsPageBreak" config:type="boolean">false</config:config-item>
<config:config-item config:name="PropLineSpacingShrinksFirstLine" config:type="boolean">false</config:config-item>
<config:config-item config:name="TabOverSpacing" config:type="boolean">false</config:config-item>
<config:config-item config:name="TabOverMargin" config:type="boolean">false</config:config-item>
<config:config-item config:name="EmbedComplexScriptFonts" config:type="boolean">true</config:config-item>
<config:config-item config:name="EmbedLatinScriptFonts" config:type="boolean">true</config:config-item>
<config:config-item config:name="EmbedOnlyUsedFonts" config:type="boolean">false</config:config-item>
<config:config-item config:name="EmbedFonts" config:type="boolean">false</config:config-item>
<config:config-item config:name="ClippedPictures" config:type="boolean">false</config:config-item>
<config:config-item config:name="FrameAutowidthWithMorePara" config:type="boolean">false</config:config-item>
<config:config-item config:name="FloattableNomargins" config:type="boolean">false</config:config-item>
<config:config-item config:name="UnbreakableNumberings" config:type="boolean">false</config:config-item>
<config:config-item config:name="AllowPrintJobCancel" config:type="boolean">true</config:config-item>
<config:config-item config:name="UseFormerObjectPositioning" config:type="boolean">false</config:config-item>
<config:config-item config:name="UseOldNumbering" config:type="boolean">false</config:config-item>
<config:config-item config:name="RsidRoot" config:type="int">68753</config:config-item>
<config:config-item config:name="PrinterPaperFromSetup" config:type="boolean">false</config:config-item>
<config:config-item config:name="CurrentDatabaseDataSource" config:type="string"/>
<config:config-item config:name="UpdateFromTemplate" config:type="boolean">true</config:config-item>
<config:config-item config:name="AddFrameOffsets" config:type="boolean">false</config:config-item>
<config:config-item config:name="LoadReadonly" config:type="boolean">false</config:config-item>
<config:config-item config:name="Rsid" config:type="int">737627</config:config-item>
<config:config-item config:name="FootnoteInColumnToPageEnd" config:type="boolean">true</config:config-item>
<config:config-item config:name="ProtectFields" config:type="boolean">false</config:config-item>
<config:config-item config:name="SaveGlobalDocumentLinks" config:type="boolean">false</config:config-item>
<config:config-item config:name="ClipAsCharacterAnchoredWriterFlyFrames" config:type="boolean">false</config:config-item>
<config:config-item config:name="LinkUpdateMode" config:type="short">1</config:config-item>
<config:config-item config:name="AddExternalLeading" config:type="boolean">true</config:config-item>
<config:config-item config:name="PrintGraphics" config:type="boolean">true</config:config-item>
<config:config-item config:name="EmbedSystemFonts" config:type="boolean">false</config:config-item>
<config:config-item config:name="IsLabelDocument" config:type="boolean">false</config:config-item>
<config:config-item config:name="AddParaLineSpacingToTableCells" config:type="boolean">false</config:config-item>
<config:config-item config:name="UseFormerTextWrapping" config:type="boolean">false</config:config-item>
<config:config-item config:name="HyphenateURLs" config:type="boolean">true</config:config-item>
<config:config-item config:name="AddParaTableSpacingAtStart" config:type="boolean">true</config:config-item>
<config:config-item config:name="TabsRelativeToIndent" config:type="boolean">true</config:config-item>
<config:config-item config:name="FieldAutoUpdate" config:type="boolean">true</config:config-item>
<config:config-item config:name="SaveVersionOnClose" config:type="boolean">false</config:config-item>
<config:config-item config:name="ChartAutoUpdate" config:type="boolean">true</config:config-item>
<config:config-item config:name="ImagePreferredDPI" config:type="int">0</config:config-item>
<config:config-item config:name="PrinterSetup" config:type="base64Binary"/>
<config:config-item config:name="SmallCapsPercentage66" config:type="boolean">true</config:config-item>
<config:config-item config:name="AlignTabStopPosition" config:type="boolean">true</config:config-item>
<config:config-item config:name="DropCapPunctuation" config:type="boolean">false</config:config-item>
<config:config-item config:name="MathBaselineAlignment" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrinterName" config:type="string"/>
<config:config-item config:name="CharacterCompressionType" config:type="short">0</config:config-item>
<config:config-item config:name="AddParaTableSpacing" config:type="boolean">true</config:config-item>
<config:config-item config:name="DoNotJustifyLinesWithManualBreak" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintHiddenText" config:type="boolean">false</config:config-item>
<config:config-item config:name="IsKernAsianPunctuation" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrinterIndependentLayout" config:type="string">high-resolution</config:config-item>
<config:config-item config:name="TabOverflow" config:type="boolean">false</config:config-item>
<config:config-item config:name="AddParaSpacingToTableCells" config:type="boolean">true</config:config-item>
<config:config-item config:name="AddVerticalFrameOffsets" config:type="boolean">false</config:config-item>
<config:config-item config:name="TabAtLeftIndentForParagraphsInList" config:type="boolean">false</config:config-item>
<config:config-item config:name="ApplyUserData" config:type="boolean">true</config:config-item>
<config:config-item config:name="MsWordCompMinLineHeightByFly" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintTextPlaceholder" config:type="boolean">false</config:config-item>
<config:config-item config:name="IgnoreFirstLineIndentInNumbering" config:type="boolean">false</config:config-item>
<config:config-item config:name="UseFormerLineSpacing" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintPageBackground" config:type="boolean">true</config:config-item>
<config:config-item config:name="RedlineProtectionKey" config:type="base64Binary"/>
<config:config-item config:name="EmbedAsianScriptFonts" config:type="boolean">true</config:config-item>
<config:config-item config:name="BackgroundParaOverDrawings" config:type="boolean">false</config:config-item>
<config:config-item config:name="SaveThumbnail" config:type="boolean">true</config:config-item>
<config:config-item config:name="ConsiderTextWrapOnObjPos" config:type="boolean">false</config:config-item>
<config:config-item config:name="EmbeddedDatabaseName" config:type="string"/>
<config:config-item config:name="ProtectForm" config:type="boolean">false</config:config-item>
<config:config-item config:name="DoNotResetParaAttrsForNumFont" config:type="boolean">false</config:config-item>
<config:config-item config:name="MsWordCompTrailingBlanks" config:type="boolean">false</config:config-item>
<config:config-item config:name="EmptyDbFieldHidesPara" config:type="boolean">false</config:config-item>
<config:config-item config:name="TableRowKeep" config:type="boolean">false</config:config-item>
<config:config-item config:name="NoNumberingShowFollowBy" config:type="boolean">false</config:config-item>
<config:config-item config:name="InvertBorderSpacing" config:type="boolean">false</config:config-item>
<config:config-item config:name="IgnoreTabsAndBlanksForLineCalculation" config:type="boolean">false</config:config-item>
<config:config-item config:name="DoNotCaptureDrawObjsOnPage" config:type="boolean">false</config:config-item>
<config:config-item config:name="GutterAtTop" config:type="boolean">false</config:config-item>
<config:config-item config:name="StylesNoDefault" config:type="boolean">false</config:config-item>
<config:config-item config:name="UnxForceZeroExtLeading" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintReversed" config:type="boolean">false</config:config-item>
<config:config-item config:name="UseOldPrinterMetrics" config:type="boolean">false</config:config-item>
<config:config-item config:name="CurrentDatabaseCommandType" config:type="int">0</config:config-item>
<config:config-item config:name="PrintDrawings" config:type="boolean">true</config:config-item>
<config:config-item config:name="OutlineLevelYieldsNumbering" config:type="boolean">false</config:config-item>
<config:config-item config:name="CurrentDatabaseCommand" config:type="string"/>
<config:config-item config:name="CollapseEmptyCellPara" config:type="boolean">true</config:config-item>
</config:config-item-set>
</office:settings>
<office:scripts>
<office:script script:language="ooo:Basic">
<ooo:libraries xmlns:ooo="http://openoffice.org/2004/office" xmlns:xlink="http://www.w3.org/1999/xlink"/>
</office:script>
</office:scripts>
<office:font-face-decls>
<style:font-face style:name="Andale Sans UI" svg:font-family="&apos;Andale Sans UI&apos;" style:font-family-generic="system" style:font-pitch="variable"/>
<style:font-face style:name="DejaVu Sans" svg:font-family="&apos;DejaVu Sans&apos;" style:font-family-generic="system" style:font-pitch="variable"/>
<style:font-face style:name="Liberation Sans" svg:font-family="&apos;Liberation Sans&apos;" style:font-adornments="Regular" style:font-family-generic="swiss" style:font-pitch="variable"/>
<style:font-face style:name="Liberation Serif" svg:font-family="&apos;Liberation Serif&apos;" style:font-family-generic="roman" style:font-pitch="variable"/>
<style:font-face style:name="Liberation Serif1" svg:font-family="&apos;Liberation Serif&apos;" style:font-adornments="Bold" style:font-family-generic="roman" style:font-pitch="variable"/>
<style:font-face style:name="Liberation Serif2" svg:font-family="&apos;Liberation Serif&apos;" style:font-adornments="Regular" style:font-family-generic="roman" style:font-pitch="variable"/>
<style:font-face style:name="StarSymbol" svg:font-family="StarSymbol"/>
<style:font-face style:name="Thorndale AMT" svg:font-family="&apos;Thorndale AMT&apos;" style:font-family-generic="roman" style:font-pitch="variable"/>
</office:font-face-decls>
<office:styles>
<style:default-style style:family="graphic">
<style:graphic-properties svg:stroke-color="#000000" draw:fill-color="#99ccff" fo:wrap-option="no-wrap" draw:shadow-offset-x="0.3cm" draw:shadow-offset-y="0.3cm" draw:start-line-spacing-horizontal="0.283cm" draw:start-line-spacing-vertical="0.283cm" draw:end-line-spacing-horizontal="0.283cm" draw:end-line-spacing-vertical="0.283cm" style:writing-mode="lr-tb" style:flow-with-text="false"/>
<style:paragraph-properties style:text-autospace="ideograph-alpha" style:line-break="strict" loext:tab-stop-distance="0cm" style:writing-mode="lr-tb" style:font-independent-line-spacing="false">
<style:tab-stops/>
</style:paragraph-properties>
<style:text-properties style:use-window-font-color="true" loext:opacity="0%" style:font-name="Thorndale AMT" fo:font-size="12pt" fo:language="en" fo:country="US" style:letter-kerning="true" style:font-name-asian="Andale Sans UI" style:font-size-asian="10.5pt" style:language-asian="zxx" style:country-asian="none" style:font-name-complex="Andale Sans UI" style:font-size-complex="12pt" style:language-complex="zxx" style:country-complex="none"/>
</style:default-style>
<style:default-style style:family="paragraph">
<style:paragraph-properties fo:hyphenation-ladder-count="no-limit" style:text-autospace="ideograph-alpha" style:punctuation-wrap="hanging" style:line-break="strict" style:tab-stop-distance="1.251cm" style:writing-mode="lr-tb"/>
<style:text-properties style:use-window-font-color="true" loext:opacity="0%" style:font-name="Thorndale AMT" fo:font-size="12pt" fo:language="en" fo:country="US" style:letter-kerning="true" style:font-name-asian="Andale Sans UI" style:font-size-asian="10.5pt" style:language-asian="zxx" style:country-asian="none" style:font-name-complex="Andale Sans UI" style:font-size-complex="12pt" style:language-complex="zxx" style:country-complex="none" fo:hyphenate="false" fo:hyphenation-remain-char-count="2" fo:hyphenation-push-char-count="2" loext:hyphenation-no-caps="false" loext:hyphenation-no-last-word="false" loext:hyphenation-word-char-count="5" loext:hyphenation-zone="no-limit"/>
</style:default-style>
<style:default-style style:family="table">
<style:table-properties table:border-model="collapsing"/>
</style:default-style>
<style:default-style style:family="table-row">
<style:table-row-properties fo:keep-together="auto"/>
</style:default-style>
<style:style style:name="Standard" style:family="paragraph" style:class="text">
<style:text-properties style:font-name="Liberation Sans" fo:font-family="&apos;Liberation Sans&apos;" style:font-style-name="Regular" style:font-family-generic="swiss" style:font-pitch="variable" style:font-size-asian="10.5pt"/>
</style:style>
<style:style style:name="Heading" style:family="paragraph" style:parent-style-name="Standard" style:next-style-name="Text_20_body" style:class="text">
<style:paragraph-properties fo:margin-top="0.423cm" fo:margin-bottom="0.212cm" style:contextual-spacing="false" fo:keep-with-next="always"/>
<style:text-properties style:font-name="Liberation Serif2" fo:font-family="&apos;Liberation Serif&apos;" style:font-style-name="Regular" style:font-family-generic="roman" style:font-pitch="variable" fo:font-size="16pt" style:font-name-asian="DejaVu Sans" style:font-family-asian="&apos;DejaVu Sans&apos;" style:font-family-generic-asian="system" style:font-pitch-asian="variable" style:font-size-asian="14pt" style:font-name-complex="DejaVu Sans" style:font-family-complex="&apos;DejaVu Sans&apos;" style:font-family-generic-complex="system" style:font-pitch-complex="variable" style:font-size-complex="14pt"/>
</style:style>
<style:style style:name="Text_20_body" style:display-name="Text body" style:family="paragraph" style:parent-style-name="Standard" style:class="text">
<style:paragraph-properties fo:margin-top="0cm" fo:margin-bottom="0.212cm" style:contextual-spacing="false"/>
<style:text-properties style:font-name="Liberation Sans" fo:font-family="&apos;Liberation Sans&apos;" style:font-style-name="Regular" style:font-family-generic="swiss" style:font-pitch="variable" style:font-size-asian="10.5pt"/>
</style:style>
<style:style style:name="List" style:family="paragraph" style:parent-style-name="Text_20_body" style:class="list">
<style:text-properties style:font-size-asian="12pt"/>
</style:style>
<style:style style:name="Caption" style:family="paragraph" style:parent-style-name="Standard" style:class="extra">
<style:paragraph-properties fo:margin-top="0.212cm" fo:margin-bottom="0.212cm" style:contextual-spacing="false" text:number-lines="false" text:line-number="0"/>
<style:text-properties fo:font-size="12pt" fo:font-style="italic" style:font-size-asian="12pt" style:font-style-asian="italic" style:font-size-complex="12pt" style:font-style-complex="italic"/>
</style:style>
<style:style style:name="Index" style:family="paragraph" style:parent-style-name="Standard" style:class="index">
<style:paragraph-properties text:number-lines="false" text:line-number="0"/>
<style:text-properties style:font-size-asian="12pt"/>
</style:style>
<style:style style:name="Heading_20_1" style:display-name="Heading 1" style:family="paragraph" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:class="text">
<style:text-properties fo:font-size="16pt" fo:font-weight="bold" style:font-size-asian="115%" style:font-weight-asian="bold" style:font-size-complex="115%" style:font-weight-complex="bold"/>
</style:style>
<style:style style:name="Table_20_Contents" style:display-name="Table Contents" style:family="paragraph" style:parent-style-name="Standard" style:class="extra">
<style:paragraph-properties text:number-lines="false" text:line-number="0"/>
<style:text-properties style:font-size-asian="10.5pt"/>
</style:style>
<style:style style:name="Table_20_Heading" style:display-name="Table Heading" style:family="paragraph" style:parent-style-name="Table_20_Contents" style:class="extra" style:master-page-name="">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false" style:page-number="auto" text:number-lines="false" text:line-number="0"/>
<style:text-properties style:font-name="Liberation Serif1" fo:font-family="&apos;Liberation Serif&apos;" style:font-style-name="Bold" style:font-family-generic="roman" style:font-pitch="variable" fo:font-weight="bold" style:font-size-asian="10.5pt" style:font-weight-asian="bold" style:font-weight-complex="bold"/>
</style:style>
<style:style style:name="Header_20_and_20_Footer" style:display-name="Header and Footer" style:family="paragraph" style:parent-style-name="Standard" style:class="extra">
<style:paragraph-properties text:number-lines="false" text:line-number="0">
<style:tab-stops>
<style:tab-stop style:position="8.795cm" style:type="center"/>
<style:tab-stop style:position="17.59cm" style:type="right"/>
</style:tab-stops>
</style:paragraph-properties>
</style:style>
<style:style style:name="Header" style:family="paragraph" style:parent-style-name="Standard" style:class="extra">
<style:paragraph-properties text:number-lines="false" text:line-number="0">
<style:tab-stops>
<style:tab-stop style:position="8.795cm" style:type="center"/>
<style:tab-stop style:position="17.59cm" style:type="right"/>
</style:tab-stops>
</style:paragraph-properties>
<style:text-properties fo:font-size="9pt" style:font-size-asian="10.5pt"/>
</style:style>
<style:style style:name="Heading_20_2" style:display-name="Heading 2" style:family="paragraph" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:class="text">
<style:text-properties fo:font-size="14pt" fo:font-style="italic" fo:font-weight="bold" style:font-size-asian="14pt" style:font-style-asian="italic" style:font-weight-asian="bold" style:font-size-complex="14pt" style:font-style-complex="italic" style:font-weight-complex="bold"/>
</style:style>
<style:style style:name="Footer" style:family="paragraph" style:parent-style-name="Standard" style:class="extra">
<style:paragraph-properties text:number-lines="false" text:line-number="0">
<style:tab-stops>
<style:tab-stop style:position="8.795cm" style:type="center"/>
<style:tab-stop style:position="17.59cm" style:type="right"/>
</style:tab-stops>
</style:paragraph-properties>
<style:text-properties fo:font-size="9pt" style:font-size-asian="10.5pt"/>
</style:style>
<style:style style:name="Heading_20_3" style:display-name="Heading 3" style:family="paragraph" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:class="text">
<style:text-properties fo:font-size="14pt" fo:font-weight="bold" style:font-size-asian="14pt" style:font-weight-asian="bold" style:font-size-complex="14pt" style:font-weight-complex="bold"/>
</style:style>
<style:style style:name="Text_20_body_20_indent" style:display-name="Text body indent" style:family="paragraph" style:parent-style-name="Text_20_body" style:class="text">
<style:paragraph-properties fo:margin-left="0.499cm" fo:margin-right="0cm" fo:margin-top="0cm" fo:margin-bottom="0cm" style:contextual-spacing="false" fo:text-indent="0cm" style:auto-text-indent="false"/>
</style:style>
<style:style style:name="Text" style:family="paragraph" style:parent-style-name="Caption" style:class="extra"/>
<style:style style:name="Quotations" style:family="paragraph" style:parent-style-name="Standard" style:class="html">
<style:paragraph-properties fo:margin-left="1cm" fo:margin-right="1cm" fo:margin-top="0cm" fo:margin-bottom="0.499cm" style:contextual-spacing="false" fo:text-indent="0cm" style:auto-text-indent="false"/>
</style:style>
<style:style style:name="Title" style:family="paragraph" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:class="chapter">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
<style:text-properties fo:font-size="28pt" fo:font-weight="bold" style:font-size-asian="28pt" style:font-weight-asian="bold" style:font-size-complex="28pt" style:font-weight-complex="bold"/>
</style:style>
<style:style style:name="Subtitle" style:family="paragraph" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:class="chapter">
<style:paragraph-properties fo:margin-top="0.106cm" fo:margin-bottom="0.212cm" style:contextual-spacing="false" fo:text-align="center" style:justify-single-word="false"/>
<style:text-properties fo:font-size="18pt" style:font-size-asian="18pt" style:font-size-complex="18pt"/>
</style:style>
<style:style style:name="Frame_20_contents" style:display-name="Frame contents" style:family="paragraph" style:parent-style-name="Standard" style:class="extra"/>
<style:style style:name="Placeholder" style:family="text">
<style:text-properties fo:font-variant="small-caps" fo:color="#008080" loext:opacity="100%" style:text-underline-style="dotted" style:text-underline-width="auto" style:text-underline-color="font-color"/>
</style:style>
<style:style style:name="Bullet_20_Symbols" style:display-name="Bullet Symbols" style:family="text">
<style:text-properties style:font-name="StarSymbol" fo:font-family="StarSymbol" fo:font-size="9pt" style:font-name-asian="StarSymbol" style:font-family-asian="StarSymbol" style:font-size-asian="9pt" style:font-name-complex="StarSymbol" style:font-family-complex="StarSymbol" style:font-size-complex="9pt"/>
</style:style>
<style:style style:name="Frame" style:family="graphic">
<style:graphic-properties text:anchor-type="paragraph" svg:x="0cm" svg:y="0cm" fo:margin-left="0.201cm" fo:margin-right="0.201cm" fo:margin-top="0.201cm" fo:margin-bottom="0.201cm" style:wrap="parallel" style:number-wrapped-paragraphs="no-limit" style:wrap-contour="false" style:vertical-pos="top" style:vertical-rel="paragraph-content" style:horizontal-pos="center" style:horizontal-rel="paragraph-content" fo:background-color="transparent" draw:fill="none" draw:fill-color="#99ccff" fo:padding="0.15cm" fo:border="0.06pt solid #000000"/>
</style:style>
<text:outline-style style:name="Outline">
<text:outline-level-style text:level="1" loext:num-list-format="%1%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
<text:outline-level-style text:level="2" loext:num-list-format="%2%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
<text:outline-level-style text:level="3" loext:num-list-format="%3%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
<text:outline-level-style text:level="4" loext:num-list-format="%4%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
<text:outline-level-style text:level="5" loext:num-list-format="%5%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
<text:outline-level-style text:level="6" loext:num-list-format="%6%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
<text:outline-level-style text:level="7" loext:num-list-format="%7%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
<text:outline-level-style text:level="8" loext:num-list-format="%8%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
<text:outline-level-style text:level="9" loext:num-list-format="%9%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
<text:outline-level-style text:level="10" loext:num-list-format="%10%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
</text:outline-style>
<text:notes-configuration text:note-class="footnote" style:num-format="1" text:start-value="0" text:footnotes-position="page" text:start-numbering-at="document"/>
<text:notes-configuration text:note-class="endnote" style:num-format="i" text:start-value="0"/>
<text:linenumbering-configuration text:number-lines="false" text:offset="0.499cm" style:num-format="1" text:number-position="left" text:increment="5"/>
<loext:theme loext:name="Office Theme">
<loext:theme-colors loext:name="LibreOffice">
<loext:color loext:name="dark1" loext:color="#000000"/>
<loext:color loext:name="light1" loext:color="#ffffff"/>
<loext:color loext:name="dark2" loext:color="#000000"/>
<loext:color loext:name="light2" loext:color="#ffffff"/>
<loext:color loext:name="accent1" loext:color="#18a303"/>
<loext:color loext:name="accent2" loext:color="#0369a3"/>
<loext:color loext:name="accent3" loext:color="#a33e03"/>
<loext:color loext:name="accent4" loext:color="#8e03a3"/>
<loext:color loext:name="accent5" loext:color="#c99c00"/>
<loext:color loext:name="accent6" loext:color="#c9211e"/>
<loext:color loext:name="hyperlink" loext:color="#0000ee"/>
<loext:color loext:name="followed-hyperlink" loext:color="#551a8b"/>
</loext:theme-colors>
</loext:theme>
</office:styles>
<office:automatic-styles>
<style:style style:name="Table1" style:family="table">
<style:table-properties style:width="16.999cm" fo:margin-left="0cm" fo:margin-right="0cm" fo:margin-top="0.381cm" fo:margin-bottom="0cm" table:align="margins"/>
</style:style>
<style:style style:name="Table1.A" style:family="table-column">
<style:table-column-properties style:column-width="4.683cm" style:rel-column-width="18053*"/>
</style:style>
<style:style style:name="Table1.C" style:family="table-column">
<style:table-column-properties style:column-width="4.685cm" style:rel-column-width="18060*"/>
</style:style>
<style:style style:name="Table1.D" style:family="table-column">
<style:table-column-properties style:column-width="2.949cm" style:rel-column-width="11369*"/>
</style:style>
<style:style style:name="Table1.A1" style:family="table-cell">
<style:table-cell-properties fo:background-color="#cccccc" fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="0.05pt solid #000000" fo:border-bottom="0.05pt solid #000000">
<style:background-image/>
</style:table-cell-properties>
</style:style>
<style:style style:name="Table1.D1" style:family="table-cell">
<style:table-cell-properties fo:background-color="#cccccc" fo:padding="0.097cm" fo:border="0.05pt solid #000000">
<style:background-image/>
</style:table-cell-properties>
</style:style>
<style:style style:name="Table1.A2" style:family="table-cell">
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
</style:style>
<style:style style:name="Table1.C2" style:family="table-cell">
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
</style:style>
<style:style style:name="Table1.D2" style:family="table-cell">
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="0.05pt solid #000000" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
</style:style>
<style:style style:name="Table1.A3" style:family="table-cell">
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
</style:style>
<style:style style:name="Table1.B3" style:family="table-cell">
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
</style:style>
<style:style style:name="Table1.C3" style:family="table-cell">
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
</style:style>
<style:style style:name="Table1.D3" style:family="table-cell">
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="0.05pt solid #000000" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
</style:style>
<style:style style:name="Table1.A4" style:family="table-cell">
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
</style:style>
<style:style style:name="Table1.C4" style:family="table-cell">
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
</style:style>
<style:style style:name="Table1.D4" style:family="table-cell">
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="0.05pt solid #000000" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
</style:style>
<style:style style:name="P1" style:family="paragraph" style:parent-style-name="Header">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
<style:text-properties officeooo:paragraph-rsid="00053457"/>
</style:style>
<style:style style:name="P2" style:family="paragraph" style:parent-style-name="Footer">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
<style:text-properties officeooo:paragraph-rsid="00053457"/>
</style:style>
<style:style style:name="P3" style:family="paragraph" style:parent-style-name="Header">
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false"/>
<style:text-properties fo:font-size="12pt" officeooo:paragraph-rsid="000b415b" style:font-size-asian="12pt" style:font-size-complex="12pt"/>
</style:style>
<style:style style:name="P4" style:family="paragraph" style:parent-style-name="Frame_20_contents">
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false"/>
</style:style>
<style:style style:name="P5" style:family="paragraph" style:parent-style-name="Footer">
<style:paragraph-properties fo:text-align="end" style:justify-single-word="false"/>
</style:style>
<style:style style:name="P6" style:family="paragraph" style:parent-style-name="Header">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
<style:text-properties officeooo:paragraph-rsid="00053457"/>
</style:style>
<style:style style:name="P7" style:family="paragraph" style:parent-style-name="Footer">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
<style:text-properties officeooo:paragraph-rsid="00053457"/>
</style:style>
<style:style style:name="P8" style:family="paragraph" style:parent-style-name="Footer">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
<style:text-properties officeooo:paragraph-rsid="0007d4d7"/>
</style:style>
<style:style style:name="P9" style:family="paragraph" style:parent-style-name="Header">
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false"/>
<style:text-properties fo:font-size="12pt" officeooo:paragraph-rsid="0007d4d7" style:font-size-asian="12pt" style:font-size-complex="12pt"/>
</style:style>
<style:style style:name="P10" style:family="paragraph" style:parent-style-name="Header">
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false"/>
<style:text-properties fo:font-size="12pt" officeooo:paragraph-rsid="00053457" style:font-size-asian="12pt" style:font-size-complex="12pt"/>
</style:style>
<style:style style:name="P11" style:family="paragraph" style:parent-style-name="Frame_20_contents">
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false"/>
</style:style>
<style:style style:name="P12" style:family="paragraph" style:parent-style-name="Footer">
<style:paragraph-properties fo:text-align="end" style:justify-single-word="false"/>
</style:style>
<style:style style:name="P13" style:family="paragraph" style:parent-style-name="Table_20_Contents">
<style:paragraph-properties fo:text-align="end" style:justify-single-word="false"/>
</style:style>
<style:style style:name="P14" style:family="paragraph" style:parent-style-name="Table_20_Heading">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
<style:text-properties style:font-name="Liberation Serif"/>
</style:style>
<style:style style:name="P15" style:family="paragraph" style:parent-style-name="Table_20_Contents">
<style:text-properties style:font-name="Liberation Serif"/>
</style:style>
<style:style style:name="P16" style:family="paragraph" style:parent-style-name="Standard">
<style:paragraph-properties fo:break-before="page"/>
</style:style>
<style:style style:name="P17" style:family="paragraph" style:parent-style-name="Heading_20_1" style:master-page-name="">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false" style:page-number="auto" fo:break-before="page"/>
<style:text-properties style:text-underline-style="solid" style:text-underline-width="auto" style:text-underline-color="font-color" fo:font-weight="bold" style:font-weight-asian="bold" style:font-weight-complex="bold"/>
</style:style>
<style:style style:name="P18" style:family="paragraph" style:parent-style-name="Heading_20_1">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
<style:text-properties style:text-underline-style="solid" style:text-underline-width="auto" style:text-underline-color="font-color" fo:font-weight="bold" style:font-weight-asian="bold" style:font-weight-complex="bold"/>
</style:style>
<style:style style:name="P19" style:family="paragraph" style:parent-style-name="Heading_20_1">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
<style:text-properties style:text-underline-style="solid" style:text-underline-width="auto" style:text-underline-color="font-color" fo:font-weight="bold" officeooo:paragraph-rsid="000796e5" style:font-weight-asian="bold" style:font-weight-complex="bold"/>
</style:style>
<style:style style:name="P20" style:family="paragraph" style:parent-style-name="Standard">
<style:text-properties officeooo:paragraph-rsid="0002e6f0"/>
</style:style>
<style:style style:name="P21" style:family="paragraph" style:parent-style-name="Standard">
<style:text-properties officeooo:paragraph-rsid="000796e5"/>
</style:style>
<style:style style:name="P22" style:family="paragraph" style:parent-style-name="Frame_20_contents">
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false"/>
</style:style>
<style:style style:name="P23" style:family="paragraph" style:parent-style-name="Header">
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false"/>
<style:text-properties fo:font-size="12pt" officeooo:paragraph-rsid="000b415b" style:font-size-asian="12pt" style:font-size-complex="12pt"/>
</style:style>
<style:style style:name="T1" style:family="text">
<style:text-properties fo:font-weight="bold" style:font-weight-asian="bold" style:font-weight-complex="bold"/>
</style:style>
<style:style style:name="T2" style:family="text">
<style:text-properties fo:font-weight="bold" officeooo:rsid="0002e6f0" style:font-weight-asian="bold" style:font-weight-complex="bold"/>
</style:style>
<style:style style:name="T3" style:family="text">
<style:text-properties fo:font-weight="bold" officeooo:rsid="00058f93" style:font-weight-asian="bold" style:font-weight-complex="bold"/>
</style:style>
<style:style style:name="T4" style:family="text">
<style:text-properties fo:font-weight="bold" officeooo:rsid="0006a771" style:font-weight-asian="bold" style:font-weight-complex="bold"/>
</style:style>
<style:style style:name="T5" style:family="text">
<style:text-properties fo:font-weight="normal" style:font-weight-asian="normal" style:font-weight-complex="normal"/>
</style:style>
<style:style style:name="T6" style:family="text">
<style:text-properties officeooo:rsid="000796e5"/>
</style:style>
<style:style style:name="fr1" style:family="graphic" style:parent-style-name="Frame">
<style:graphic-properties fo:margin-left="0cm" fo:margin-right="0cm" fo:margin-top="0cm" fo:margin-bottom="0cm" style:wrap="parallel" style:number-wrapped-paragraphs="no-limit" style:vertical-pos="middle" style:vertical-rel="baseline" style:horizontal-pos="left" style:horizontal-rel="paragraph" draw:opacity="100%" fo:padding="0cm" fo:border="none" draw:wrap-influence-on-position="once-concurrent" loext:allow-overlap="true">
<style:columns fo:column-count="1" fo:column-gap="0cm"/>
</style:graphic-properties>
</style:style>
<style:page-layout style:name="pm1">
<style:page-layout-properties fo:page-width="20.999cm" fo:page-height="29.699cm" style:num-format="1" style:print-orientation="portrait" fo:margin-top="2cm" fo:margin-bottom="2cm" fo:margin-left="2cm" fo:margin-right="2cm" style:writing-mode="lr-tb" style:layout-grid-color="#c0c0c0" style:layout-grid-lines="20" style:layout-grid-base-height="0.706cm" style:layout-grid-ruby-height="0.353cm" style:layout-grid-mode="none" style:layout-grid-ruby-below="false" style:layout-grid-print="false" style:layout-grid-display="false" style:footnote-max-height="0cm" loext:margin-gutter="0cm">
<style:footnote-sep style:width="0.018cm" style:distance-before-sep="0.101cm" style:distance-after-sep="0.101cm" style:line-style="none" style:adjustment="left" style:rel-width="25%" style:color="#000000"/>
</style:page-layout-properties>
<style:header-style>
<style:header-footer-properties fo:min-height="0cm" fo:margin-left="0cm" fo:margin-right="0cm" fo:margin-bottom="0.499cm"/>
</style:header-style>
<style:footer-style>
<style:header-footer-properties fo:min-height="0cm" fo:margin-left="0cm" fo:margin-right="0cm" fo:margin-top="0.499cm"/>
</style:footer-style>
</style:page-layout>
<style:style style:name="dp1" style:family="drawing-page">
<style:drawing-page-properties draw:background-size="full"/>
</style:style>
</office:automatic-styles>
<office:master-styles>
<style:master-page style:name="Standard" style:page-layout-name="pm1" draw:style-name="dp1">
<style:header>
<text:p text:style-name="P1"><text:placeholder text:placeholder-type="text">&lt;if test=&quot;company and company.header&quot;&gt;</text:placeholder></text:p>
<text:p text:style-name="P1"><text:placeholder text:placeholder-type="text">&lt;for each=&quot;line in company.header_used.split(&apos;\n&apos;)&quot;&gt;</text:placeholder></text:p>
<text:p text:style-name="P1"><text:placeholder text:placeholder-type="text">&lt;line&gt;</text:placeholder></text:p>
<text:p text:style-name="P1"><text:placeholder text:placeholder-type="text">&lt;/for&gt;</text:placeholder></text:p>
<text:p text:style-name="P2"><text:placeholder text:placeholder-type="text">&lt;/if&gt;</text:placeholder></text:p>
<text:p text:style-name="P3"><text:placeholder text:placeholder-type="text">&lt;choose&gt;</text:placeholder></text:p>
<text:p text:style-name="P3"><text:placeholder text:placeholder-type="text">&lt;when test=&quot;company and company.logo&quot;&gt;</text:placeholder></text:p>
<text:p text:style-name="P3"><draw:frame draw:style-name="fr1" draw:name="image:company.get_logo_cm(7, 3.5)" text:anchor-type="as-char" svg:width="7.001cm" draw:z-index="2">
<draw:text-box fo:min-height="3cm">
<text:p text:style-name="P4"/>
</draw:text-box>
</draw:frame></text:p>
<text:p text:style-name="P3"><text:placeholder text:placeholder-type="text">&lt;/when&gt;</text:placeholder></text:p>
<text:p text:style-name="P3"><text:placeholder text:placeholder-type="text">&lt;when test=&quot;company&quot;&gt;</text:placeholder></text:p>
<text:p text:style-name="P3"><text:placeholder text:placeholder-type="text">&lt;company.rec_name&gt;</text:placeholder></text:p>
<text:p text:style-name="P3"><text:placeholder text:placeholder-type="text">&lt;/when&gt;</text:placeholder></text:p>
<text:p text:style-name="P3"><text:placeholder text:placeholder-type="text">&lt;/choose&gt;</text:placeholder></text:p>
</style:header>
<style:footer>
<text:p text:style-name="P2"><text:placeholder text:placeholder-type="text">&lt;if test=&quot;company and company.footer&quot;&gt;</text:placeholder></text:p>
<text:p text:style-name="P2"><text:placeholder text:placeholder-type="text">&lt;for each=&quot;line in company.footer_used.split(&apos;\n&apos;)&quot;&gt;</text:placeholder></text:p>
<text:p text:style-name="P2"><text:placeholder text:placeholder-type="text">&lt;line&gt;</text:placeholder></text:p>
<text:p text:style-name="P2"><text:placeholder text:placeholder-type="text">&lt;/for&gt;</text:placeholder></text:p>
<text:p text:style-name="P2"><text:placeholder text:placeholder-type="text">&lt;/if&gt;</text:placeholder></text:p>
<text:p text:style-name="P5"><text:page-number text:select-page="current">3</text:page-number>/<text:page-count>3</text:page-count></text:p>
</style:footer>
</style:master-page>
</office:master-styles>
<office:body>
<office:text text:use-soft-page-breaks="true">
<office:forms form:automatic-focus="false" form:apply-design-mode="false"/>
<text:sequence-decls>
<text:sequence-decl text:display-outline-level="0" text:name="Illustration"/>
<text:sequence-decl text:display-outline-level="0" text:name="Table"/>
<text:sequence-decl text:display-outline-level="0" text:name="Text"/>
<text:sequence-decl text:display-outline-level="0" text:name="Drawing"/>
<text:sequence-decl text:display-outline-level="0" text:name="Figure"/>
</text:sequence-decls>
<text:p text:style-name="P16"><text:placeholder text:placeholder-type="text">&lt;for each=&quot;shipment in records&quot;&gt;</text:placeholder></text:p>
<text:p text:style-name="P16"><text:placeholder text:placeholder-type="text">&lt;choose test=&quot;shipment.state&quot;&gt;</text:placeholder></text:p>
<text:p text:style-name="Standard"><text:placeholder text:placeholder-type="text">&lt;when test=&quot;&apos;draft&apos;&quot;&gt;</text:placeholder></text:p>
<text:p text:style-name="P19"><text:span text:style-name="T6">Draft </text:span>Restocking List</text:p>
<text:p text:style-name="Standard"><text:placeholder text:placeholder-type="text">&lt;/when&gt;</text:placeholder></text:p>
<text:p text:style-name="P21"><text:placeholder text:placeholder-type="text">&lt;when test=&quot;&apos;cancelled&apos;&quot;&gt;</text:placeholder></text:p>
<text:p text:style-name="P19"><text:span text:style-name="T6">Cancelled </text:span>Restocking List</text:p>
<text:p text:style-name="P21"><text:placeholder text:placeholder-type="text">&lt;/when&gt;</text:placeholder></text:p>
<text:p text:style-name="Standard"><text:placeholder text:placeholder-type="text">&lt;otherwise&gt;</text:placeholder></text:p>
<text:p text:style-name="P18">Restocking List No: <text:span text:style-name="T6"><text:placeholder text:placeholder-type="text">&lt;shipment.number&gt;</text:placeholder></text:span></text:p>
<text:p text:style-name="Standard"><text:placeholder text:placeholder-type="text">&lt;/otherwise&gt;</text:placeholder></text:p>
<text:p text:style-name="Standard"><text:placeholder text:placeholder-type="text">&lt;/choose&gt;</text:placeholder></text:p>
<text:p text:style-name="P20"><text:span text:style-name="T1">Reference:</text:span><text:span text:style-name="T5"> </text:span><text:span text:style-name="T5"><text:placeholder text:placeholder-type="text">&lt;shipment.origins or &apos;&apos;&gt;</text:placeholder></text:span><text:span text:style-name="T5"><text:placeholder text:placeholder-type="text">&lt;&apos;, &apos; if (shipment.origins and shipment.reference) else &apos;&apos;&gt;</text:placeholder></text:span><text:span text:style-name="T5"><text:placeholder text:placeholder-type="text">&lt;shipment.reference or &apos;&apos;&gt;</text:placeholder></text:span></text:p>
<text:p text:style-name="Standard"><text:span text:style-name="T2">Customer</text:span><text:span text:style-name="T1">:</text:span><text:span text:style-name="T5"> </text:span><text:span text:style-name="T5"><text:placeholder text:placeholder-type="text">&lt;shipment.customer.rec_name&gt;</text:placeholder></text:span></text:p>
<text:p text:style-name="Standard"><text:span text:style-name="T1">Planned Date:</text:span><text:span text:style-name="T5"> </text:span><text:span text:style-name="T5"><text:placeholder text:placeholder-type="text">&lt;shipment.planned_date and format_date(shipment.planned_date, user.language) or &apos;&apos;&gt;</text:placeholder></text:span></text:p>
<text:p text:style-name="Standard"><text:span text:style-name="T1">Warehouse:</text:span><text:span text:style-name="T5"> </text:span><text:span text:style-name="T5"><text:placeholder text:placeholder-type="text">&lt;shipment.warehouse.rec_name&gt;</text:placeholder></text:span></text:p>
<table:table table:name="Table1" table:style-name="Table1">
<table:table-column table:style-name="Table1.A" table:number-columns-repeated="2"/>
<table:table-column table:style-name="Table1.C"/>
<table:table-column table:style-name="Table1.D"/>
<table:table-header-rows>
<table:table-row>
<table:table-cell table:style-name="Table1.A1" office:value-type="string">
<text:p text:style-name="P14">From Location</text:p>
</table:table-cell>
<table:table-cell table:style-name="Table1.A1" office:value-type="string">
<text:p text:style-name="P14">To Location</text:p>
</table:table-cell>
<table:table-cell table:style-name="Table1.A1" office:value-type="string">
<text:p text:style-name="P14">Product</text:p>
</table:table-cell>
<table:table-cell table:style-name="Table1.D1" office:value-type="string">
<text:p text:style-name="P14">Quantity</text:p>
</table:table-cell>
</table:table-row>
</table:table-header-rows>
<table:table-row>
<table:table-cell table:style-name="Table1.A2" table:number-columns-spanned="2" office:value-type="string">
<text:p text:style-name="P15"><text:placeholder text:placeholder-type="text">&lt;for each=&quot;move in moves(shipment)&quot;&gt;</text:placeholder></text:p>
</table:table-cell>
<table:covered-table-cell/>
<table:table-cell table:style-name="Table1.C2" office:value-type="string">
<text:p text:style-name="P15"/>
</table:table-cell>
<table:table-cell table:style-name="Table1.D2" office:value-type="string">
<text:p text:style-name="P15"/>
</table:table-cell>
</table:table-row>
<table:table-row>
<table:table-cell table:style-name="Table1.A3" office:value-type="string">
<text:p text:style-name="Table_20_Contents"><text:placeholder text:placeholder-type="text">&lt;move.from_location_name&gt;</text:placeholder></text:p>
</table:table-cell>
<table:table-cell table:style-name="Table1.A3" office:value-type="string">
<text:p text:style-name="Table_20_Contents"><text:placeholder text:placeholder-type="text">&lt;move.to_location_name&gt;</text:placeholder></text:p>
</table:table-cell>
<table:table-cell table:style-name="Table1.C2" office:value-type="string">
<text:p text:style-name="Table_20_Contents"><text:placeholder text:placeholder-type="text">&lt;move.product.rec_name&gt;</text:placeholder></text:p>
</table:table-cell>
<table:table-cell table:style-name="Table1.D2" office:value-type="string">
<text:p text:style-name="P13"><text:placeholder text:placeholder-type="text">&lt;format_number_symbol(move.quantity, user.language, move.unit, digits=move.unit.digits)&gt;</text:placeholder><text:soft-page-break/></text:p>
</table:table-cell>
</table:table-row>
<table:table-row>
<table:table-cell table:style-name="Table1.A2" table:number-columns-spanned="2" office:value-type="string">
<text:p text:style-name="Table_20_Contents"><text:placeholder text:placeholder-type="text">&lt;/for&gt;</text:placeholder></text:p>
</table:table-cell>
<table:covered-table-cell/>
<table:table-cell table:style-name="Table1.C2" office:value-type="string">
<text:p text:style-name="P15"/>
</table:table-cell>
<table:table-cell table:style-name="Table1.D2" office:value-type="string">
<text:p text:style-name="P15"/>
</table:table-cell>
</table:table-row>
</table:table>
<text:p text:style-name="Text_20_body"><text:placeholder text:placeholder-type="text">&lt;/for&gt;</text:placeholder></text:p>
</office:text>
</office:body>
</office:document>

View File

@@ -0,0 +1,601 @@
<?xml version="1.0" encoding="UTF-8"?>
<office:document xmlns:css3t="http://www.w3.org/TR/css3-text/" xmlns:grddl="http://www.w3.org/2003/g/data-view#" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xforms="http://www.w3.org/2002/xforms" xmlns:dom="http://www.w3.org/2001/xml-events" xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" xmlns:ooow="http://openoffice.org/2004/writer" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:drawooo="http://openoffice.org/2010/draw" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" xmlns:tableooo="http://openoffice.org/2009/table" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:rpt="http://openoffice.org/2005/report" xmlns:formx="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:officeooo="http://openoffice.org/2009/office" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text">
<office:meta><meta:generator>LibreOffice/7.6.4.1$Linux_X86_64 LibreOffice_project/60$Build-1</meta:generator><meta:creation-date>2008-06-07T15:29:02</meta:creation-date><dc:date>2009-03-26T18:33:13</dc:date><meta:editing-cycles>1</meta:editing-cycles><meta:editing-duration>PT0S</meta:editing-duration><meta:document-statistic meta:table-count="1" meta:image-count="0" meta:object-count="0" meta:page-count="3" meta:paragraph-count="52" meta:word-count="118" meta:character-count="1471" meta:non-whitespace-character-count="1404"/><meta:user-defined meta:name="Info 1"/><meta:user-defined meta:name="Info 2"/><meta:user-defined meta:name="Info 3"/><meta:user-defined meta:name="Info 4"/></office:meta>
<office:settings>
<config:config-item-set config:name="ooo:view-settings">
<config:config-item config:name="ViewAreaTop" config:type="long">0</config:config-item>
<config:config-item config:name="ViewAreaLeft" config:type="long">0</config:config-item>
<config:config-item config:name="ViewAreaWidth" config:type="long">25269</config:config-item>
<config:config-item config:name="ViewAreaHeight" config:type="long">23973</config:config-item>
<config:config-item config:name="ShowRedlineChanges" config:type="boolean">true</config:config-item>
<config:config-item config:name="InBrowseMode" config:type="boolean">false</config:config-item>
<config:config-item-map-indexed config:name="Views">
<config:config-item-map-entry>
<config:config-item config:name="ViewId" config:type="string">view2</config:config-item>
<config:config-item config:name="ViewLeft" config:type="long">5955</config:config-item>
<config:config-item config:name="ViewTop" config:type="long">10248</config:config-item>
<config:config-item config:name="VisibleLeft" config:type="long">0</config:config-item>
<config:config-item config:name="VisibleTop" config:type="long">0</config:config-item>
<config:config-item config:name="VisibleRight" config:type="long">25268</config:config-item>
<config:config-item config:name="VisibleBottom" config:type="long">23971</config:config-item>
<config:config-item config:name="ZoomType" config:type="short">0</config:config-item>
<config:config-item config:name="ViewLayoutColumns" config:type="short">0</config:config-item>
<config:config-item config:name="ViewLayoutBookMode" config:type="boolean">false</config:config-item>
<config:config-item config:name="ZoomFactor" config:type="short">100</config:config-item>
<config:config-item config:name="IsSelectedFrame" config:type="boolean">false</config:config-item>
<config:config-item config:name="KeepRatio" config:type="boolean">false</config:config-item>
<config:config-item config:name="AnchoredTextOverflowLegacy" config:type="boolean">false</config:config-item>
<config:config-item config:name="LegacySingleLineFontwork" config:type="boolean">false</config:config-item>
<config:config-item config:name="ConnectorUseSnapRect" config:type="boolean">false</config:config-item>
<config:config-item config:name="IgnoreBreakAfterMultilineField" config:type="boolean">false</config:config-item>
</config:config-item-map-entry>
</config:config-item-map-indexed>
</config:config-item-set>
<config:config-item-set config:name="ooo:configuration-settings">
<config:config-item config:name="PrintRightPages" config:type="boolean">true</config:config-item>
<config:config-item config:name="PrintProspectRTL" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintLeftPages" config:type="boolean">true</config:config-item>
<config:config-item config:name="PrintPaperFromSetup" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintControls" config:type="boolean">true</config:config-item>
<config:config-item config:name="PrintProspect" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintBlackFonts" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintAnnotationMode" config:type="short">0</config:config-item>
<config:config-item config:name="PrintEmptyPages" config:type="boolean">true</config:config-item>
<config:config-item config:name="PrintSingleJobs" config:type="boolean">false</config:config-item>
<config:config-item config:name="AutoFirstLineIndentDisregardLineSpace" config:type="boolean">false</config:config-item>
<config:config-item config:name="HeaderSpacingBelowLastPara" config:type="boolean">false</config:config-item>
<config:config-item config:name="ProtectBookmarks" config:type="boolean">false</config:config-item>
<config:config-item config:name="ContinuousEndnotes" config:type="boolean">false</config:config-item>
<config:config-item config:name="DisableOffPagePositioning" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintTables" config:type="boolean">true</config:config-item>
<config:config-item config:name="SubtractFlysAnchoredAtFlys" config:type="boolean">true</config:config-item>
<config:config-item config:name="ApplyParagraphMarkFormatToNumbering" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintFaxName" config:type="string"/>
<config:config-item config:name="SurroundTextWrapSmall" config:type="boolean">false</config:config-item>
<config:config-item config:name="TreatSingleColumnBreakAsPageBreak" config:type="boolean">false</config:config-item>
<config:config-item config:name="PropLineSpacingShrinksFirstLine" config:type="boolean">false</config:config-item>
<config:config-item config:name="TabOverSpacing" config:type="boolean">false</config:config-item>
<config:config-item config:name="TabOverMargin" config:type="boolean">false</config:config-item>
<config:config-item config:name="EmbedComplexScriptFonts" config:type="boolean">true</config:config-item>
<config:config-item config:name="EmbedLatinScriptFonts" config:type="boolean">true</config:config-item>
<config:config-item config:name="EmbedOnlyUsedFonts" config:type="boolean">false</config:config-item>
<config:config-item config:name="EmbedFonts" config:type="boolean">false</config:config-item>
<config:config-item config:name="ClippedPictures" config:type="boolean">false</config:config-item>
<config:config-item config:name="FrameAutowidthWithMorePara" config:type="boolean">false</config:config-item>
<config:config-item config:name="FloattableNomargins" config:type="boolean">false</config:config-item>
<config:config-item config:name="UnbreakableNumberings" config:type="boolean">false</config:config-item>
<config:config-item config:name="AllowPrintJobCancel" config:type="boolean">true</config:config-item>
<config:config-item config:name="UseFormerObjectPositioning" config:type="boolean">false</config:config-item>
<config:config-item config:name="UseOldNumbering" config:type="boolean">false</config:config-item>
<config:config-item config:name="RsidRoot" config:type="int">1813180</config:config-item>
<config:config-item config:name="PrinterPaperFromSetup" config:type="boolean">false</config:config-item>
<config:config-item config:name="CurrentDatabaseDataSource" config:type="string"/>
<config:config-item config:name="UpdateFromTemplate" config:type="boolean">true</config:config-item>
<config:config-item config:name="AddFrameOffsets" config:type="boolean">false</config:config-item>
<config:config-item config:name="LoadReadonly" config:type="boolean">false</config:config-item>
<config:config-item config:name="Rsid" config:type="int">3382560</config:config-item>
<config:config-item config:name="FootnoteInColumnToPageEnd" config:type="boolean">true</config:config-item>
<config:config-item config:name="ProtectFields" config:type="boolean">false</config:config-item>
<config:config-item config:name="SaveGlobalDocumentLinks" config:type="boolean">false</config:config-item>
<config:config-item config:name="ClipAsCharacterAnchoredWriterFlyFrames" config:type="boolean">false</config:config-item>
<config:config-item config:name="LinkUpdateMode" config:type="short">1</config:config-item>
<config:config-item config:name="AddExternalLeading" config:type="boolean">true</config:config-item>
<config:config-item config:name="PrintGraphics" config:type="boolean">true</config:config-item>
<config:config-item config:name="EmbedSystemFonts" config:type="boolean">false</config:config-item>
<config:config-item config:name="IsLabelDocument" config:type="boolean">false</config:config-item>
<config:config-item config:name="AddParaLineSpacingToTableCells" config:type="boolean">false</config:config-item>
<config:config-item config:name="UseFormerTextWrapping" config:type="boolean">false</config:config-item>
<config:config-item config:name="HyphenateURLs" config:type="boolean">true</config:config-item>
<config:config-item config:name="AddParaTableSpacingAtStart" config:type="boolean">true</config:config-item>
<config:config-item config:name="TabsRelativeToIndent" config:type="boolean">true</config:config-item>
<config:config-item config:name="FieldAutoUpdate" config:type="boolean">true</config:config-item>
<config:config-item config:name="SaveVersionOnClose" config:type="boolean">false</config:config-item>
<config:config-item config:name="ChartAutoUpdate" config:type="boolean">true</config:config-item>
<config:config-item config:name="ImagePreferredDPI" config:type="int">0</config:config-item>
<config:config-item config:name="PrinterSetup" config:type="base64Binary"/>
<config:config-item config:name="SmallCapsPercentage66" config:type="boolean">true</config:config-item>
<config:config-item config:name="AlignTabStopPosition" config:type="boolean">true</config:config-item>
<config:config-item config:name="DropCapPunctuation" config:type="boolean">false</config:config-item>
<config:config-item config:name="MathBaselineAlignment" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrinterName" config:type="string"/>
<config:config-item config:name="CharacterCompressionType" config:type="short">0</config:config-item>
<config:config-item config:name="AddParaTableSpacing" config:type="boolean">true</config:config-item>
<config:config-item config:name="DoNotJustifyLinesWithManualBreak" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintHiddenText" config:type="boolean">false</config:config-item>
<config:config-item config:name="IsKernAsianPunctuation" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrinterIndependentLayout" config:type="string">high-resolution</config:config-item>
<config:config-item config:name="TabOverflow" config:type="boolean">false</config:config-item>
<config:config-item config:name="AddParaSpacingToTableCells" config:type="boolean">true</config:config-item>
<config:config-item config:name="AddVerticalFrameOffsets" config:type="boolean">false</config:config-item>
<config:config-item config:name="TabAtLeftIndentForParagraphsInList" config:type="boolean">false</config:config-item>
<config:config-item config:name="ApplyUserData" config:type="boolean">false</config:config-item>
<config:config-item config:name="MsWordCompMinLineHeightByFly" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintTextPlaceholder" config:type="boolean">false</config:config-item>
<config:config-item config:name="IgnoreFirstLineIndentInNumbering" config:type="boolean">false</config:config-item>
<config:config-item config:name="UseFormerLineSpacing" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintPageBackground" config:type="boolean">true</config:config-item>
<config:config-item config:name="RedlineProtectionKey" config:type="base64Binary"/>
<config:config-item config:name="EmbedAsianScriptFonts" config:type="boolean">true</config:config-item>
<config:config-item config:name="BackgroundParaOverDrawings" config:type="boolean">false</config:config-item>
<config:config-item config:name="SaveThumbnail" config:type="boolean">true</config:config-item>
<config:config-item config:name="ConsiderTextWrapOnObjPos" config:type="boolean">false</config:config-item>
<config:config-item config:name="EmbeddedDatabaseName" config:type="string"/>
<config:config-item config:name="ProtectForm" config:type="boolean">false</config:config-item>
<config:config-item config:name="DoNotResetParaAttrsForNumFont" config:type="boolean">false</config:config-item>
<config:config-item config:name="MsWordCompTrailingBlanks" config:type="boolean">false</config:config-item>
<config:config-item config:name="EmptyDbFieldHidesPara" config:type="boolean">false</config:config-item>
<config:config-item config:name="TableRowKeep" config:type="boolean">false</config:config-item>
<config:config-item config:name="NoNumberingShowFollowBy" config:type="boolean">false</config:config-item>
<config:config-item config:name="InvertBorderSpacing" config:type="boolean">false</config:config-item>
<config:config-item config:name="IgnoreTabsAndBlanksForLineCalculation" config:type="boolean">false</config:config-item>
<config:config-item config:name="DoNotCaptureDrawObjsOnPage" config:type="boolean">false</config:config-item>
<config:config-item config:name="GutterAtTop" config:type="boolean">false</config:config-item>
<config:config-item config:name="StylesNoDefault" config:type="boolean">false</config:config-item>
<config:config-item config:name="UnxForceZeroExtLeading" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintReversed" config:type="boolean">false</config:config-item>
<config:config-item config:name="UseOldPrinterMetrics" config:type="boolean">false</config:config-item>
<config:config-item config:name="CurrentDatabaseCommandType" config:type="int">0</config:config-item>
<config:config-item config:name="PrintDrawings" config:type="boolean">true</config:config-item>
<config:config-item config:name="OutlineLevelYieldsNumbering" config:type="boolean">false</config:config-item>
<config:config-item config:name="CurrentDatabaseCommand" config:type="string"/>
<config:config-item config:name="CollapseEmptyCellPara" config:type="boolean">true</config:config-item>
</config:config-item-set>
</office:settings>
<office:scripts>
<office:script script:language="ooo:Basic">
<ooo:libraries xmlns:ooo="http://openoffice.org/2004/office" xmlns:xlink="http://www.w3.org/1999/xlink"/>
</office:script>
</office:scripts>
<office:font-face-decls>
<style:font-face style:name="Andale Sans UI" svg:font-family="&apos;Andale Sans UI&apos;" style:font-family-generic="system" style:font-pitch="variable"/>
<style:font-face style:name="DejaVu Sans" svg:font-family="&apos;DejaVu Sans&apos;" style:font-family-generic="system" style:font-pitch="variable"/>
<style:font-face style:name="Liberation Sans" svg:font-family="&apos;Liberation Sans&apos;" style:font-family-generic="swiss" style:font-pitch="variable"/>
<style:font-face style:name="Liberation Sans1" svg:font-family="&apos;Liberation Sans&apos;" style:font-adornments="Regular" style:font-family-generic="swiss" style:font-pitch="variable"/>
<style:font-face style:name="Liberation Serif" svg:font-family="&apos;Liberation Serif&apos;" style:font-family-generic="roman" style:font-pitch="variable"/>
<style:font-face style:name="Liberation Serif1" svg:font-family="&apos;Liberation Serif&apos;" style:font-adornments="Bold" style:font-family-generic="roman" style:font-pitch="variable"/>
<style:font-face style:name="Liberation Serif2" svg:font-family="&apos;Liberation Serif&apos;" style:font-adornments="Regular" style:font-family-generic="roman" style:font-pitch="variable"/>
<style:font-face style:name="StarSymbol" svg:font-family="StarSymbol"/>
<style:font-face style:name="Thorndale AMT" svg:font-family="&apos;Thorndale AMT&apos;" style:font-family-generic="roman" style:font-pitch="variable"/>
</office:font-face-decls>
<office:styles>
<style:default-style style:family="graphic">
<style:graphic-properties svg:stroke-color="#000000" draw:fill-color="#99ccff" fo:wrap-option="no-wrap" draw:shadow-offset-x="0.3cm" draw:shadow-offset-y="0.3cm" draw:start-line-spacing-horizontal="0.283cm" draw:start-line-spacing-vertical="0.283cm" draw:end-line-spacing-horizontal="0.283cm" draw:end-line-spacing-vertical="0.283cm" style:writing-mode="lr-tb" style:flow-with-text="false"/>
<style:paragraph-properties style:text-autospace="ideograph-alpha" style:line-break="strict" loext:tab-stop-distance="0cm" style:font-independent-line-spacing="false">
<style:tab-stops/>
</style:paragraph-properties>
<style:text-properties style:use-window-font-color="true" loext:opacity="0%" style:font-name="Thorndale AMT" fo:font-size="12pt" fo:language="en" fo:country="US" style:letter-kerning="true" style:font-name-asian="Andale Sans UI" style:font-size-asian="10.5pt" style:language-asian="zxx" style:country-asian="none" style:font-name-complex="Andale Sans UI" style:font-size-complex="12pt" style:language-complex="zxx" style:country-complex="none"/>
</style:default-style>
<style:default-style style:family="paragraph">
<style:paragraph-properties fo:hyphenation-ladder-count="no-limit" style:text-autospace="ideograph-alpha" style:punctuation-wrap="hanging" style:line-break="strict" style:tab-stop-distance="1.251cm" style:writing-mode="lr-tb"/>
<style:text-properties style:use-window-font-color="true" loext:opacity="0%" style:font-name="Thorndale AMT" fo:font-size="12pt" fo:language="en" fo:country="US" style:letter-kerning="true" style:font-name-asian="Andale Sans UI" style:font-size-asian="10.5pt" style:language-asian="zxx" style:country-asian="none" style:font-name-complex="Andale Sans UI" style:font-size-complex="12pt" style:language-complex="zxx" style:country-complex="none" fo:hyphenate="false" fo:hyphenation-remain-char-count="2" fo:hyphenation-push-char-count="2" loext:hyphenation-no-caps="false" loext:hyphenation-no-last-word="false" loext:hyphenation-word-char-count="5" loext:hyphenation-zone="no-limit"/>
</style:default-style>
<style:default-style style:family="table">
<style:table-properties table:border-model="collapsing"/>
</style:default-style>
<style:default-style style:family="table-row">
<style:table-row-properties fo:keep-together="auto"/>
</style:default-style>
<style:style style:name="Standard" style:family="paragraph" style:class="text">
<style:text-properties style:font-name="Liberation Sans1" fo:font-family="&apos;Liberation Sans&apos;" style:font-style-name="Regular" style:font-family-generic="swiss" style:font-pitch="variable" style:font-size-asian="10.5pt"/>
</style:style>
<style:style style:name="Heading" style:family="paragraph" style:parent-style-name="Standard" style:next-style-name="Text_20_body" style:class="text">
<style:paragraph-properties fo:margin-top="0.423cm" fo:margin-bottom="0.212cm" style:contextual-spacing="false" fo:keep-with-next="always"/>
<style:text-properties style:font-name="Liberation Serif2" fo:font-family="&apos;Liberation Serif&apos;" style:font-style-name="Regular" style:font-family-generic="roman" style:font-pitch="variable" fo:font-size="16pt" style:font-name-asian="DejaVu Sans" style:font-family-asian="&apos;DejaVu Sans&apos;" style:font-family-generic-asian="system" style:font-pitch-asian="variable" style:font-size-asian="14pt" style:font-name-complex="DejaVu Sans" style:font-family-complex="&apos;DejaVu Sans&apos;" style:font-family-generic-complex="system" style:font-pitch-complex="variable" style:font-size-complex="14pt"/>
</style:style>
<style:style style:name="Text_20_body" style:display-name="Text body" style:family="paragraph" style:parent-style-name="Standard" style:class="text">
<style:paragraph-properties fo:margin-top="0cm" fo:margin-bottom="0.212cm" style:contextual-spacing="false"/>
<style:text-properties style:font-name="Liberation Sans1" fo:font-family="&apos;Liberation Sans&apos;" style:font-style-name="Regular" style:font-family-generic="swiss" style:font-pitch="variable" style:font-size-asian="10.5pt"/>
</style:style>
<style:style style:name="List" style:family="paragraph" style:parent-style-name="Text_20_body" style:class="list">
<style:text-properties style:font-size-asian="12pt"/>
</style:style>
<style:style style:name="Caption" style:family="paragraph" style:parent-style-name="Standard" style:class="extra">
<style:paragraph-properties fo:margin-top="0.212cm" fo:margin-bottom="0.212cm" style:contextual-spacing="false" text:number-lines="false" text:line-number="0"/>
<style:text-properties fo:font-size="12pt" fo:font-style="italic" style:font-size-asian="12pt" style:font-style-asian="italic" style:font-size-complex="12pt" style:font-style-complex="italic"/>
</style:style>
<style:style style:name="Index" style:family="paragraph" style:parent-style-name="Standard" style:class="index">
<style:paragraph-properties text:number-lines="false" text:line-number="0"/>
<style:text-properties style:font-size-asian="12pt"/>
</style:style>
<style:style style:name="Table_20_Contents" style:display-name="Table Contents" style:family="paragraph" style:parent-style-name="Standard" style:class="extra">
<style:paragraph-properties text:number-lines="false" text:line-number="0"/>
<style:text-properties style:font-size-asian="10.5pt"/>
</style:style>
<style:style style:name="Table_20_Heading" style:display-name="Table Heading" style:family="paragraph" style:parent-style-name="Table_20_Contents" style:class="extra" style:master-page-name="">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false" style:page-number="auto" text:number-lines="false" text:line-number="0"/>
<style:text-properties style:font-name="Liberation Serif1" fo:font-family="&apos;Liberation Serif&apos;" style:font-style-name="Bold" style:font-family-generic="roman" style:font-pitch="variable" fo:font-weight="bold" style:font-size-asian="10.5pt" style:font-weight-asian="bold" style:font-weight-complex="bold"/>
</style:style>
<style:style style:name="Heading_20_1" style:display-name="Heading 1" style:family="paragraph" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:class="text">
<style:text-properties fo:font-size="16pt" fo:font-weight="bold" style:font-size-asian="115%" style:font-weight-asian="bold" style:font-size-complex="115%" style:font-weight-complex="bold"/>
</style:style>
<style:style style:name="Heading_20_2" style:display-name="Heading 2" style:family="paragraph" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:class="text">
<style:text-properties fo:font-size="14pt" fo:font-style="italic" fo:font-weight="bold" style:font-size-asian="14pt" style:font-style-asian="italic" style:font-weight-asian="bold" style:font-size-complex="14pt" style:font-style-complex="italic" style:font-weight-complex="bold"/>
</style:style>
<style:style style:name="Heading_20_3" style:display-name="Heading 3" style:family="paragraph" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:class="text">
<style:text-properties fo:font-size="14pt" fo:font-weight="bold" style:font-size-asian="14pt" style:font-weight-asian="bold" style:font-size-complex="14pt" style:font-weight-complex="bold"/>
</style:style>
<style:style style:name="Header_20_and_20_Footer" style:display-name="Header and Footer" style:family="paragraph" style:parent-style-name="Standard" style:class="extra">
<style:paragraph-properties text:number-lines="false" text:line-number="0">
<style:tab-stops>
<style:tab-stop style:position="8.5cm" style:type="center"/>
<style:tab-stop style:position="17cm" style:type="right"/>
</style:tab-stops>
</style:paragraph-properties>
</style:style>
<style:style style:name="Header" style:family="paragraph" style:parent-style-name="Standard" style:class="extra">
<style:paragraph-properties text:number-lines="false" text:line-number="0">
<style:tab-stops>
<style:tab-stop style:position="8.795cm" style:type="center"/>
<style:tab-stop style:position="17.59cm" style:type="right"/>
</style:tab-stops>
</style:paragraph-properties>
<style:text-properties fo:font-size="9pt" style:font-size-asian="10.5pt"/>
</style:style>
<style:style style:name="Footer" style:family="paragraph" style:parent-style-name="Standard" style:class="extra">
<style:paragraph-properties text:number-lines="false" text:line-number="0">
<style:tab-stops>
<style:tab-stop style:position="8.795cm" style:type="center"/>
<style:tab-stop style:position="17.59cm" style:type="right"/>
</style:tab-stops>
</style:paragraph-properties>
<style:text-properties fo:font-size="9pt" style:font-size-asian="10.5pt"/>
</style:style>
<style:style style:name="Text_20_body_20_indent" style:display-name="Text body indent" style:family="paragraph" style:parent-style-name="Text_20_body" style:class="text">
<style:paragraph-properties fo:margin-left="0.499cm" fo:margin-right="0cm" fo:text-indent="0cm" style:auto-text-indent="false"/>
</style:style>
<style:style style:name="Text" style:family="paragraph" style:parent-style-name="Caption" style:class="extra"/>
<style:style style:name="Quotations" style:family="paragraph" style:parent-style-name="Standard" style:class="html">
<style:paragraph-properties fo:margin-left="1cm" fo:margin-right="1cm" fo:margin-top="0cm" fo:margin-bottom="0.499cm" style:contextual-spacing="false" fo:text-indent="0cm" style:auto-text-indent="false"/>
</style:style>
<style:style style:name="Title" style:family="paragraph" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:class="chapter">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
<style:text-properties fo:font-size="28pt" fo:font-weight="bold" style:font-size-asian="28pt" style:font-weight-asian="bold" style:font-size-complex="28pt" style:font-weight-complex="bold"/>
</style:style>
<style:style style:name="Subtitle" style:family="paragraph" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:class="chapter">
<style:paragraph-properties fo:margin-top="0.106cm" fo:margin-bottom="0.212cm" style:contextual-spacing="false" fo:text-align="center" style:justify-single-word="false"/>
<style:text-properties fo:font-size="18pt" style:font-size-asian="18pt" style:font-size-complex="18pt"/>
</style:style>
<style:style style:name="Frame_20_contents" style:display-name="Frame contents" style:family="paragraph" style:parent-style-name="Standard" style:class="extra"/>
<style:style style:name="Placeholder" style:family="text">
<style:text-properties fo:font-variant="small-caps" fo:color="#008080" loext:opacity="100%" style:text-underline-style="dotted" style:text-underline-width="auto" style:text-underline-color="font-color"/>
</style:style>
<style:style style:name="Bullet_20_Symbols" style:display-name="Bullet Symbols" style:family="text">
<style:text-properties style:font-name="StarSymbol" fo:font-family="StarSymbol" fo:font-size="9pt" style:font-name-asian="StarSymbol" style:font-family-asian="StarSymbol" style:font-size-asian="9pt" style:font-name-complex="StarSymbol" style:font-family-complex="StarSymbol" style:font-size-complex="9pt"/>
</style:style>
<style:style style:name="Frame" style:family="graphic">
<style:graphic-properties text:anchor-type="paragraph" svg:x="0cm" svg:y="0cm" fo:margin-left="0.201cm" fo:margin-right="0.201cm" fo:margin-top="0.201cm" fo:margin-bottom="0.201cm" style:wrap="parallel" style:number-wrapped-paragraphs="no-limit" style:wrap-contour="false" style:vertical-pos="top" style:vertical-rel="paragraph-content" style:horizontal-pos="center" style:horizontal-rel="paragraph-content" fo:background-color="transparent" draw:fill="none" draw:fill-color="#99ccff" fo:padding="0.15cm" fo:border="0.06pt solid #000000"/>
</style:style>
<text:outline-style style:name="Outline">
<text:outline-level-style text:level="1" loext:num-list-format="%1%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
<text:outline-level-style text:level="2" loext:num-list-format="%2%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
<text:outline-level-style text:level="3" loext:num-list-format="%3%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
<text:outline-level-style text:level="4" loext:num-list-format="%4%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
<text:outline-level-style text:level="5" loext:num-list-format="%5%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
<text:outline-level-style text:level="6" loext:num-list-format="%6%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
<text:outline-level-style text:level="7" loext:num-list-format="%7%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
<text:outline-level-style text:level="8" loext:num-list-format="%8%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
<text:outline-level-style text:level="9" loext:num-list-format="%9%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
<text:outline-level-style text:level="10" loext:num-list-format="%10%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
</text:outline-style>
<text:notes-configuration text:note-class="footnote" style:num-format="1" text:start-value="0" text:footnotes-position="page" text:start-numbering-at="document"/>
<text:notes-configuration text:note-class="endnote" style:num-format="i" text:start-value="0"/>
<text:linenumbering-configuration text:number-lines="false" text:offset="0.499cm" style:num-format="1" text:number-position="left" text:increment="5"/>
<loext:theme loext:name="Office Theme">
<loext:theme-colors loext:name="LibreOffice">
<loext:color loext:name="dark1" loext:color="#000000"/>
<loext:color loext:name="light1" loext:color="#ffffff"/>
<loext:color loext:name="dark2" loext:color="#000000"/>
<loext:color loext:name="light2" loext:color="#ffffff"/>
<loext:color loext:name="accent1" loext:color="#18a303"/>
<loext:color loext:name="accent2" loext:color="#0369a3"/>
<loext:color loext:name="accent3" loext:color="#a33e03"/>
<loext:color loext:name="accent4" loext:color="#8e03a3"/>
<loext:color loext:name="accent5" loext:color="#c99c00"/>
<loext:color loext:name="accent6" loext:color="#c9211e"/>
<loext:color loext:name="hyperlink" loext:color="#0000ee"/>
<loext:color loext:name="followed-hyperlink" loext:color="#551a8b"/>
</loext:theme-colors>
</loext:theme>
</office:styles>
<office:automatic-styles>
<style:style style:name="Table1" style:family="table">
<style:table-properties style:width="16.999cm" fo:margin-left="0cm" fo:margin-right="0cm" fo:margin-top="0.381cm" fo:margin-bottom="0cm" table:align="margins"/>
</style:style>
<style:style style:name="Table1.A" style:family="table-column">
<style:table-column-properties style:column-width="9.606cm" style:rel-column-width="37032*"/>
</style:style>
<style:style style:name="Table1.B" style:family="table-column">
<style:table-column-properties style:column-width="7.392cm" style:rel-column-width="28503*"/>
</style:style>
<style:style style:name="Table1.A1" style:family="table-cell">
<style:table-cell-properties fo:background-color="#cccccc" fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="0.05pt solid #000000" fo:border-bottom="0.05pt solid #000000">
<style:background-image/>
</style:table-cell-properties>
</style:style>
<style:style style:name="Table1.B1" style:family="table-cell">
<style:table-cell-properties fo:background-color="#cccccc" fo:padding="0.097cm" fo:border="0.05pt solid #000000">
<style:background-image/>
</style:table-cell-properties>
</style:style>
<style:style style:name="Table1.A2" style:family="table-cell">
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="0.05pt solid #000000" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
</style:style>
<style:style style:name="Table1.A3" style:family="table-cell">
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
</style:style>
<style:style style:name="Table1.B3" style:family="table-cell">
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="0.05pt solid #000000" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
</style:style>
<style:style style:name="Table1.A4" style:family="table-cell">
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="0.05pt solid #000000" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
</style:style>
<style:style style:name="P1" style:family="paragraph" style:parent-style-name="Header">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
<style:text-properties officeooo:paragraph-rsid="00224f9c"/>
</style:style>
<style:style style:name="P2" style:family="paragraph" style:parent-style-name="Footer">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
<style:text-properties officeooo:paragraph-rsid="00224f9c"/>
</style:style>
<style:style style:name="P3" style:family="paragraph" style:parent-style-name="Header">
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false"/>
<style:text-properties fo:font-size="12pt" officeooo:paragraph-rsid="00339d20" style:font-size-asian="12pt" style:font-size-complex="12pt"/>
</style:style>
<style:style style:name="P4" style:family="paragraph" style:parent-style-name="Frame_20_contents">
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false"/>
</style:style>
<style:style style:name="P5" style:family="paragraph" style:parent-style-name="Footer">
<style:paragraph-properties fo:text-align="end" style:justify-single-word="false"/>
</style:style>
<style:style style:name="P6" style:family="paragraph" style:parent-style-name="Header">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
<style:text-properties officeooo:paragraph-rsid="00224f9c"/>
</style:style>
<style:style style:name="P7" style:family="paragraph" style:parent-style-name="Footer">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
<style:text-properties officeooo:paragraph-rsid="00224f9c"/>
</style:style>
<style:style style:name="P8" style:family="paragraph" style:parent-style-name="Footer">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
<style:text-properties officeooo:paragraph-rsid="002fecfa"/>
</style:style>
<style:style style:name="P9" style:family="paragraph" style:parent-style-name="Header">
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false"/>
<style:text-properties fo:font-size="12pt" officeooo:paragraph-rsid="002fecfa" style:font-size-asian="12pt" style:font-size-complex="12pt"/>
</style:style>
<style:style style:name="P10" style:family="paragraph" style:parent-style-name="Header">
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false"/>
<style:text-properties fo:font-size="12pt" officeooo:paragraph-rsid="00224f9c" style:font-size-asian="12pt" style:font-size-complex="12pt"/>
</style:style>
<style:style style:name="P11" style:family="paragraph" style:parent-style-name="Frame_20_contents">
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false"/>
</style:style>
<style:style style:name="P12" style:family="paragraph" style:parent-style-name="Footer">
<style:paragraph-properties fo:text-align="end" style:justify-single-word="false"/>
</style:style>
<style:style style:name="P13" style:family="paragraph" style:parent-style-name="Text_20_body">
<style:text-properties style:font-name="Liberation Serif"/>
</style:style>
<style:style style:name="P14" style:family="paragraph" style:parent-style-name="Table_20_Heading">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
<style:text-properties style:font-name="Liberation Serif"/>
</style:style>
<style:style style:name="P15" style:family="paragraph" style:parent-style-name="Table_20_Contents">
<style:text-properties style:font-name="Liberation Serif"/>
</style:style>
<style:style style:name="P16" style:family="paragraph" style:parent-style-name="Table_20_Contents">
<style:paragraph-properties fo:text-align="end" style:justify-single-word="false"/>
<style:text-properties style:font-name="Liberation Serif"/>
</style:style>
<style:style style:name="P17" style:family="paragraph" style:parent-style-name="Table_20_Contents">
<style:text-properties style:font-name="Liberation Serif" officeooo:paragraph-rsid="0025e811"/>
</style:style>
<style:style style:name="P18" style:family="paragraph" style:parent-style-name="Standard">
<style:paragraph-properties fo:break-before="page"/>
</style:style>
<style:style style:name="P19" style:family="paragraph" style:parent-style-name="Standard">
<style:paragraph-properties fo:margin-left="11.28cm" fo:margin-right="0cm" fo:text-indent="0cm" style:auto-text-indent="false"/>
<style:text-properties style:font-name="Liberation Sans"/>
</style:style>
<style:style style:name="P20" style:family="paragraph" style:parent-style-name="Standard" style:master-page-name="">
<style:paragraph-properties fo:margin-left="11.28cm" fo:margin-right="0cm" fo:text-indent="0cm" style:auto-text-indent="false" style:page-number="auto" fo:break-before="page"/>
<style:text-properties style:font-name="Liberation Sans"/>
</style:style>
<style:style style:name="P21" style:family="paragraph" style:parent-style-name="Standard">
<style:paragraph-properties fo:margin-left="11.28cm" fo:margin-right="0cm" fo:text-indent="0cm" style:auto-text-indent="false"/>
<style:text-properties style:font-name="Liberation Sans" officeooo:paragraph-rsid="0028d873"/>
</style:style>
<style:style style:name="P22" style:family="paragraph" style:parent-style-name="Standard" style:master-page-name="">
<style:paragraph-properties fo:margin-left="11.28cm" fo:margin-right="0cm" fo:text-indent="0cm" style:auto-text-indent="false" style:page-number="auto" fo:break-before="page"/>
<style:text-properties style:font-name="Liberation Sans" officeooo:paragraph-rsid="0028d873"/>
</style:style>
<style:style style:name="P23" style:family="paragraph" style:parent-style-name="Standard" style:master-page-name="">
<style:paragraph-properties fo:margin-left="0cm" fo:margin-right="0cm" fo:text-indent="0cm" style:auto-text-indent="false" style:page-number="auto" fo:break-before="page"/>
<style:text-properties style:font-name="Liberation Sans" fo:font-size="6pt" officeooo:paragraph-rsid="0028d873" style:font-size-asian="5.25pt" style:font-size-complex="6pt"/>
</style:style>
<style:style style:name="P24" style:family="paragraph" style:parent-style-name="Heading_20_1">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
<style:text-properties style:text-underline-style="solid" style:text-underline-width="auto" style:text-underline-color="font-color"/>
</style:style>
<style:style style:name="P25" style:family="paragraph" style:parent-style-name="Heading_20_1">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
<style:text-properties style:text-underline-style="solid" style:text-underline-width="auto" style:text-underline-color="font-color" officeooo:paragraph-rsid="002e5ea4"/>
</style:style>
<style:style style:name="P26" style:family="paragraph" style:parent-style-name="Standard">
<style:text-properties officeooo:paragraph-rsid="001d64ef"/>
</style:style>
<style:style style:name="P27" style:family="paragraph" style:parent-style-name="Standard">
<style:text-properties officeooo:paragraph-rsid="002e5ea4"/>
</style:style>
<style:style style:name="P28" style:family="paragraph" style:parent-style-name="Standard">
<style:text-properties officeooo:paragraph-rsid="002f19d1"/>
</style:style>
<style:style style:name="P29" style:family="paragraph" style:parent-style-name="Standard">
<style:text-properties officeooo:rsid="002f19d1" officeooo:paragraph-rsid="002f19d1"/>
</style:style>
<style:style style:name="P30" style:family="paragraph" style:parent-style-name="Standard">
<style:text-properties fo:font-weight="normal" officeooo:paragraph-rsid="002f19d1" style:font-weight-asian="normal" style:font-weight-complex="normal"/>
</style:style>
<style:style style:name="P31" style:family="paragraph" style:parent-style-name="Frame_20_contents">
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false"/>
</style:style>
<style:style style:name="P32" style:family="paragraph" style:parent-style-name="Header">
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false"/>
<style:text-properties fo:font-size="12pt" officeooo:paragraph-rsid="00339d20" style:font-size-asian="12pt" style:font-size-complex="12pt"/>
</style:style>
<style:style style:name="T1" style:family="text">
<style:text-properties fo:font-weight="bold" style:font-weight-asian="bold" style:font-weight-complex="bold"/>
</style:style>
<style:style style:name="T2" style:family="text">
<style:text-properties fo:font-weight="normal" style:font-weight-asian="normal" style:font-weight-complex="normal"/>
</style:style>
<style:style style:name="T3" style:family="text">
<style:text-properties officeooo:rsid="002e5ea4"/>
</style:style>
<style:style style:name="fr1" style:family="graphic" style:parent-style-name="Frame">
<style:graphic-properties fo:margin-left="0cm" fo:margin-right="0cm" fo:margin-top="0cm" fo:margin-bottom="0cm" style:wrap="parallel" style:number-wrapped-paragraphs="no-limit" style:vertical-pos="middle" style:vertical-rel="baseline" style:horizontal-pos="left" style:horizontal-rel="paragraph" draw:opacity="100%" fo:padding="0cm" fo:border="none" draw:wrap-influence-on-position="once-concurrent" loext:allow-overlap="true">
<style:columns fo:column-count="1" fo:column-gap="0cm"/>
</style:graphic-properties>
</style:style>
<style:page-layout style:name="pm1">
<style:page-layout-properties fo:page-width="20.999cm" fo:page-height="29.699cm" style:num-format="1" style:print-orientation="portrait" fo:margin-top="2cm" fo:margin-bottom="2cm" fo:margin-left="2cm" fo:margin-right="2cm" style:writing-mode="lr-tb" style:layout-grid-color="#c0c0c0" style:layout-grid-lines="44" style:layout-grid-base-height="0.55cm" style:layout-grid-ruby-height="0cm" style:layout-grid-mode="none" style:layout-grid-ruby-below="false" style:layout-grid-print="true" style:layout-grid-display="true" style:footnote-max-height="0cm" loext:margin-gutter="0cm">
<style:footnote-sep style:width="0.018cm" style:distance-before-sep="0.101cm" style:distance-after-sep="0.101cm" style:line-style="none" style:adjustment="left" style:rel-width="25%" style:color="#000000"/>
</style:page-layout-properties>
<style:header-style>
<style:header-footer-properties fo:min-height="0cm" fo:margin-left="0cm" fo:margin-right="0cm" fo:margin-bottom="0.499cm"/>
</style:header-style>
<style:footer-style>
<style:header-footer-properties fo:min-height="0cm" fo:margin-left="0cm" fo:margin-right="0cm" fo:margin-top="0.499cm"/>
</style:footer-style>
</style:page-layout>
<style:style style:name="dp1" style:family="drawing-page">
<style:drawing-page-properties draw:background-size="full"/>
</style:style>
</office:automatic-styles>
<office:master-styles>
<style:master-page style:name="Standard" style:page-layout-name="pm1" draw:style-name="dp1">
<style:header>
<text:p text:style-name="P1"><text:placeholder text:placeholder-type="text">&lt;if test=&quot;company and company.header&quot;&gt;</text:placeholder></text:p>
<text:p text:style-name="P1"><text:placeholder text:placeholder-type="text">&lt;for each=&quot;line in company.header_used.split(&apos;\n&apos;)&quot;&gt;</text:placeholder></text:p>
<text:p text:style-name="P1"><text:placeholder text:placeholder-type="text">&lt;line&gt;</text:placeholder></text:p>
<text:p text:style-name="P1"><text:placeholder text:placeholder-type="text">&lt;/for&gt;</text:placeholder></text:p>
<text:p text:style-name="P2"><text:placeholder text:placeholder-type="text">&lt;/if&gt;</text:placeholder></text:p>
<text:p text:style-name="P3"><text:placeholder text:placeholder-type="text">&lt;choose&gt;</text:placeholder></text:p>
<text:p text:style-name="P3"><text:placeholder text:placeholder-type="text">&lt;when test=&quot;company and company.logo&quot;&gt;</text:placeholder></text:p>
<text:p text:style-name="P3"><draw:frame draw:style-name="fr1" draw:name="image:company.get_logo_cm(7, 3.5)" text:anchor-type="as-char" svg:width="7.001cm" draw:z-index="2">
<draw:text-box fo:min-height="3cm">
<text:p text:style-name="P4"/>
</draw:text-box>
</draw:frame></text:p>
<text:p text:style-name="P3"><text:placeholder text:placeholder-type="text">&lt;/when&gt;</text:placeholder></text:p>
<text:p text:style-name="P3"><text:placeholder text:placeholder-type="text">&lt;when test=&quot;company&quot;&gt;</text:placeholder></text:p>
<text:p text:style-name="P3"><text:placeholder text:placeholder-type="text">&lt;company.rec_name&gt;</text:placeholder></text:p>
<text:p text:style-name="P3"><text:placeholder text:placeholder-type="text">&lt;/when&gt;</text:placeholder></text:p>
<text:p text:style-name="P3"><text:placeholder text:placeholder-type="text">&lt;/choose&gt;</text:placeholder></text:p>
</style:header>
<style:footer>
<text:p text:style-name="P2"><text:placeholder text:placeholder-type="text">&lt;if test=&quot;company and company.footer&quot;&gt;</text:placeholder></text:p>
<text:p text:style-name="P2"><text:placeholder text:placeholder-type="text">&lt;for each=&quot;line in company.footer_used.split(&apos;\n&apos;)&quot;&gt;</text:placeholder></text:p>
<text:p text:style-name="P2"><text:placeholder text:placeholder-type="text">&lt;line&gt;</text:placeholder></text:p>
<text:p text:style-name="P2"><text:placeholder text:placeholder-type="text">&lt;/for&gt;</text:placeholder></text:p>
<text:p text:style-name="P2"><text:placeholder text:placeholder-type="text">&lt;/if&gt;</text:placeholder></text:p>
<text:p text:style-name="P5"><text:page-number text:select-page="current">3</text:page-number>/<text:page-count>3</text:page-count></text:p>
</style:footer>
</style:master-page>
</office:master-styles>
<office:body>
<office:text text:use-soft-page-breaks="true">
<office:forms form:automatic-focus="false" form:apply-design-mode="false"/>
<text:sequence-decls>
<text:sequence-decl text:display-outline-level="0" text:name="Illustration"/>
<text:sequence-decl text:display-outline-level="0" text:name="Table"/>
<text:sequence-decl text:display-outline-level="0" text:name="Text"/>
<text:sequence-decl text:display-outline-level="0" text:name="Drawing"/>
<text:sequence-decl text:display-outline-level="0" text:name="Figure"/>
</text:sequence-decls>
<text:p text:style-name="P18"><text:placeholder text:placeholder-type="text">&lt;for each=&quot;shipment in records&quot;&gt;</text:placeholder></text:p>
<text:p text:style-name="P23"/>
<text:p text:style-name="P21"><text:placeholder text:placeholder-type="text">&lt;replace text:p=&quot;set_lang(shipment.delivery_address.party.lang)&quot;&gt;</text:placeholder></text:p>
<text:p text:style-name="P21"><text:placeholder text:placeholder-type="text">&lt;replace text:p=&quot;shipment.set_lang(shipment.delivery_address.party.lang)&quot;&gt;</text:placeholder></text:p>
<text:p text:style-name="P21"><text:placeholder text:placeholder-type="text">&lt;shipment.customer.full_name&gt;</text:placeholder></text:p>
<text:p text:style-name="P21"><text:placeholder text:placeholder-type="text">&lt;for each=&quot;line in shipment.delivery_address.full_address.split(&apos;\n&apos;)&quot;&gt;</text:placeholder></text:p>
<text:p text:style-name="P19"><text:placeholder text:placeholder-type="text">&lt;line&gt;</text:placeholder></text:p>
<text:p text:style-name="P19"><text:placeholder text:placeholder-type="text">&lt;/for&gt;</text:placeholder></text:p>
<text:p text:style-name="Standard"><text:placeholder text:placeholder-type="text">&lt;choose&gt;</text:placeholder></text:p>
<text:p text:style-name="Standard"><text:placeholder text:placeholder-type="text">&lt;when test=&quot;shipment.state in [&apos;packed&apos;, &apos;done&apos;]&quot;&gt;</text:placeholder></text:p>
<text:p text:style-name="P24">Delivery Note No: <text:span text:style-name="T3"><text:placeholder text:placeholder-type="text">&lt;shipment.number&gt;</text:placeholder></text:span></text:p>
<text:p text:style-name="Standard"><text:placeholder text:placeholder-type="text">&lt;/when&gt;</text:placeholder></text:p>
<text:p text:style-name="Standard"><text:placeholder text:placeholder-type="text">&lt;when test=&quot;shipment.state == &apos;cancelled&apos;&quot;&gt;</text:placeholder></text:p>
<text:p text:style-name="P25"><text:span text:style-name="T3">Cancelled </text:span>Delivery Note</text:p>
<text:p text:style-name="Standard"><text:placeholder text:placeholder-type="text">&lt;/when&gt;</text:placeholder></text:p>
<text:p text:style-name="P27"><text:placeholder text:placeholder-type="text">&lt;otherwise&gt;</text:placeholder></text:p>
<text:p text:style-name="P25"><text:span text:style-name="T3">Draft </text:span>Delivery Note</text:p>
<text:p text:style-name="P27"><text:placeholder text:placeholder-type="text">&lt;/otherwise&gt;</text:placeholder></text:p>
<text:p text:style-name="Standard"><text:soft-page-break/><text:placeholder text:placeholder-type="text">&lt;/choose&gt;</text:placeholder></text:p>
<text:p text:style-name="Standard"><text:placeholder text:placeholder-type="text">&lt;if test=&quot;shipment.customer.code&quot;&gt;</text:placeholder></text:p>
<text:p text:style-name="P28"><text:span text:style-name="T1">Customer Code: </text:span><text:span text:style-name="T2"><text:placeholder text:placeholder-type="text">&lt;shipment.customer.code&gt;</text:placeholder></text:span></text:p>
<text:p text:style-name="P30"><text:placeholder text:placeholder-type="text">&lt;/if&gt;</text:placeholder></text:p>
<text:p text:style-name="P28"><text:span text:style-name="T1">Reference: </text:span><text:span text:style-name="T2"><text:placeholder text:placeholder-type="text">&lt;shipment.origins or &apos;&apos;&gt;</text:placeholder></text:span><text:span text:style-name="T2"><text:placeholder text:placeholder-type="text">&lt;&apos;, &apos; if (shipment.origins and shipment.reference) else &apos;&apos;&gt;</text:placeholder></text:span><text:span text:style-name="T2"><text:placeholder text:placeholder-type="text">&lt;shipment.reference or &apos;&apos;&gt;</text:placeholder></text:span></text:p>
<text:p text:style-name="P30"><text:placeholder text:placeholder-type="text">&lt;if test=&quot;shipment.warehouse.address and shipment.warehouse.address.country&quot;&gt;</text:placeholder></text:p>
<text:p text:style-name="P29"><text:span text:style-name="T1">Location:</text:span><text:span text:style-name="T2"> </text:span><text:span text:style-name="T2"><text:placeholder text:placeholder-type="text">&lt;shipment.warehouse.address.country.name&gt;</text:placeholder></text:span></text:p>
<text:p text:style-name="P30"><text:placeholder text:placeholder-type="text">&lt;/if&gt;</text:placeholder></text:p>
<text:p text:style-name="P28"><text:span text:style-name="T1">Date: </text:span><text:span text:style-name="T2"><text:placeholder text:placeholder-type="text">&lt;format_date(shipment.effective_date or datetime.date.today(), shipment.delivery_address.party.lang)&gt;</text:placeholder></text:span></text:p>
<table:table table:name="Table1" table:style-name="Table1">
<table:table-column table:style-name="Table1.A"/>
<table:table-column table:style-name="Table1.B"/>
<table:table-header-rows>
<table:table-row>
<table:table-cell table:style-name="Table1.A1" office:value-type="string">
<text:p text:style-name="P14">Product</text:p>
</table:table-cell>
<table:table-cell table:style-name="Table1.B1" office:value-type="string">
<text:p text:style-name="P14">Quantity</text:p>
</table:table-cell>
</table:table-row>
</table:table-header-rows>
<table:table-row>
<table:table-cell table:style-name="Table1.A2" table:number-columns-spanned="2" office:value-type="string">
<text:p text:style-name="P15"><text:placeholder text:placeholder-type="text">&lt;for each=&quot;move in moves(shipment)&quot;&gt;</text:placeholder></text:p>
</table:table-cell>
<table:covered-table-cell/>
</table:table-row>
<table:table-row>
<table:table-cell table:style-name="Table1.A3" office:value-type="string">
<text:p text:style-name="P17"><text:placeholder text:placeholder-type="text">&lt;move.product_name&gt;</text:placeholder></text:p>
</table:table-cell>
<table:table-cell table:style-name="Table1.B3" office:value-type="string">
<text:p text:style-name="P16"><text:placeholder text:placeholder-type="text">&lt;format_number_symbol(move.quantity, shipment.delivery_address.party.lang, move.unit, digits=move.unit.digits)&gt;</text:placeholder></text:p>
</table:table-cell>
</table:table-row>
<table:table-row>
<table:table-cell table:style-name="Table1.A2" table:number-columns-spanned="2" office:value-type="string">
<text:p text:style-name="P15"><text:placeholder text:placeholder-type="text">&lt;/for&gt;</text:placeholder></text:p>
</table:table-cell>
<table:covered-table-cell/>
</table:table-row>
</table:table>
<text:p text:style-name="P13"><text:placeholder text:placeholder-type="text">&lt;/for&gt;</text:placeholder></text:p>
</office:text>
</office:body>
</office:document>

View File

@@ -0,0 +1,48 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from trytond.exceptions import UserError, UserWarning
from trytond.model.exceptions import ValidationError
class AssignError(UserError):
pass
class MoveValidationError(ValidationError):
pass
class LocationValidationError(ValidationError):
pass
class PeriodCloseError(UserError):
pass
class InventoryValidationError(ValidationError):
pass
class InventoryCountWarning(UserWarning):
pass
class MoveOriginWarning(UserWarning):
pass
class MoveFutureWarning(UserWarning):
pass
class ProductCostPriceError(ValidationError):
pass
class ProductStockWarning(UserWarning):
pass
class ShipmentCheckQuantityWarning(UserWarning):
pass

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M4 8h3V4h14c1.1 0 2 .9 2 2v11h-2c0 1.66-1.34 3-3 3s-3-1.34-3-3H9c0 1.66-1.34 3-3 3s-3-1.34-3-3H1v-5zm14 10.5c.83 0 1.5-.67 1.5-1.5s-.67-1.5-1.5-1.5-1.5.67-1.5 1.5.67 1.5 1.5 1.5zm-13.5-9L2.54 12H7V9.5zm1.5 9c.83 0 1.5-.67 1.5-1.5s-.67-1.5-1.5-1.5-1.5.67-1.5 1.5.67 1.5 1.5 1.5z"/></svg>

After

Width:  |  Height:  |  Size: 416 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M20 8h-3V4H3c-1.1 0-2 .9-2 2v11h2c0 1.66 1.34 3 3 3s3-1.34 3-3h6c0 1.66 1.34 3 3 3s3-1.34 3-3h2v-5l-3-4zM6 18.5c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zm13.5-9l1.96 2.5H17V9.5h2.5zm-1.5 9c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5z"/></svg>

After

Width:  |  Height:  |  Size: 426 B

View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path d="M0 0h24v24H0z" fill="none"/>
<path d="M19 7V4H5v3H2v13h8v-4h4v4h8V7h-3zm-8 3H9v1h2v1H8V9h2V8H8V7h3v3zm5 2h-1v-2h-2V7h1v2h1V7h1v5z"/>
</svg>

After

Width:  |  Height:  |  Size: 241 B

View File

@@ -0,0 +1,610 @@
<?xml version="1.0" encoding="UTF-8"?>
<office:document xmlns:css3t="http://www.w3.org/TR/css3-text/" xmlns:grddl="http://www.w3.org/2003/g/data-view#" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xforms="http://www.w3.org/2002/xforms" xmlns:dom="http://www.w3.org/2001/xml-events" xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" xmlns:ooow="http://openoffice.org/2004/writer" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:drawooo="http://openoffice.org/2010/draw" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" xmlns:tableooo="http://openoffice.org/2009/table" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:rpt="http://openoffice.org/2005/report" xmlns:formx="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:officeooo="http://openoffice.org/2009/office" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text">
<office:meta><meta:generator>LibreOffice/7.6.4.1$Linux_X86_64 LibreOffice_project/60$Build-1</meta:generator><meta:initial-creator>Bertrand Chenal</meta:initial-creator><meta:creation-date>2009-03-26T13:54:48</meta:creation-date><dc:date>2024-04-19T23:16:19.369202166</dc:date><meta:editing-cycles>66</meta:editing-cycles><meta:editing-duration>PT7H29M8S</meta:editing-duration><meta:document-statistic meta:table-count="1" meta:image-count="0" meta:object-count="0" meta:page-count="3" meta:paragraph-count="41" meta:word-count="91" meta:character-count="1078" meta:non-whitespace-character-count="1027"/><meta:user-defined meta:name="Info 1"/><meta:user-defined meta:name="Info 2"/><meta:user-defined meta:name="Info 3"/><meta:user-defined meta:name="Info 4"/></office:meta>
<office:settings>
<config:config-item-set config:name="ooo:view-settings">
<config:config-item config:name="ViewAreaTop" config:type="long">0</config:config-item>
<config:config-item config:name="ViewAreaLeft" config:type="long">0</config:config-item>
<config:config-item config:name="ViewAreaWidth" config:type="long">25269</config:config-item>
<config:config-item config:name="ViewAreaHeight" config:type="long">23973</config:config-item>
<config:config-item config:name="ShowRedlineChanges" config:type="boolean">true</config:config-item>
<config:config-item config:name="InBrowseMode" config:type="boolean">false</config:config-item>
<config:config-item-map-indexed config:name="Views">
<config:config-item-map-entry>
<config:config-item config:name="ViewId" config:type="string">view2</config:config-item>
<config:config-item config:name="ViewLeft" config:type="long">5955</config:config-item>
<config:config-item config:name="ViewTop" config:type="long">10248</config:config-item>
<config:config-item config:name="VisibleLeft" config:type="long">0</config:config-item>
<config:config-item config:name="VisibleTop" config:type="long">0</config:config-item>
<config:config-item config:name="VisibleRight" config:type="long">25268</config:config-item>
<config:config-item config:name="VisibleBottom" config:type="long">23971</config:config-item>
<config:config-item config:name="ZoomType" config:type="short">0</config:config-item>
<config:config-item config:name="ViewLayoutColumns" config:type="short">0</config:config-item>
<config:config-item config:name="ViewLayoutBookMode" config:type="boolean">false</config:config-item>
<config:config-item config:name="ZoomFactor" config:type="short">100</config:config-item>
<config:config-item config:name="IsSelectedFrame" config:type="boolean">false</config:config-item>
<config:config-item config:name="KeepRatio" config:type="boolean">false</config:config-item>
<config:config-item config:name="AnchoredTextOverflowLegacy" config:type="boolean">false</config:config-item>
<config:config-item config:name="LegacySingleLineFontwork" config:type="boolean">false</config:config-item>
<config:config-item config:name="ConnectorUseSnapRect" config:type="boolean">false</config:config-item>
<config:config-item config:name="IgnoreBreakAfterMultilineField" config:type="boolean">false</config:config-item>
</config:config-item-map-entry>
</config:config-item-map-indexed>
</config:config-item-set>
<config:config-item-set config:name="ooo:configuration-settings">
<config:config-item config:name="PrintRightPages" config:type="boolean">true</config:config-item>
<config:config-item config:name="PrintProspectRTL" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintLeftPages" config:type="boolean">true</config:config-item>
<config:config-item config:name="PrintPaperFromSetup" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintControls" config:type="boolean">true</config:config-item>
<config:config-item config:name="PrintProspect" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintBlackFonts" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintAnnotationMode" config:type="short">0</config:config-item>
<config:config-item config:name="PrintEmptyPages" config:type="boolean">true</config:config-item>
<config:config-item config:name="PrintSingleJobs" config:type="boolean">false</config:config-item>
<config:config-item config:name="AutoFirstLineIndentDisregardLineSpace" config:type="boolean">false</config:config-item>
<config:config-item config:name="HeaderSpacingBelowLastPara" config:type="boolean">false</config:config-item>
<config:config-item config:name="ProtectBookmarks" config:type="boolean">false</config:config-item>
<config:config-item config:name="ContinuousEndnotes" config:type="boolean">false</config:config-item>
<config:config-item config:name="DisableOffPagePositioning" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintTables" config:type="boolean">true</config:config-item>
<config:config-item config:name="SubtractFlysAnchoredAtFlys" config:type="boolean">true</config:config-item>
<config:config-item config:name="ApplyParagraphMarkFormatToNumbering" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintFaxName" config:type="string"/>
<config:config-item config:name="SurroundTextWrapSmall" config:type="boolean">false</config:config-item>
<config:config-item config:name="TreatSingleColumnBreakAsPageBreak" config:type="boolean">false</config:config-item>
<config:config-item config:name="PropLineSpacingShrinksFirstLine" config:type="boolean">false</config:config-item>
<config:config-item config:name="TabOverSpacing" config:type="boolean">false</config:config-item>
<config:config-item config:name="TabOverMargin" config:type="boolean">false</config:config-item>
<config:config-item config:name="EmbedComplexScriptFonts" config:type="boolean">true</config:config-item>
<config:config-item config:name="EmbedLatinScriptFonts" config:type="boolean">true</config:config-item>
<config:config-item config:name="EmbedOnlyUsedFonts" config:type="boolean">false</config:config-item>
<config:config-item config:name="EmbedFonts" config:type="boolean">false</config:config-item>
<config:config-item config:name="ClippedPictures" config:type="boolean">false</config:config-item>
<config:config-item config:name="FrameAutowidthWithMorePara" config:type="boolean">false</config:config-item>
<config:config-item config:name="FloattableNomargins" config:type="boolean">false</config:config-item>
<config:config-item config:name="UnbreakableNumberings" config:type="boolean">false</config:config-item>
<config:config-item config:name="AllowPrintJobCancel" config:type="boolean">true</config:config-item>
<config:config-item config:name="UseFormerObjectPositioning" config:type="boolean">false</config:config-item>
<config:config-item config:name="UseOldNumbering" config:type="boolean">false</config:config-item>
<config:config-item config:name="RsidRoot" config:type="int">1178252</config:config-item>
<config:config-item config:name="PrinterPaperFromSetup" config:type="boolean">false</config:config-item>
<config:config-item config:name="CurrentDatabaseDataSource" config:type="string"/>
<config:config-item config:name="UpdateFromTemplate" config:type="boolean">true</config:config-item>
<config:config-item config:name="AddFrameOffsets" config:type="boolean">false</config:config-item>
<config:config-item config:name="LoadReadonly" config:type="boolean">false</config:config-item>
<config:config-item config:name="Rsid" config:type="int">2325796</config:config-item>
<config:config-item config:name="FootnoteInColumnToPageEnd" config:type="boolean">true</config:config-item>
<config:config-item config:name="ProtectFields" config:type="boolean">false</config:config-item>
<config:config-item config:name="SaveGlobalDocumentLinks" config:type="boolean">false</config:config-item>
<config:config-item config:name="ClipAsCharacterAnchoredWriterFlyFrames" config:type="boolean">false</config:config-item>
<config:config-item config:name="LinkUpdateMode" config:type="short">1</config:config-item>
<config:config-item config:name="AddExternalLeading" config:type="boolean">true</config:config-item>
<config:config-item config:name="PrintGraphics" config:type="boolean">true</config:config-item>
<config:config-item config:name="EmbedSystemFonts" config:type="boolean">false</config:config-item>
<config:config-item config:name="IsLabelDocument" config:type="boolean">false</config:config-item>
<config:config-item config:name="AddParaLineSpacingToTableCells" config:type="boolean">false</config:config-item>
<config:config-item config:name="UseFormerTextWrapping" config:type="boolean">false</config:config-item>
<config:config-item config:name="HyphenateURLs" config:type="boolean">true</config:config-item>
<config:config-item config:name="AddParaTableSpacingAtStart" config:type="boolean">true</config:config-item>
<config:config-item config:name="TabsRelativeToIndent" config:type="boolean">true</config:config-item>
<config:config-item config:name="FieldAutoUpdate" config:type="boolean">true</config:config-item>
<config:config-item config:name="SaveVersionOnClose" config:type="boolean">false</config:config-item>
<config:config-item config:name="ChartAutoUpdate" config:type="boolean">true</config:config-item>
<config:config-item config:name="ImagePreferredDPI" config:type="int">0</config:config-item>
<config:config-item config:name="PrinterSetup" config:type="base64Binary"/>
<config:config-item config:name="SmallCapsPercentage66" config:type="boolean">true</config:config-item>
<config:config-item config:name="AlignTabStopPosition" config:type="boolean">true</config:config-item>
<config:config-item config:name="DropCapPunctuation" config:type="boolean">false</config:config-item>
<config:config-item config:name="MathBaselineAlignment" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrinterName" config:type="string"/>
<config:config-item config:name="CharacterCompressionType" config:type="short">0</config:config-item>
<config:config-item config:name="AddParaTableSpacing" config:type="boolean">true</config:config-item>
<config:config-item config:name="DoNotJustifyLinesWithManualBreak" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintHiddenText" config:type="boolean">false</config:config-item>
<config:config-item config:name="IsKernAsianPunctuation" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrinterIndependentLayout" config:type="string">high-resolution</config:config-item>
<config:config-item config:name="TabOverflow" config:type="boolean">false</config:config-item>
<config:config-item config:name="AddParaSpacingToTableCells" config:type="boolean">true</config:config-item>
<config:config-item config:name="AddVerticalFrameOffsets" config:type="boolean">false</config:config-item>
<config:config-item config:name="TabAtLeftIndentForParagraphsInList" config:type="boolean">false</config:config-item>
<config:config-item config:name="ApplyUserData" config:type="boolean">true</config:config-item>
<config:config-item config:name="MsWordCompMinLineHeightByFly" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintTextPlaceholder" config:type="boolean">false</config:config-item>
<config:config-item config:name="IgnoreFirstLineIndentInNumbering" config:type="boolean">false</config:config-item>
<config:config-item config:name="UseFormerLineSpacing" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintPageBackground" config:type="boolean">true</config:config-item>
<config:config-item config:name="RedlineProtectionKey" config:type="base64Binary"/>
<config:config-item config:name="EmbedAsianScriptFonts" config:type="boolean">true</config:config-item>
<config:config-item config:name="BackgroundParaOverDrawings" config:type="boolean">false</config:config-item>
<config:config-item config:name="SaveThumbnail" config:type="boolean">true</config:config-item>
<config:config-item config:name="ConsiderTextWrapOnObjPos" config:type="boolean">false</config:config-item>
<config:config-item config:name="EmbeddedDatabaseName" config:type="string"/>
<config:config-item config:name="ProtectForm" config:type="boolean">false</config:config-item>
<config:config-item config:name="DoNotResetParaAttrsForNumFont" config:type="boolean">false</config:config-item>
<config:config-item config:name="MsWordCompTrailingBlanks" config:type="boolean">false</config:config-item>
<config:config-item config:name="EmptyDbFieldHidesPara" config:type="boolean">false</config:config-item>
<config:config-item config:name="TableRowKeep" config:type="boolean">false</config:config-item>
<config:config-item config:name="NoNumberingShowFollowBy" config:type="boolean">false</config:config-item>
<config:config-item config:name="InvertBorderSpacing" config:type="boolean">false</config:config-item>
<config:config-item config:name="IgnoreTabsAndBlanksForLineCalculation" config:type="boolean">false</config:config-item>
<config:config-item config:name="DoNotCaptureDrawObjsOnPage" config:type="boolean">false</config:config-item>
<config:config-item config:name="GutterAtTop" config:type="boolean">false</config:config-item>
<config:config-item config:name="StylesNoDefault" config:type="boolean">false</config:config-item>
<config:config-item config:name="UnxForceZeroExtLeading" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintReversed" config:type="boolean">false</config:config-item>
<config:config-item config:name="UseOldPrinterMetrics" config:type="boolean">false</config:config-item>
<config:config-item config:name="CurrentDatabaseCommandType" config:type="int">0</config:config-item>
<config:config-item config:name="PrintDrawings" config:type="boolean">true</config:config-item>
<config:config-item config:name="OutlineLevelYieldsNumbering" config:type="boolean">false</config:config-item>
<config:config-item config:name="CurrentDatabaseCommand" config:type="string"/>
<config:config-item config:name="CollapseEmptyCellPara" config:type="boolean">true</config:config-item>
</config:config-item-set>
</office:settings>
<office:scripts>
<office:script script:language="ooo:Basic">
<ooo:libraries xmlns:ooo="http://openoffice.org/2004/office" xmlns:xlink="http://www.w3.org/1999/xlink"/>
</office:script>
</office:scripts>
<office:font-face-decls>
<style:font-face style:name="Andale Sans UI" svg:font-family="&apos;Andale Sans UI&apos;" style:font-family-generic="system" style:font-pitch="variable"/>
<style:font-face style:name="DejaVu Sans" svg:font-family="&apos;DejaVu Sans&apos;" style:font-family-generic="system" style:font-pitch="variable"/>
<style:font-face style:name="Liberation Sans" svg:font-family="&apos;Liberation Sans&apos;" style:font-adornments="Regular" style:font-family-generic="swiss" style:font-pitch="variable"/>
<style:font-face style:name="Liberation Serif" svg:font-family="&apos;Liberation Serif&apos;" style:font-family-generic="roman" style:font-pitch="variable"/>
<style:font-face style:name="Liberation Serif1" svg:font-family="&apos;Liberation Serif&apos;" style:font-adornments="Bold" style:font-family-generic="roman" style:font-pitch="variable"/>
<style:font-face style:name="Liberation Serif2" svg:font-family="&apos;Liberation Serif&apos;" style:font-adornments="Regular" style:font-family-generic="roman" style:font-pitch="variable"/>
<style:font-face style:name="StarSymbol" svg:font-family="StarSymbol"/>
<style:font-face style:name="Thorndale AMT" svg:font-family="&apos;Thorndale AMT&apos;" style:font-family-generic="roman" style:font-pitch="variable"/>
</office:font-face-decls>
<office:styles>
<style:default-style style:family="graphic">
<style:graphic-properties svg:stroke-color="#000000" draw:fill-color="#99ccff" fo:wrap-option="no-wrap" draw:shadow-offset-x="0.3cm" draw:shadow-offset-y="0.3cm" draw:start-line-spacing-horizontal="0.283cm" draw:start-line-spacing-vertical="0.283cm" draw:end-line-spacing-horizontal="0.283cm" draw:end-line-spacing-vertical="0.283cm" style:writing-mode="lr-tb" style:flow-with-text="false"/>
<style:paragraph-properties style:text-autospace="ideograph-alpha" style:line-break="strict" loext:tab-stop-distance="0cm" style:font-independent-line-spacing="false">
<style:tab-stops/>
</style:paragraph-properties>
<style:text-properties style:use-window-font-color="true" loext:opacity="0%" style:font-name="Thorndale AMT" fo:font-size="12pt" fo:language="en" fo:country="US" style:letter-kerning="true" style:font-name-asian="Andale Sans UI" style:font-size-asian="10.5pt" style:language-asian="zxx" style:country-asian="none" style:font-name-complex="Andale Sans UI" style:font-size-complex="12pt" style:language-complex="zxx" style:country-complex="none"/>
</style:default-style>
<style:default-style style:family="paragraph">
<style:paragraph-properties fo:hyphenation-ladder-count="no-limit" style:text-autospace="ideograph-alpha" style:punctuation-wrap="hanging" style:line-break="strict" style:tab-stop-distance="1.251cm" style:writing-mode="lr-tb"/>
<style:text-properties style:use-window-font-color="true" loext:opacity="0%" style:font-name="Thorndale AMT" fo:font-size="12pt" fo:language="en" fo:country="US" style:letter-kerning="true" style:font-name-asian="Andale Sans UI" style:font-size-asian="10.5pt" style:language-asian="zxx" style:country-asian="none" style:font-name-complex="Andale Sans UI" style:font-size-complex="12pt" style:language-complex="zxx" style:country-complex="none" fo:hyphenate="false" fo:hyphenation-remain-char-count="2" fo:hyphenation-push-char-count="2" loext:hyphenation-no-caps="false" loext:hyphenation-no-last-word="false" loext:hyphenation-word-char-count="5" loext:hyphenation-zone="no-limit"/>
</style:default-style>
<style:default-style style:family="table">
<style:table-properties table:border-model="collapsing"/>
</style:default-style>
<style:default-style style:family="table-row">
<style:table-row-properties fo:keep-together="auto"/>
</style:default-style>
<style:style style:name="Standard" style:family="paragraph" style:class="text">
<style:text-properties style:font-name="Liberation Sans" fo:font-family="&apos;Liberation Sans&apos;" style:font-style-name="Regular" style:font-family-generic="swiss" style:font-pitch="variable" style:font-size-asian="10.5pt"/>
</style:style>
<style:style style:name="Heading" style:family="paragraph" style:parent-style-name="Standard" style:next-style-name="Text_20_body" style:class="text">
<style:paragraph-properties fo:margin-top="0.423cm" fo:margin-bottom="0.212cm" style:contextual-spacing="false" fo:keep-with-next="always"/>
<style:text-properties style:font-name="Liberation Serif2" fo:font-family="&apos;Liberation Serif&apos;" style:font-style-name="Regular" style:font-family-generic="roman" style:font-pitch="variable" fo:font-size="16pt" style:font-name-asian="DejaVu Sans" style:font-family-asian="&apos;DejaVu Sans&apos;" style:font-family-generic-asian="system" style:font-pitch-asian="variable" style:font-size-asian="14pt" style:font-name-complex="DejaVu Sans" style:font-family-complex="&apos;DejaVu Sans&apos;" style:font-family-generic-complex="system" style:font-pitch-complex="variable" style:font-size-complex="14pt"/>
</style:style>
<style:style style:name="Text_20_body" style:display-name="Text body" style:family="paragraph" style:parent-style-name="Standard" style:class="text">
<style:paragraph-properties fo:margin-top="0cm" fo:margin-bottom="0.212cm" style:contextual-spacing="false"/>
<style:text-properties style:font-name="Liberation Sans" fo:font-family="&apos;Liberation Sans&apos;" style:font-style-name="Regular" style:font-family-generic="swiss" style:font-pitch="variable" style:font-size-asian="10.5pt"/>
</style:style>
<style:style style:name="List" style:family="paragraph" style:parent-style-name="Text_20_body" style:class="list">
<style:text-properties style:font-size-asian="12pt"/>
</style:style>
<style:style style:name="Caption" style:family="paragraph" style:parent-style-name="Standard" style:class="extra">
<style:paragraph-properties fo:margin-top="0.212cm" fo:margin-bottom="0.212cm" style:contextual-spacing="false" text:number-lines="false" text:line-number="0"/>
<style:text-properties fo:font-size="12pt" fo:font-style="italic" style:font-size-asian="12pt" style:font-style-asian="italic" style:font-size-complex="12pt" style:font-style-complex="italic"/>
</style:style>
<style:style style:name="Index" style:family="paragraph" style:parent-style-name="Standard" style:class="index">
<style:paragraph-properties text:number-lines="false" text:line-number="0"/>
<style:text-properties style:font-size-asian="12pt"/>
</style:style>
<style:style style:name="Heading_20_1" style:display-name="Heading 1" style:family="paragraph" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:class="text">
<style:text-properties fo:font-size="16pt" fo:font-weight="bold" style:font-size-asian="115%" style:font-weight-asian="bold" style:font-size-complex="115%" style:font-weight-complex="bold"/>
</style:style>
<style:style style:name="Table_20_Contents" style:display-name="Table Contents" style:family="paragraph" style:parent-style-name="Standard" style:class="extra">
<style:paragraph-properties text:number-lines="false" text:line-number="0"/>
<style:text-properties style:font-size-asian="10.5pt"/>
</style:style>
<style:style style:name="Table_20_Heading" style:display-name="Table Heading" style:family="paragraph" style:parent-style-name="Table_20_Contents" style:class="extra" style:master-page-name="">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false" style:page-number="auto" text:number-lines="false" text:line-number="0"/>
<style:text-properties style:font-name="Liberation Serif1" fo:font-family="&apos;Liberation Serif&apos;" style:font-style-name="Bold" style:font-family-generic="roman" style:font-pitch="variable" fo:font-weight="bold" style:font-size-asian="10.5pt" style:font-weight-asian="bold" style:font-weight-complex="bold"/>
</style:style>
<style:style style:name="Header_20_and_20_Footer" style:display-name="Header and Footer" style:family="paragraph" style:parent-style-name="Standard" style:class="extra">
<style:paragraph-properties text:number-lines="false" text:line-number="0">
<style:tab-stops>
<style:tab-stop style:position="8.5cm" style:type="center"/>
<style:tab-stop style:position="17cm" style:type="right"/>
</style:tab-stops>
</style:paragraph-properties>
</style:style>
<style:style style:name="Header" style:family="paragraph" style:parent-style-name="Standard" style:class="extra">
<style:paragraph-properties text:number-lines="false" text:line-number="0">
<style:tab-stops>
<style:tab-stop style:position="8.795cm" style:type="center"/>
<style:tab-stop style:position="17.59cm" style:type="right"/>
</style:tab-stops>
</style:paragraph-properties>
<style:text-properties fo:font-size="9pt" style:font-size-asian="10.5pt"/>
</style:style>
<style:style style:name="Heading_20_2" style:display-name="Heading 2" style:family="paragraph" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:class="text">
<style:text-properties fo:font-size="14pt" fo:font-style="italic" fo:font-weight="bold" style:font-size-asian="14pt" style:font-style-asian="italic" style:font-weight-asian="bold" style:font-size-complex="14pt" style:font-style-complex="italic" style:font-weight-complex="bold"/>
</style:style>
<style:style style:name="Footer" style:family="paragraph" style:parent-style-name="Standard" style:class="extra">
<style:paragraph-properties text:number-lines="false" text:line-number="0">
<style:tab-stops>
<style:tab-stop style:position="8.795cm" style:type="center"/>
<style:tab-stop style:position="17.59cm" style:type="right"/>
</style:tab-stops>
</style:paragraph-properties>
<style:text-properties fo:font-size="9pt" style:font-size-asian="10.5pt"/>
</style:style>
<style:style style:name="Heading_20_3" style:display-name="Heading 3" style:family="paragraph" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:class="text">
<style:text-properties fo:font-size="14pt" fo:font-weight="bold" style:font-size-asian="14pt" style:font-weight-asian="bold" style:font-size-complex="14pt" style:font-weight-complex="bold"/>
</style:style>
<style:style style:name="Text_20_body_20_indent" style:display-name="Text body indent" style:family="paragraph" style:parent-style-name="Text_20_body" style:class="text">
<style:paragraph-properties fo:margin-left="0.499cm" fo:margin-right="0cm" fo:margin-top="0cm" fo:margin-bottom="0cm" style:contextual-spacing="false" fo:text-indent="0cm" style:auto-text-indent="false"/>
</style:style>
<style:style style:name="Text" style:family="paragraph" style:parent-style-name="Caption" style:class="extra"/>
<style:style style:name="Quotations" style:family="paragraph" style:parent-style-name="Standard" style:class="html">
<style:paragraph-properties fo:margin-left="1cm" fo:margin-right="1cm" fo:margin-top="0cm" fo:margin-bottom="0.499cm" style:contextual-spacing="false" fo:text-indent="0cm" style:auto-text-indent="false"/>
</style:style>
<style:style style:name="Title" style:family="paragraph" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:class="chapter">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
<style:text-properties fo:font-size="28pt" fo:font-weight="bold" style:font-size-asian="28pt" style:font-weight-asian="bold" style:font-size-complex="28pt" style:font-weight-complex="bold"/>
</style:style>
<style:style style:name="Subtitle" style:family="paragraph" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:class="chapter">
<style:paragraph-properties fo:margin-top="0.106cm" fo:margin-bottom="0.212cm" style:contextual-spacing="false" fo:text-align="center" style:justify-single-word="false"/>
<style:text-properties fo:font-size="18pt" style:font-size-asian="18pt" style:font-size-complex="18pt"/>
</style:style>
<style:style style:name="Frame_20_contents" style:display-name="Frame contents" style:family="paragraph" style:parent-style-name="Standard" style:class="extra"/>
<style:style style:name="Placeholder" style:family="text">
<style:text-properties fo:font-variant="small-caps" fo:color="#008080" loext:opacity="100%" style:text-underline-style="dotted" style:text-underline-width="auto" style:text-underline-color="font-color"/>
</style:style>
<style:style style:name="Bullet_20_Symbols" style:display-name="Bullet Symbols" style:family="text">
<style:text-properties style:font-name="StarSymbol" fo:font-family="StarSymbol" fo:font-size="9pt" style:font-name-asian="StarSymbol" style:font-family-asian="StarSymbol" style:font-size-asian="9pt" style:font-name-complex="StarSymbol" style:font-family-complex="StarSymbol" style:font-size-complex="9pt"/>
</style:style>
<style:style style:name="Frame" style:family="graphic">
<style:graphic-properties text:anchor-type="paragraph" svg:x="0cm" svg:y="0cm" fo:margin-left="0.201cm" fo:margin-right="0.201cm" fo:margin-top="0.201cm" fo:margin-bottom="0.201cm" style:wrap="parallel" style:number-wrapped-paragraphs="no-limit" style:wrap-contour="false" style:vertical-pos="top" style:vertical-rel="paragraph-content" style:horizontal-pos="center" style:horizontal-rel="paragraph-content" fo:background-color="transparent" draw:fill="none" draw:fill-color="#99ccff" fo:padding="0.15cm" fo:border="0.06pt solid #000000"/>
</style:style>
<text:outline-style style:name="Outline">
<text:outline-level-style text:level="1" loext:num-list-format="%1%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
<text:outline-level-style text:level="2" loext:num-list-format="%2%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
<text:outline-level-style text:level="3" loext:num-list-format="%3%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
<text:outline-level-style text:level="4" loext:num-list-format="%4%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
<text:outline-level-style text:level="5" loext:num-list-format="%5%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
<text:outline-level-style text:level="6" loext:num-list-format="%6%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
<text:outline-level-style text:level="7" loext:num-list-format="%7%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
<text:outline-level-style text:level="8" loext:num-list-format="%8%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
<text:outline-level-style text:level="9" loext:num-list-format="%9%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
<text:outline-level-style text:level="10" loext:num-list-format="%10%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
</text:outline-style>
<text:notes-configuration text:note-class="footnote" style:num-format="1" text:start-value="0" text:footnotes-position="page" text:start-numbering-at="document"/>
<text:notes-configuration text:note-class="endnote" style:num-format="i" text:start-value="0"/>
<text:linenumbering-configuration text:number-lines="false" text:offset="0.499cm" style:num-format="1" text:number-position="left" text:increment="5"/>
<loext:theme loext:name="Office Theme">
<loext:theme-colors loext:name="LibreOffice">
<loext:color loext:name="dark1" loext:color="#000000"/>
<loext:color loext:name="light1" loext:color="#ffffff"/>
<loext:color loext:name="dark2" loext:color="#000000"/>
<loext:color loext:name="light2" loext:color="#ffffff"/>
<loext:color loext:name="accent1" loext:color="#18a303"/>
<loext:color loext:name="accent2" loext:color="#0369a3"/>
<loext:color loext:name="accent3" loext:color="#a33e03"/>
<loext:color loext:name="accent4" loext:color="#8e03a3"/>
<loext:color loext:name="accent5" loext:color="#c99c00"/>
<loext:color loext:name="accent6" loext:color="#c9211e"/>
<loext:color loext:name="hyperlink" loext:color="#0000ee"/>
<loext:color loext:name="followed-hyperlink" loext:color="#551a8b"/>
</loext:theme-colors>
</loext:theme>
</office:styles>
<office:automatic-styles>
<style:style style:name="Table1" style:family="table">
<style:table-properties style:width="16.999cm" fo:margin-left="0cm" fo:margin-right="0cm" fo:margin-top="0.381cm" fo:margin-bottom="0cm" table:align="margins"/>
</style:style>
<style:style style:name="Table1.A" style:family="table-column">
<style:table-column-properties style:column-width="4.683cm" style:rel-column-width="18053*"/>
</style:style>
<style:style style:name="Table1.C" style:family="table-column">
<style:table-column-properties style:column-width="4.685cm" style:rel-column-width="18060*"/>
</style:style>
<style:style style:name="Table1.D" style:family="table-column">
<style:table-column-properties style:column-width="2.949cm" style:rel-column-width="11369*"/>
</style:style>
<style:style style:name="Table1.A1" style:family="table-cell">
<style:table-cell-properties fo:background-color="#cccccc" fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="0.05pt solid #000000" fo:border-bottom="0.05pt solid #000000">
<style:background-image/>
</style:table-cell-properties>
</style:style>
<style:style style:name="Table1.D1" style:family="table-cell">
<style:table-cell-properties fo:background-color="#cccccc" fo:padding="0.097cm" fo:border="0.05pt solid #000000">
<style:background-image/>
</style:table-cell-properties>
</style:style>
<style:style style:name="Table1.A2" style:family="table-cell">
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
</style:style>
<style:style style:name="Table1.C2" style:family="table-cell">
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
</style:style>
<style:style style:name="Table1.D2" style:family="table-cell">
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="0.05pt solid #000000" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
</style:style>
<style:style style:name="Table1.A3" style:family="table-cell">
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
</style:style>
<style:style style:name="Table1.B3" style:family="table-cell">
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
</style:style>
<style:style style:name="Table1.C3" style:family="table-cell">
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
</style:style>
<style:style style:name="Table1.D3" style:family="table-cell">
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="0.05pt solid #000000" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
</style:style>
<style:style style:name="Table1.A4" style:family="table-cell">
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
</style:style>
<style:style style:name="Table1.C4" style:family="table-cell">
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
</style:style>
<style:style style:name="Table1.D4" style:family="table-cell">
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="0.05pt solid #000000" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
</style:style>
<style:style style:name="P1" style:family="paragraph" style:parent-style-name="Header">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
<style:text-properties officeooo:paragraph-rsid="0019177b"/>
</style:style>
<style:style style:name="P2" style:family="paragraph" style:parent-style-name="Footer">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
<style:text-properties officeooo:paragraph-rsid="0019177b"/>
</style:style>
<style:style style:name="P3" style:family="paragraph" style:parent-style-name="Header">
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false"/>
<style:text-properties fo:font-size="12pt" officeooo:paragraph-rsid="00237d24" style:font-size-asian="12pt" style:font-size-complex="12pt"/>
</style:style>
<style:style style:name="P4" style:family="paragraph" style:parent-style-name="Frame_20_contents">
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false"/>
</style:style>
<style:style style:name="P5" style:family="paragraph" style:parent-style-name="Footer">
<style:paragraph-properties fo:text-align="end" style:justify-single-word="false"/>
</style:style>
<style:style style:name="P6" style:family="paragraph" style:parent-style-name="Header">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
<style:text-properties officeooo:paragraph-rsid="0019177b"/>
</style:style>
<style:style style:name="P7" style:family="paragraph" style:parent-style-name="Footer">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
<style:text-properties officeooo:paragraph-rsid="0019177b"/>
</style:style>
<style:style style:name="P8" style:family="paragraph" style:parent-style-name="Footer">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
<style:text-properties officeooo:paragraph-rsid="001ff59d"/>
</style:style>
<style:style style:name="P9" style:family="paragraph" style:parent-style-name="Heading_20_1">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
</style:style>
<style:style style:name="P10" style:family="paragraph" style:parent-style-name="Header">
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false"/>
<style:text-properties fo:font-size="12pt" officeooo:paragraph-rsid="001ff59d" style:font-size-asian="12pt" style:font-size-complex="12pt"/>
</style:style>
<style:style style:name="P11" style:family="paragraph" style:parent-style-name="Header">
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false"/>
<style:text-properties fo:font-size="12pt" officeooo:paragraph-rsid="0019177b" style:font-size-asian="12pt" style:font-size-complex="12pt"/>
</style:style>
<style:style style:name="P12" style:family="paragraph" style:parent-style-name="Frame_20_contents">
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false"/>
</style:style>
<style:style style:name="P13" style:family="paragraph" style:parent-style-name="Footer">
<style:paragraph-properties fo:text-align="end" style:justify-single-word="false"/>
</style:style>
<style:style style:name="P14" style:family="paragraph" style:parent-style-name="Table_20_Contents">
<style:paragraph-properties fo:text-align="end" style:justify-single-word="false"/>
</style:style>
<style:style style:name="P15" style:family="paragraph" style:parent-style-name="Standard">
<style:paragraph-properties fo:margin-left="11.25cm" fo:margin-right="0cm" fo:text-align="end" style:justify-single-word="false" fo:text-indent="0cm" style:auto-text-indent="false"/>
</style:style>
<style:style style:name="P16" style:family="paragraph" style:parent-style-name="Standard" style:master-page-name="">
<style:paragraph-properties fo:margin-left="11.25cm" fo:margin-right="0cm" fo:text-align="end" style:justify-single-word="false" fo:text-indent="0cm" style:auto-text-indent="false" style:page-number="auto" fo:break-before="page"/>
</style:style>
<style:style style:name="P17" style:family="paragraph" style:parent-style-name="Table_20_Heading">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
<style:text-properties style:font-name="Liberation Serif"/>
</style:style>
<style:style style:name="P18" style:family="paragraph" style:parent-style-name="Table_20_Contents">
<style:text-properties style:font-name="Liberation Serif"/>
</style:style>
<style:style style:name="P19" style:family="paragraph" style:parent-style-name="Standard">
<style:paragraph-properties fo:break-before="page"/>
</style:style>
<style:style style:name="P20" style:family="paragraph" style:parent-style-name="Standard">
<style:text-properties officeooo:paragraph-rsid="00139706"/>
</style:style>
<style:style style:name="P21" style:family="paragraph" style:parent-style-name="Heading_20_1">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
<style:text-properties style:text-underline-style="solid" style:text-underline-width="auto" style:text-underline-color="font-color" fo:font-weight="bold" style:font-weight-asian="bold" style:font-weight-complex="bold"/>
</style:style>
<style:style style:name="P22" style:family="paragraph" style:parent-style-name="Standard" style:master-page-name="">
<style:paragraph-properties fo:margin-left="0cm" fo:margin-right="0cm" fo:text-align="start" style:justify-single-word="false" fo:text-indent="0cm" style:auto-text-indent="false" style:page-number="auto" fo:break-before="page"/>
<style:text-properties fo:font-size="6pt" style:font-size-asian="5.25pt" style:font-size-complex="6pt"/>
</style:style>
<style:style style:name="P23" style:family="paragraph" style:parent-style-name="Frame_20_contents">
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false"/>
</style:style>
<style:style style:name="P24" style:family="paragraph" style:parent-style-name="Header">
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false"/>
<style:text-properties fo:font-size="12pt" officeooo:paragraph-rsid="00237d24" style:font-size-asian="12pt" style:font-size-complex="12pt"/>
</style:style>
<style:style style:name="T1" style:family="text">
<style:text-properties fo:font-weight="bold" style:font-weight-asian="bold" style:font-weight-complex="bold"/>
</style:style>
<style:style style:name="T2" style:family="text">
<style:text-properties fo:font-weight="bold" officeooo:rsid="001d148e" style:font-weight-asian="bold" style:font-weight-complex="bold"/>
</style:style>
<style:style style:name="T3" style:family="text">
<style:text-properties fo:font-weight="normal" style:font-weight-asian="normal" style:font-weight-complex="normal"/>
</style:style>
<style:style style:name="T4" style:family="text">
<style:text-properties officeooo:rsid="001f6b1d"/>
</style:style>
<style:style style:name="T5" style:family="text">
<style:text-properties style:text-underline-style="solid" style:text-underline-width="auto" style:text-underline-color="font-color" fo:font-weight="bold" style:font-weight-asian="bold" style:font-weight-complex="bold"/>
</style:style>
<style:style style:name="T6" style:family="text">
<style:text-properties style:text-underline-style="solid" style:text-underline-width="auto" style:text-underline-color="font-color" fo:font-weight="bold" officeooo:rsid="001f6b1d" style:font-weight-asian="bold" style:font-weight-complex="bold"/>
</style:style>
<style:style style:name="fr1" style:family="graphic" style:parent-style-name="Frame">
<style:graphic-properties fo:margin-left="0cm" fo:margin-right="0cm" fo:margin-top="0cm" fo:margin-bottom="0cm" style:wrap="parallel" style:number-wrapped-paragraphs="no-limit" style:vertical-pos="middle" style:vertical-rel="baseline" style:horizontal-pos="left" style:horizontal-rel="paragraph" draw:opacity="100%" fo:padding="0cm" fo:border="none" draw:wrap-influence-on-position="once-concurrent" loext:allow-overlap="true">
<style:columns fo:column-count="1" fo:column-gap="0cm"/>
</style:graphic-properties>
</style:style>
<style:page-layout style:name="pm1">
<style:page-layout-properties fo:page-width="20.999cm" fo:page-height="29.699cm" style:num-format="1" style:print-orientation="portrait" fo:margin-top="2cm" fo:margin-bottom="2cm" fo:margin-left="2cm" fo:margin-right="2cm" style:writing-mode="lr-tb" style:layout-grid-color="#c0c0c0" style:layout-grid-lines="20" style:layout-grid-base-height="0.706cm" style:layout-grid-ruby-height="0.353cm" style:layout-grid-mode="none" style:layout-grid-ruby-below="false" style:layout-grid-print="false" style:layout-grid-display="false" style:footnote-max-height="0cm" loext:margin-gutter="0cm">
<style:footnote-sep style:width="0.018cm" style:distance-before-sep="0.101cm" style:distance-after-sep="0.101cm" style:line-style="none" style:adjustment="left" style:rel-width="25%" style:color="#000000"/>
</style:page-layout-properties>
<style:header-style>
<style:header-footer-properties fo:min-height="0cm" fo:margin-left="0cm" fo:margin-right="0cm" fo:margin-bottom="0.499cm"/>
</style:header-style>
<style:footer-style>
<style:header-footer-properties fo:min-height="0cm" fo:margin-left="0cm" fo:margin-right="0cm" fo:margin-top="0.499cm"/>
</style:footer-style>
</style:page-layout>
<style:style style:name="dp1" style:family="drawing-page">
<style:drawing-page-properties draw:background-size="full"/>
</style:style>
</office:automatic-styles>
<office:master-styles>
<style:master-page style:name="Standard" style:page-layout-name="pm1" draw:style-name="dp1">
<style:header>
<text:p text:style-name="P1"><text:placeholder text:placeholder-type="text">&lt;if test=&quot;company and company.header&quot;&gt;</text:placeholder></text:p>
<text:p text:style-name="P1"><text:placeholder text:placeholder-type="text">&lt;for each=&quot;line in company.header_used.split(&apos;\n&apos;)&quot;&gt;</text:placeholder></text:p>
<text:p text:style-name="P1"><text:placeholder text:placeholder-type="text">&lt;line&gt;</text:placeholder></text:p>
<text:p text:style-name="P1"><text:placeholder text:placeholder-type="text">&lt;/for&gt;</text:placeholder></text:p>
<text:p text:style-name="P2"><text:placeholder text:placeholder-type="text">&lt;/if&gt;</text:placeholder></text:p>
<text:p text:style-name="P3"><text:placeholder text:placeholder-type="text">&lt;choose&gt;</text:placeholder></text:p>
<text:p text:style-name="P3"><text:placeholder text:placeholder-type="text">&lt;when test=&quot;company and company.logo&quot;&gt;</text:placeholder></text:p>
<text:p text:style-name="P3"><draw:frame draw:style-name="fr1" draw:name="image:company.get_logo_cm(7, 3.5)" text:anchor-type="as-char" svg:width="7.001cm" draw:z-index="2">
<draw:text-box fo:min-height="3cm">
<text:p text:style-name="P4"/>
</draw:text-box>
</draw:frame></text:p>
<text:p text:style-name="P3"><text:placeholder text:placeholder-type="text">&lt;/when&gt;</text:placeholder></text:p>
<text:p text:style-name="P3"><text:placeholder text:placeholder-type="text">&lt;when test=&quot;company&quot;&gt;</text:placeholder></text:p>
<text:p text:style-name="P3"><text:placeholder text:placeholder-type="text">&lt;company.rec_name&gt;</text:placeholder></text:p>
<text:p text:style-name="P3"><text:placeholder text:placeholder-type="text">&lt;/when&gt;</text:placeholder></text:p>
<text:p text:style-name="P3"><text:placeholder text:placeholder-type="text">&lt;/choose&gt;</text:placeholder></text:p>
</style:header>
<style:footer>
<text:p text:style-name="P2"><text:placeholder text:placeholder-type="text">&lt;if test=&quot;company and company.footer&quot;&gt;</text:placeholder></text:p>
<text:p text:style-name="P2"><text:placeholder text:placeholder-type="text">&lt;for each=&quot;line in company.footer_used.split(&apos;\n&apos;)&quot;&gt;</text:placeholder></text:p>
<text:p text:style-name="P2"><text:placeholder text:placeholder-type="text">&lt;line&gt;</text:placeholder></text:p>
<text:p text:style-name="P2"><text:placeholder text:placeholder-type="text">&lt;/for&gt;</text:placeholder></text:p>
<text:p text:style-name="P2"><text:placeholder text:placeholder-type="text">&lt;/if&gt;</text:placeholder></text:p>
<text:p text:style-name="P5"><text:page-number text:select-page="current">3</text:page-number>/<text:page-count>3</text:page-count></text:p>
</style:footer>
</style:master-page>
</office:master-styles>
<office:body>
<office:text text:use-soft-page-breaks="true">
<office:forms form:automatic-focus="false" form:apply-design-mode="false"/>
<text:sequence-decls>
<text:sequence-decl text:display-outline-level="0" text:name="Illustration"/>
<text:sequence-decl text:display-outline-level="0" text:name="Table"/>
<text:sequence-decl text:display-outline-level="0" text:name="Text"/>
<text:sequence-decl text:display-outline-level="0" text:name="Drawing"/>
<text:sequence-decl text:display-outline-level="0" text:name="Figure"/>
</text:sequence-decls>
<text:p text:style-name="P19"><text:placeholder text:placeholder-type="text">&lt;for each=&quot;shipment in records&quot;&gt;</text:placeholder></text:p>
<text:p text:style-name="P22"/>
<text:p text:style-name="P15"><text:placeholder text:placeholder-type="text">&lt;if test=&quot;shipment.to_location.warehouse and shipment.to_location.warehouse.address&quot;&gt;</text:placeholder></text:p>
<text:p text:style-name="P15"><text:placeholder text:placeholder-type="text">&lt;for each=&quot;line in shipment.to_location.warehouse.address.full_address.split(&apos;\n&apos;)&quot;&gt;</text:placeholder></text:p>
<text:p text:style-name="P15"><text:placeholder text:placeholder-type="text">&lt;line&gt;</text:placeholder></text:p>
<text:p text:style-name="P15"><text:placeholder text:placeholder-type="text">&lt;/for&gt;</text:placeholder></text:p>
<text:p text:style-name="P15"><text:placeholder text:placeholder-type="text">&lt;/if&gt;</text:placeholder></text:p>
<text:p text:style-name="P9"><text:span text:style-name="T5"><text:placeholder text:placeholder-type="text">&lt;shipment.state_string&gt;</text:placeholder></text:span><text:span text:style-name="T5"><text:s/>Internal Shipment No: </text:span><text:span text:style-name="T6"><text:placeholder text:placeholder-type="text">&lt;shipment.number&gt;</text:placeholder></text:span></text:p>
<text:p text:style-name="Standard"><text:span text:style-name="T1">Reference:</text:span><text:span text:style-name="T3"> </text:span><text:span text:style-name="T3"><text:placeholder text:placeholder-type="text">&lt;shipment.reference or &apos;&apos;&gt;</text:placeholder></text:span></text:p>
<text:p text:style-name="Standard"><text:span text:style-name="T1">From Location:</text:span> <text:placeholder text:placeholder-type="text">&lt;shipment.from_location.rec_name&gt;</text:placeholder></text:p>
<text:p text:style-name="Standard"><text:span text:style-name="T1">To Location:</text:span> <text:placeholder text:placeholder-type="text">&lt;shipment.to_location.rec_name&gt;</text:placeholder></text:p>
<text:p text:style-name="Standard"><text:span text:style-name="T1">Planned Date:</text:span><text:span text:style-name="T3"> </text:span><text:span text:style-name="T3"><text:placeholder text:placeholder-type="text">&lt;shipment.planned_date and format_date(shipment.planned_date, user.language) or &apos;&apos;&gt;</text:placeholder></text:span></text:p>
<table:table table:name="Table1" table:style-name="Table1">
<table:table-column table:style-name="Table1.A" table:number-columns-repeated="2"/>
<table:table-column table:style-name="Table1.C"/>
<table:table-column table:style-name="Table1.D"/>
<table:table-header-rows>
<table:table-row>
<table:table-cell table:style-name="Table1.A1" office:value-type="string">
<text:p text:style-name="P17">From Location</text:p>
</table:table-cell>
<table:table-cell table:style-name="Table1.A1" office:value-type="string">
<text:p text:style-name="P17">To Location</text:p>
</table:table-cell>
<table:table-cell table:style-name="Table1.A1" office:value-type="string">
<text:p text:style-name="P17">Product</text:p>
</table:table-cell>
<table:table-cell table:style-name="Table1.D1" office:value-type="string">
<text:p text:style-name="P17">Quantity</text:p>
</table:table-cell>
</table:table-row>
</table:table-header-rows>
<table:table-row>
<table:table-cell table:style-name="Table1.A2" table:number-columns-spanned="2" office:value-type="string">
<text:p text:style-name="Table_20_Contents"><text:placeholder text:placeholder-type="text">&lt;for each=&quot;move in moves(shipment)&quot;&gt;</text:placeholder></text:p>
</table:table-cell>
<table:covered-table-cell/>
<table:table-cell table:style-name="Table1.C2" office:value-type="string">
<text:p text:style-name="P18"/>
</table:table-cell>
<table:table-cell table:style-name="Table1.D2" office:value-type="string">
<text:p text:style-name="P18"/>
</table:table-cell>
</table:table-row>
<table:table-row>
<table:table-cell table:style-name="Table1.A3" office:value-type="string">
<text:p text:style-name="Table_20_Contents"><text:placeholder text:placeholder-type="text">&lt;move.from_location_name&gt;</text:placeholder></text:p>
</table:table-cell>
<table:table-cell table:style-name="Table1.A3" office:value-type="string">
<text:p text:style-name="Table_20_Contents"><text:placeholder text:placeholder-type="text">&lt;move.to_location_name&gt;</text:placeholder></text:p>
</table:table-cell>
<table:table-cell table:style-name="Table1.C2" office:value-type="string">
<text:p text:style-name="Table_20_Contents"><text:placeholder text:placeholder-type="text">&lt;move.product.rec_name&gt;</text:placeholder></text:p>
</table:table-cell>
<table:table-cell table:style-name="Table1.D2" office:value-type="string">
<text:p text:style-name="P14"><text:placeholder text:placeholder-type="text">&lt;format_number_symbol(move.quantity, user.language, move.unit, digits=move.unit.digits)&gt;</text:placeholder><text:soft-page-break/></text:p>
</table:table-cell>
</table:table-row>
<table:table-row>
<table:table-cell table:style-name="Table1.A2" table:number-columns-spanned="2" office:value-type="string">
<text:p text:style-name="Table_20_Contents"><text:placeholder text:placeholder-type="text">&lt;/for&gt;</text:placeholder></text:p>
</table:table-cell>
<table:covered-table-cell/>
<table:table-cell table:style-name="Table1.C2" office:value-type="string">
<text:p text:style-name="P18"/>
</table:table-cell>
<table:table-cell table:style-name="Table1.D2" office:value-type="string">
<text:p text:style-name="P18"/>
</table:table-cell>
</table:table-row>
</table:table>
<text:p text:style-name="Text_20_body"><text:placeholder text:placeholder-type="text">&lt;/for&gt;</text:placeholder></text:p>
</office:text>
</office:body>
</office:document>

619
modules/stock/inventory.py Normal file
View File

@@ -0,0 +1,619 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from collections import defaultdict
from sql import Null
from sql.functions import CharLength
from trytond.i18n import gettext
from trytond.model import (
ChatMixin, Check, Index, Model, ModelSQL, ModelView, Workflow, fields)
from trytond.model.exceptions import AccessError
from trytond.pool import Pool
from trytond.pyson import Bool, Eval, If
from trytond.tools import grouped_slice, is_full_text, lstrip_wildcard
from trytond.transaction import Transaction
from trytond.wizard import Button, StateTransition, StateView, Wizard
from .exceptions import InventoryCountWarning, InventoryValidationError
class Inventory(Workflow, ModelSQL, ModelView, ChatMixin):
__name__ = 'stock.inventory'
_rec_name = 'number'
_states = {
'readonly': Eval('state') != 'draft',
}
number = fields.Char('Number', readonly=True,
help="The main identifier for the inventory.")
location = fields.Many2One(
'stock.location', 'Location', required=True,
domain=[('type', '=', 'storage')], states={
'readonly': (Eval('state') != 'draft') | Eval('lines', [0]),
},
help="The location inventoried.")
date = fields.Date('Date', required=True, states={
'readonly': (Eval('state') != 'draft') | Eval('lines', [0]),
},
help="The date of the stock count.")
lines = fields.One2Many(
'stock.inventory.line', 'inventory', 'Lines',
states={
'readonly': (_states['readonly'] | ~Eval('location')
| ~Eval('date')),
})
empty_quantity = fields.Selection([
(None, ""),
('keep', "Keep"),
('empty', "Empty"),
], "Empty Quantity", states=_states,
help="How lines without a quantity are handled.")
company = fields.Many2One('company.company', 'Company', required=True,
states={
'readonly': (Eval('state') != 'draft') | Eval('lines', [0]),
},
help="The company the inventory is associated with.")
state = fields.Selection([
('draft', "Draft"),
('done', "Done"),
('cancelled', "Cancelled"),
], "State", readonly=True, sort=False,
help="The current state of the inventory.")
del _states
@classmethod
def __setup__(cls):
cls.number.search_unaccented = False
super().__setup__()
t = cls.__table__()
cls._sql_indexes.add(
Index(
t,
(t.state, Index.Equality(cardinality='low')),
where=t.state == 'draft'))
cls._order.insert(0, ('date', 'DESC'))
cls._transitions |= set((
('draft', 'done'),
('draft', 'cancelled'),
))
cls._buttons.update({
'confirm': {
'invisible': Eval('state').in_(['done', 'cancelled']),
'depends': ['state'],
},
'cancel': {
'invisible': Eval('state').in_(['cancelled', 'done']),
'depends': ['state'],
},
'complete_lines': {
'readonly': Eval('state') != 'draft',
'depends': ['state'],
},
'do_count': {
'readonly': Eval('state') != 'draft',
'depends': ['state'],
},
})
@classmethod
def order_number(cls, tables):
table, _ = tables[None]
return [
~((table.state == 'cancelled') & (table.number == Null)),
CharLength(table.number), table.number]
@staticmethod
def default_state():
return 'draft'
@staticmethod
def default_date():
Date = Pool().get('ir.date')
return Date.today()
@staticmethod
def default_company():
return Transaction().context.get('company')
def get_rec_name(self, name):
pool = Pool()
Lang = pool.get('ir.lang')
lang = Lang.get()
date = lang.strftime(self.date)
return f"[{self.number}] {self.location.rec_name} @ {date}"
@classmethod
def search_rec_name(cls, name, clause):
_, operator, operand, *extra = clause
if operator.startswith('!') or operator.startswith('not '):
bool_op = 'AND'
else:
bool_op = 'OR'
number_value = operand
if operator.endswith('like') and is_full_text(operand):
number_value = lstrip_wildcard(operand)
return [bool_op,
('number', operator, number_value, *extra),
('location.rec_name', operator, operand, *extra),
]
@classmethod
def view_attributes(cls):
return super().view_attributes() + [
('/tree', 'visual', If(Eval('state') == 'cancelled', 'muted', '')),
]
@classmethod
@ModelView.button
@Workflow.transition('done')
def confirm(cls, inventories):
pool = Pool()
Move = pool.get('stock.move')
transaction = Transaction()
moves = []
for inventory in inventories:
keys = set()
for line in inventory.lines:
key = line.unique_key
if key in keys:
raise InventoryValidationError(
gettext('stock.msg_inventory_line_unique',
line=line.rec_name,
inventory=inventory.rec_name))
keys.add(key)
move = line.get_move()
if move:
moves.append(move)
if moves:
with transaction.set_context(_product_replacement=False):
Move.save(moves)
Move.do(moves)
@classmethod
@ModelView.button
@Workflow.transition('cancelled')
def cancel(cls, inventories):
Line = Pool().get("stock.inventory.line")
Line.cancel_move([l for i in inventories for l in i.lines])
@classmethod
def preprocess_values(cls, mode, values):
pool = Pool()
Configuration = pool.get('stock.configuration')
values = super().preprocess_values(mode, values)
if mode == 'create' and not values.get('number'):
company_id = values.get('company', cls.default_company())
if company_id is not None:
configuration = Configuration(1)
if sequence := configuration.get_multivalue(
'inventory_sequence', company=company_id):
values['number'] = sequence.get()
return values
@classmethod
def on_modification(cls, mode, inventories, field_names=None):
super().on_modification(mode, inventories, field_names=field_names)
if mode in {'create', 'write'}:
cls.complete_lines(inventories, fill=False)
@classmethod
def check_modification(
cls, mode, inventories, values=None, external=False):
super().check_modification(
mode, inventories, values=values, external=external)
if mode == 'delete':
for inventory in inventories:
if inventory.state not in {'cancelled', 'draft'}:
raise AccessError(gettext(
'stock.msg_inventory_delete_cancel',
inventory=inventory.rec_name))
@classmethod
def copy(cls, inventories, default=None):
pool = Pool()
Date = pool.get('ir.date')
if default is None:
default = {}
else:
default = default.copy()
default.setdefault('date', Date.today())
default.setdefault('lines.moves', None)
default.setdefault('number', None)
return super().copy(inventories, default=default)
@staticmethod
def grouping():
return ('product',)
@classmethod
@ModelView.button
def complete_lines(cls, inventories, fill=True):
'''
Complete or update the inventories
'''
pool = Pool()
Line = pool.get('stock.inventory.line')
Product = pool.get('product.product')
grouping = cls.grouping()
to_save, to_delete = [], []
for inventory in inventories:
# Once done computation is wrong because include created moves
if inventory.state == 'done':
continue
# Compute product quantities
with Transaction().set_context(
company=inventory.company.id,
stock_date_end=inventory.date):
if fill:
pbl = Product.products_by_location(
[inventory.location.id],
grouping=grouping)
else:
product_ids = [l.product.id for l in inventory.lines]
pbl = defaultdict(int)
for product_ids in grouped_slice(product_ids):
pbl.update(Product.products_by_location(
[inventory.location.id],
grouping=grouping,
grouping_filter=(list(product_ids),)))
# Update existing lines
for line in inventory.lines:
if line.product.type != 'goods':
to_delete.append(line)
continue
key = (inventory.location.id,) + line.unique_key
if key in pbl:
quantity = pbl.pop(key)
else:
quantity = 0.0
line.update_for_complete(quantity)
to_save.append(line)
if not fill:
continue
product_idx = grouping.index('product') + 1
# Index some data
product2type = {}
product2consumable = {}
for product in Product.browse({line[product_idx] for line in pbl}):
product2type[product.id] = product.type
product2consumable[product.id] = product.consumable
# Create lines if needed
for key, quantity in pbl.items():
product_id = key[product_idx]
if (product2type[product_id] != 'goods'
or product2consumable[product_id]):
continue
if not quantity:
continue
line = Line(
inventory=inventory,
**{fname: key[i] for i, fname in enumerate(grouping, 1)})
line.update_for_complete(quantity)
to_save.append(line)
if to_delete:
Line.delete(to_delete)
if to_save:
Line.save(to_save)
@classmethod
@ModelView.button_action('stock.wizard_inventory_count')
def do_count(cls, inventories):
cls.complete_lines(inventories)
class InventoryLine(ModelSQL, ModelView):
__name__ = 'stock.inventory.line'
_states = {
'readonly': Eval('inventory_state') != 'draft',
}
product = fields.Many2One('product.product', 'Product', required=True,
domain=[
('type', '=', 'goods'),
], states=_states)
unit = fields.Function(fields.Many2One(
'product.uom', "Unit",
help="The unit in which the quantity is specified."),
'get_unit')
expected_quantity = fields.Float(
"Expected Quantity", digits='unit', required=True, readonly=True,
states={
'invisible': Eval('id', -1) < 0,
},
help="The quantity the system calculated should be in the location.")
quantity = fields.Float(
"Actual Quantity", digits='unit', states=_states,
domain=[
If(Eval('quantity', None),
('quantity', '>=', 0),
()),
],
help="The actual quantity found in the location.")
moves = fields.One2Many('stock.move', 'origin', 'Moves', readonly=True)
inventory = fields.Many2One('stock.inventory', 'Inventory', required=True,
ondelete='CASCADE',
states={
'readonly': _states['readonly'] & Bool(Eval('inventory')),
},
help="The inventory the line belongs to.")
inventory_location = fields.Function(
fields.Many2One('stock.location', "Location"),
'on_change_with_inventory_location',
searcher='search_inventory_location')
inventory_date = fields.Function(
fields.Date("Date"),
'on_change_with_inventory_date',
searcher='search_inventory_date')
inventory_state = fields.Function(
fields.Selection('get_inventory_states', "Inventory State",
depends={'inventory'}),
'on_change_with_inventory_state')
@classmethod
def __setup__(cls):
super().__setup__()
cls.__access__.add('inventory')
t = cls.__table__()
cls._sql_constraints += [
('check_line_qty_pos', Check(t, t.quantity >= 0),
'stock.msg_inventory_line_quantity_positive'),
]
cls._order.insert(0, ('product', 'ASC'))
@staticmethod
def default_expected_quantity():
return 0.
@fields.depends('product')
def on_change_product(self):
if self.product:
self.unit = self.product.default_uom
@fields.depends('inventory', '_parent_inventory.location')
def on_change_with_inventory_location(self, name=None):
return self.inventory.location if self.inventory else None
@classmethod
def search_inventory_location(cls, name, clause):
nested = clause[0][len(name):]
return [('inventory.location' + nested, *clause[1:])]
@fields.depends('inventory', '_parent_inventory.date')
def on_change_with_inventory_date(self, name=None):
if self.inventory:
return self.inventory.date
@classmethod
def search_inventory_date(cls, name, clause):
return [('inventory.date',) + tuple(clause[1:])]
@classmethod
def get_inventory_states(cls):
pool = Pool()
Inventory = pool.get('stock.inventory')
return Inventory.fields_get(['state'])['state']['selection']
@fields.depends('inventory', '_parent_inventory.state')
def on_change_with_inventory_state(self, name=None):
if self.inventory:
return self.inventory.state
return 'draft'
def get_rec_name(self, name):
return self.product.rec_name
@classmethod
def search_rec_name(cls, name, clause):
return [('product.rec_name',) + tuple(clause[1:])]
def get_unit(self, name):
return self.product.default_uom
@property
def unique_key(self):
key = []
for fname in self.inventory.grouping():
value = getattr(self, fname)
if isinstance(value, Model):
value = value.id
key.append(value)
return tuple(key)
@classmethod
def cancel_move(cls, lines):
Move = Pool().get('stock.move')
moves = [m for l in lines for m in l.moves if l.moves]
Move.cancel(moves)
Move.delete(moves)
def get_move(self):
'''
Return Move instance for the inventory line
'''
pool = Pool()
Move = pool.get('stock.move')
qty = self.quantity
if qty is None:
if self.inventory.empty_quantity is None:
raise InventoryValidationError(
gettext('stock.msg_inventory_missing_empty_quantity',
inventory=self.inventory.rec_name))
if self.inventory.empty_quantity == 'keep':
return
else:
qty = 0.0
delta_qty = self.unit.round(self.expected_quantity - qty)
if delta_qty == 0.0:
return
from_location = self.inventory.location
to_location = self.inventory.location.lost_found_used
if not to_location:
raise InventoryValidationError(
gettext('stock.msg_inventory_location_missing_lost_found',
inventory=self.inventory.rec_name,
location=self.inventory.location.rec_name))
if delta_qty < 0:
(from_location, to_location, delta_qty) = \
(to_location, from_location, -delta_qty)
return Move(
from_location=from_location,
to_location=to_location,
quantity=delta_qty,
product=self.product,
unit=self.unit,
company=self.inventory.company,
effective_date=self.inventory.date,
origin=self,
)
@fields.depends('expected_quantity')
def update_for_complete(self, quantity):
if self.expected_quantity != quantity:
self.expected_quantity = quantity
@classmethod
def check_modification(cls, mode, lines, values=None, external=False):
super().check_modification(
mode, lines, values=values, external=external)
if mode == 'delete':
for line in lines:
if line.inventory_state not in {'cancelled', 'draft'}:
raise AccessError(gettext(
'stock.msg_inventory_line_delete_cancel',
line=line.rec_name,
inventory=line.inventory.rec_name))
class Count(Wizard):
__name__ = 'stock.inventory.count'
start_state = 'search'
search = StateView(
'stock.inventory.count.search',
'stock.inventory_count_search_view_form', [
Button("End", 'end', 'tryton-cancel'),
Button("Select", 'quantity', 'tryton-forward', default=True),
])
quantity = StateView(
'stock.inventory.count.quantity',
'stock.inventory_count_quantity_view_form', [
Button("Cancel", 'search', 'tryton-cancel'),
Button("Add", 'add', 'tryton-ok', default=True),
])
add = StateTransition()
def default_quantity(self, fields):
pool = Pool()
InventoryLine = pool.get('stock.inventory.line')
Warning = pool.get('res.user.warning')
values = {}
lines = InventoryLine.search(
self.get_line_domain(self.record), limit=1)
if not lines:
warning_name = '%s.%s.count_create' % (
self.record, self.search.search)
if Warning.check(warning_name):
raise InventoryCountWarning(warning_name,
gettext('stock.msg_inventory_count_create_line',
search=self.search.search.rec_name))
line = self.get_line()
line.update_for_complete(0)
line.save()
else:
line, = lines
values['line'] = line.id
values['product'] = line.product.id
values['unit'] = line.unit.id
if line.unit.rounding == 1:
values['quantity'] = 1.
return values
def get_line_domain(self, inventory):
pool = Pool()
Product = pool.get('product.product')
domain = [
('inventory', '=', inventory.id),
]
if isinstance(self.search.search, Product):
domain.append(('product', '=', self.search.search.id))
return domain
def get_line(self):
pool = Pool()
Product = pool.get('product.product')
InventoryLine = pool.get('stock.inventory.line')
line = InventoryLine(inventory=self.record)
if isinstance(self.search.search, Product):
line.product = self.search.search
return line
def transition_add(self):
if self.quantity.line and self.quantity.quantity:
line = self.quantity.line
if line.quantity:
line.quantity += self.quantity.quantity
else:
line.quantity = self.quantity.quantity
line.save()
return 'search'
class CountSearch(ModelView):
__name__ = 'stock.inventory.count.search'
search = fields.Reference(
"Search", [
('product.product', "Product"),
],
required=True,
domain={
'product.product': [
('type', '=', 'goods'),
('consumable', '=', False),
],
},
help="The item that's counted.")
@classmethod
def default_search(cls):
return 'product.product,-1'
class CountQuantity(ModelView):
__name__ = 'stock.inventory.count.quantity'
line = fields.Many2One(
'stock.inventory.line', "Line", readonly=True, required=True)
product = fields.Many2One('product.product', "Product", readonly=True)
unit = fields.Many2One(
'product.uom', "Unit", readonly=True,
help="The unit in which the quantities are specified.")
total_quantity = fields.Float(
"Total Quantity", digits='unit', readonly=True,
help="The total amount of the line counted so far.")
quantity = fields.Float(
"Quantity", digits='unit', required=True,
help="The quantity to add to the existing count.")
@fields.depends('quantity', 'line')
def on_change_quantity(self):
if self.line:
self.total_quantity = (
(self.line.quantity or 0) + (self.quantity or 0))

175
modules/stock/inventory.xml Normal file
View File

@@ -0,0 +1,175 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<tryton>
<data>
<record model="ir.ui.view" id="inventory_view_form">
<field name="model">stock.inventory</field>
<field name="type">form</field>
<field name="name">inventory_form</field>
</record>
<record model="ir.ui.view" id="inventory_view_tree">
<field name="model">stock.inventory</field>
<field name="type">tree</field>
<field name="name">inventory_tree</field>
</record>
<record model="ir.action.act_window" id="act_inventory_form">
<field name="name">Inventories</field>
<field name="res_model">stock.inventory</field>
<field name="search_value"
eval="[('create_date', '>=', DateTime(hour=0, minute=0, second=0, microsecond=0, delta_years=-1))]"
pyson="1"/>
</record>
<record model="ir.action.act_window.view"
id="act_inventory_form_view1">
<field name="sequence" eval="1"/>
<field name="view" ref="inventory_view_tree"/>
<field name="act_window" ref="act_inventory_form"/>
</record>
<record model="ir.action.act_window.view"
id="act_inventory_form_view2">
<field name="sequence" eval="2"/>
<field name="view" ref="inventory_view_form"/>
<field name="act_window" ref="act_inventory_form"/>
</record>
<record model="ir.action.act_window.domain" id="act_inventory_form_domain_draft">
<field name="name">Draft</field>
<field name="sequence" eval="10"/>
<field name="domain" eval="[('state', '=', 'draft')]" pyson="1"/>
<field name="count" eval="True"/>
<field name="act_window" ref="act_inventory_form"/>
</record>
<record model="ir.action.act_window.domain" id="act_inventory_form_domain_all">
<field name="name">All</field>
<field name="sequence" eval="9999"/>
<field name="domain"></field>
<field name="act_window" ref="act_inventory_form"/>
</record>
<menuitem
parent="menu_stock"
action="act_inventory_form"
sequence="20"
id="menu_inventory_form"/>
<record model="ir.ui.view" id="inventory_line_view_form">
<field name="model">stock.inventory.line</field>
<field name="type">form</field>
<field name="name">inventory_line_form</field>
</record>
<record model="ir.ui.view" id="inventory_line_view_list">
<field name="model">stock.inventory.line</field>
<field name="type">tree</field>
<field name="name">inventory_line_list</field>
</record>
<record model="ir.action.act_window" id="act_inventory_line_relate">
<field name="name">Inventory Lines</field>
<field name="res_model">stock.inventory.line</field>
<field
name="domain"
eval="[If(Eval('active_ids', []) == [Eval('active_id')], ('inventory', '=', Eval('active_id')), ('inventory', 'in', Eval('active_ids')))]"
pyson="1"/>
</record>
<record model="ir.action.keyword" id="act_inventory_line_relate_keyword1">
<field name="keyword">form_relate</field>
<field name="model">stock.inventory,-1</field>
<field name="action" ref="act_inventory_line_relate"/>
</record>
<record model="ir.rule.group" id="rule_group_inventory_companies">
<field name="name">User in companies</field>
<field name="model">stock.inventory</field>
<field name="global_p" eval="True"/>
</record>
<record model="ir.rule" id="rule_inventory_companies">
<field name="domain"
eval="[('company', 'in', Eval('companies', []))]"
pyson="1"/>
<field name="rule_group" ref="rule_group_inventory_companies"/>
</record>
<record model="ir.model.access" id="access_inventory">
<field name="model">stock.inventory</field>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_inventory_group_stock">
<field name="model">stock.inventory</field>
<field name="group" ref="group_stock"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_delete" eval="True"/>
</record>
<record model="ir.model.button" id="inventory_confirm_button">
<field name="model">stock.inventory</field>
<field name="name">confirm</field>
<field name="string">Confirm</field>
<field name="confirm">Are you sure you want to confirm the inventory?</field>
</record>
<record model="ir.model.button" id="inventory_cancel_button">
<field name="model">stock.inventory</field>
<field name="name">cancel</field>
<field name="string">Cancel</field>
<field name="confirm">Are you sure you want to cancel the inventory?</field>
</record>
<record model="ir.model.button" id="inventory_complete_lines_button">
<field name="model">stock.inventory</field>
<field name="name">complete_lines</field>
<field name="string">Complete</field>
<field name="help">Add an inventory line for each missing products</field>
</record>
<record model="ir.model.button" id="inventory_count_button">
<field name="model">stock.inventory</field>
<field name="name">do_count</field>
<field name="string">Count</field>
<field name="help">Launch the wizard to count products</field>
</record>
<record model="ir.action.wizard" id="wizard_inventory_count">
<field name="name">Count</field>
<field name="wiz_name">stock.inventory.count</field>
<field name="model">stock.inventory</field>
</record>
<record model="ir.ui.view" id="inventory_count_search_view_form">
<field name="model">stock.inventory.count.search</field>
<field name="type">form</field>
<field name="name">inventory_count_search_form</field>
</record>
<record model="ir.ui.view" id="inventory_count_quantity_view_form">
<field name="model">stock.inventory.count.quantity</field>
<field name="type">form</field>
<field name="name">inventory_count_quantity_form</field>
</record>
<record model="ir.sequence.type" id="sequence_type_inventory">
<field name="name">Inventory</field>
</record>
<record model="ir.sequence.type-res.group"
id="sequence_type_inventory_group_admin">
<field name="sequence_type" ref="sequence_type_inventory"/>
<field name="group" ref="res.group_admin"/>
</record>
<record model="ir.sequence.type-res.group"
id="sequence_type_inventory_group_stock_admin">
<field name="sequence_type" ref="sequence_type_inventory"/>
<field name="group" ref="group_stock_admin"/>
</record>
<record model="ir.sequence" id="sequence_inventory">
<field name="name">Inventory</field>
<field name="sequence_type" ref="sequence_type_inventory"/>
</record>
</data>
</tryton>

57
modules/stock/ir.py Normal file
View File

@@ -0,0 +1,57 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from itertools import groupby
from operator import attrgetter
from trytond.pool import Pool, PoolMeta
from trytond.transaction import Transaction
from .shipment import ShipmentAssignMixin
class Cron(metaclass=PoolMeta):
__name__ = 'ir.cron'
@classmethod
def __setup__(cls):
super().__setup__()
cls.method.selection.extend([
('product.product|recompute_cost_price_from_moves',
"Recompute Cost Price from Moves"),
('stock.shipment.out|reschedule',
"Reschedule Customer Shipments"),
('stock.shipment.in.return|reschedule',
"Reschedule Supplier Return Shipments"),
('stock.shipment.internal|reschedule',
"Reschedule Internal Shipments"),
('ir.cron|stock_shipment_assign_try', "Assign Shipments"),
])
@classmethod
def __register__(cls, module):
table = cls.__table__()
cursor = Transaction().connection.cursor()
super().__register__(module)
# Migration from 7.0: replace assign_cron
cursor.execute(*table.update(
[table.method], ['ir.cron|stock_shipment_assign_try'],
where=table.method.in_([
'stock.shipment.out|assign_cron',
'stock.shipment.internal|assign_cron',
])))
@classmethod
def stock_shipment_assign_try(cls):
pool = Pool()
records = []
for _, kls in pool.iterobject():
if issubclass(kls, ShipmentAssignMixin):
records.extend(kls.to_assign())
records.sort(key=attrgetter('assign_order_key'))
for kls, records in groupby(records, key=attrgetter('__class__')):
kls.assign_try(list(records))

4151
modules/stock/locale/bg.po Normal file

File diff suppressed because it is too large Load Diff

3848
modules/stock/locale/ca.po Normal file

File diff suppressed because it is too large Load Diff

4051
modules/stock/locale/cs.po Normal file

File diff suppressed because it is too large Load Diff

3863
modules/stock/locale/de.po Normal file

File diff suppressed because it is too large Load Diff

3848
modules/stock/locale/es.po Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

4086
modules/stock/locale/et.po Normal file

File diff suppressed because it is too large Load Diff

4100
modules/stock/locale/fa.po Normal file

File diff suppressed because it is too large Load Diff

4049
modules/stock/locale/fi.po Normal file

File diff suppressed because it is too large Load Diff

3848
modules/stock/locale/fr.po Normal file

File diff suppressed because it is too large Load Diff

4044
modules/stock/locale/hu.po Normal file

File diff suppressed because it is too large Load Diff

3950
modules/stock/locale/id.po Normal file

File diff suppressed because it is too large Load Diff

4079
modules/stock/locale/it.po Normal file

File diff suppressed because it is too large Load Diff

4196
modules/stock/locale/lo.po Normal file

File diff suppressed because it is too large Load Diff

4052
modules/stock/locale/lt.po Normal file

File diff suppressed because it is too large Load Diff

3861
modules/stock/locale/nl.po Normal file

File diff suppressed because it is too large Load Diff

3952
modules/stock/locale/pl.po Normal file

File diff suppressed because it is too large Load Diff

4097
modules/stock/locale/pt.po Normal file

File diff suppressed because it is too large Load Diff

3956
modules/stock/locale/ro.po Normal file

File diff suppressed because it is too large Load Diff

4154
modules/stock/locale/ru.po Normal file

File diff suppressed because it is too large Load Diff

4114
modules/stock/locale/sl.po Normal file

File diff suppressed because it is too large Load Diff

4043
modules/stock/locale/tr.po Normal file

File diff suppressed because it is too large Load Diff

3779
modules/stock/locale/uk.po Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

761
modules/stock/location.py Normal file
View File

@@ -0,0 +1,761 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
import datetime
import operator
from decimal import Decimal
from sql import Column
from trytond.cache import Cache
from trytond.i18n import gettext
from trytond.model import (
DeactivableMixin, Index, MatchMixin, Model, ModelSQL, ModelView, fields,
sequence_ordered, tree)
from trytond.modules.product import price_digits, round_price
from trytond.pool import Pool
from trytond.pyson import Eval, If, TimeDelta
from trytond.tools import grouped_slice
from trytond.transaction import (
Transaction, inactive_records, without_check_access)
from .exceptions import LocationValidationError
class WarehouseWasteLocation(ModelSQL):
__name__ = 'stock.location.waste'
warehouse = fields.Many2One(
'stock.location', "Warehouse", required=True, ondelete='CASCADE',
domain=[('type', '=', 'warehouse')])
location = fields.Many2One(
'stock.location', "Waste Location", required=True, ondelete='CASCADE',
domain=[('type', '=', 'lost_found')])
class Location(DeactivableMixin, tree(), ModelSQL, ModelView):
__name__ = 'stock.location'
_default_warehouse_cache = Cache('stock.location.default_warehouse',
context=False)
name = fields.Char("Name", size=None, required=True, translate=True)
code = fields.Char(
"Code",
help="The internal identifier used for the location.")
address = fields.Many2One(
'party.address', "Address",
states={
'invisible': Eval('type') != 'warehouse',
})
type = fields.Selection([
('supplier', 'Supplier'),
('customer', 'Customer'),
('lost_found', 'Lost and Found'),
('warehouse', 'Warehouse'),
('storage', 'Storage'),
('production', 'Production'),
('drop', 'Drop'),
('rental', "Rental"),
('view', 'View'),
], "Type",
help_selection={
'supplier': "Used as the source of stock received from suppliers.",
'customer': "Used as the destination for stock sent to customers.",
'lost_found': "Used for damages, discrepancies and wastage.",
'warehouse': (
"Regroup storage locations under a logistics warehouse."),
'storage': "Used to physically store goods.",
'production': (
"Used as the destination of components and the source of "
"finished products."),
'drop': "Used during the drop shipping process.",
'view': "Group locations logically.",
})
type_string = type.translated('type')
parent = fields.Many2One(
"stock.location", "Parent", ondelete='CASCADE',
left="left", right="right",
help="Used to add structure above the location.")
left = fields.Integer('Left', required=True)
right = fields.Integer('Right', required=True)
childs = fields.One2Many("stock.location", "parent", "Children",
help="Used to add structure below the location.")
flat_childs = fields.Boolean(
"Flat Children",
help="Check to enforce a single level of children with no "
"grandchildren.")
warehouse = fields.Function(fields.Many2One('stock.location', 'Warehouse'),
'get_warehouse')
input_location = fields.Many2One(
"stock.location", "Input", states={
'invisible': Eval('type') != 'warehouse',
'required': Eval('type') == 'warehouse',
},
domain=[
['OR',
('type', '=', 'storage'),
('id', '=', Eval('storage_location', -1)),
],
['OR',
('parent', 'child_of', [Eval('id', -1)]),
('parent', '=', None),
],
],
help="Where incoming stock is received.")
output_location = fields.Many2One(
"stock.location", "Output", states={
'invisible': Eval('type') != 'warehouse',
'required': Eval('type') == 'warehouse',
},
domain=[
['OR',
('type', '=', 'storage'),
('id', '=', Eval('storage_location', -1)),
],
['OR',
('parent', 'child_of', [Eval('id', -1)]),
('parent', '=', None)]],
help="Where outgoing stock is sent from.")
storage_location = fields.Many2One(
"stock.location", "Storage", states={
'invisible': Eval('type') != 'warehouse',
'required': Eval('type') == 'warehouse',
},
domain=[
('type', 'in', ['storage', 'view']),
['OR',
('parent', 'child_of', [Eval('id', -1)]),
('parent', '=', None)]],
help="The top level location where stock is stored.")
picking_location = fields.Many2One(
'stock.location', 'Picking', states={
'invisible': Eval('type') != 'warehouse',
},
domain=[
('type', '=', 'storage'),
('parent', 'child_of', [Eval('storage_location', -1)]),
],
help="Where stock is picked from.\n"
"Leave empty to use the storage location.")
lost_found_location = fields.Many2One(
'stock.location', "Lost and Found",
states={
'invisible': Eval('type') != 'warehouse',
},
domain=[
('type', '=', 'lost_found'),
],
help="Used, by inventories, when correcting stock levels "
"in the warehouse.")
waste_locations = fields.Many2Many(
'stock.location.waste', 'warehouse', 'location', "Waste Locations",
states={
'invisible': Eval('type') != 'warehouse',
},
domain=[
('type', '=', 'lost_found'),
],
help="The locations used for waste products from the warehouse.")
waste_warehouses = fields.Many2Many(
'stock.location.waste', 'location', 'warehouse', "Waste Warehouses",
states={
'invisible': Eval('type') != 'lost_found',
},
domain=[
('type', '=', 'warehouse'),
],
help="The warehouses that use the location for waste products.")
allow_pickup = fields.Boolean(
"Allow Pickup",
states={
'invisible': (
(Eval('type') != 'warehouse')
& ~Eval('address')),
})
quantity = fields.Function(
fields.Float(
"Quantity", digits='quantity_uom',
help="The amount of stock in the location."),
'get_quantity', searcher='search_quantity')
forecast_quantity = fields.Function(
fields.Float(
"Forecast Quantity", digits='quantity_uom',
help="The amount of stock expected to be in the location."),
'get_quantity', searcher='search_quantity')
quantity_uom = fields.Function(fields.Many2One(
'product.uom', "Quantity UoM",
help="The Unit of Measure for the quantities."),
'get_quantity_uom')
cost_value = fields.Function(fields.Numeric(
"Cost Value", digits=price_digits,
help="The value of the stock in the location."),
'get_cost_value')
@classmethod
def __setup__(cls):
cls.code.search_unaccented = False
super().__setup__()
t = cls.__table__()
cls._sql_indexes.update({
Index(t, (t.code, Index.Similarity())),
Index(
t,
(t.left, Index.Range(cardinality='high')),
(t.right, Index.Range(cardinality='high'))),
})
cls._order.insert(0, ('name', 'ASC'))
parent_domain = [
['OR',
('parent.flat_childs', '=', False),
('parent', '=', None),
]
]
childs_domain = [
If(Eval('flat_childs', False),
('childs', '=', None),
()),
]
childs_mapping = cls._childs_domain()
for type_, allowed_parents in cls._parent_domain().items():
parent_domain.append(If(Eval('type') == type_,
('type', 'in', allowed_parents), ()))
childs_domain.append(If(Eval('type') == type_,
('type', 'in', childs_mapping[type_]), ()))
cls.parent.domain = parent_domain
cls.childs.domain = childs_domain
@classmethod
def _parent_domain(cls):
'''Returns a dict with location types as keys and a list of allowed
parent location types as values'''
return {
'customer': ['customer'],
'supplier': ['supplier'],
'production': ['production'],
'lost_found': ['lost_found'],
'view': ['warehouse', 'view', 'storage'],
'storage': ['warehouse', 'view', 'storage'],
'warehouse': ['view'],
}
@classmethod
def _childs_domain(cls):
childs_domain = {}
for type_, allowed_parents in cls._parent_domain().items():
for parent in allowed_parents:
childs_domain.setdefault(parent, [])
childs_domain[parent].append(type_)
return childs_domain
@classmethod
def validate_fields(cls, locations, field_names):
super().validate_fields(locations, field_names)
inactives = []
for location in locations:
location.check_type_for_moves(field_names)
if 'active' in field_names and not location.active:
inactives.append(location)
cls.check_inactive(inactives)
def check_type_for_moves(self, field_names=None):
""" Check locations with moves have types compatible with moves. """
pool = Pool()
Move = pool.get('stock.move')
if field_names and 'type' not in field_names:
return
invalid_move_types = ['warehouse', 'view']
if self.type in invalid_move_types:
moves = Move.search([
['OR',
('to_location', '=', self.id),
('from_location', '=', self.id),
],
('state', 'not in', ['staging', 'draft']),
],
order=[], limit=1)
if moves:
raise LocationValidationError(
gettext('stock.msg_location_invalid_type_for_moves',
location=self.rec_name,
type=self.type_string))
@classmethod
def check_inactive(cls, locations):
"Check inactive location are empty"
assert all(not l.active for l in locations)
empty = cls.get_empty_locations(locations)
non_empty = set(locations) - set(empty)
if non_empty:
raise LocationValidationError(
gettext('stock.msg_location_inactive_not_empty',
location=next(iter(non_empty)).rec_name))
@classmethod
def get_empty_locations(cls, locations=None):
pool = Pool()
Move = pool.get('stock.move')
Product = pool.get('product.product')
if locations is None:
locations = cls.search([])
if not locations:
return []
location_ids = list(map(int, locations))
with without_check_access(), inactive_records():
query = Move.compute_quantities_query(
location_ids, with_childs=True)
quantities = Move.compute_quantities(
query, location_ids, with_childs=True)
empty = set(location_ids)
product_ids = [q[1] for q in quantities.keys()]
consumables = {
p.id for p in Product.browse(product_ids) if p.consumable}
for (location_id, product), quantity in quantities.items():
if quantity and product not in consumables:
empty.discard(location_id)
for sub_ids in grouped_slice(list(empty)):
sub_ids = list(sub_ids)
moves = Move.search([
('state', 'not in', ['done', 'cancelled']),
['OR',
('from_location', 'in', sub_ids),
('to_location', 'in', sub_ids),
],
])
for move in moves:
for location in [move.from_location, move.to_location]:
empty.discard(location.id)
return cls.browse(empty)
@staticmethod
def default_left():
return 0
@staticmethod
def default_right():
return 0
@classmethod
def default_flat_childs(cls):
return False
@staticmethod
def default_type():
return 'storage'
def get_warehouse(self, name):
# Order by descending left to get the first one in the tree
with inactive_records():
locations = self.search([
('parent', 'parent_of', [self.id]),
('type', '=', 'warehouse'),
], order=[('left', 'DESC')])
if locations:
return locations[0].id
@classmethod
def get_default_warehouse(cls):
warehouse = Transaction().context.get('warehouse')
if warehouse:
return warehouse
warehouse = cls._default_warehouse_cache.get(None, -1)
if warehouse == -1:
warehouses = cls.search([
('type', '=', 'warehouse'),
], limit=2)
if len(warehouses) == 1:
warehouse = warehouses[0].id
else:
warehouse = None
cls._default_warehouse_cache.set(None, warehouse)
return warehouse
@property
def lost_found_used(self):
if self.warehouse:
return self.warehouse.lost_found_location
def get_rec_name(self, name):
if self.code:
return f'[{self.code}] {self.name}'
else:
return self.name
@classmethod
def search_rec_name(cls, name, clause):
if clause[1].startswith('!') or clause[1].startswith('not '):
bool_op = 'AND'
else:
bool_op = 'OR'
return [bool_op,
(cls._rec_name,) + tuple(clause[1:]),
('code',) + tuple(clause[1:]),
]
@classmethod
def _get_quantity_grouping(cls):
context = Transaction().context
grouping, grouping_filter, key = (), (), []
if context.get('product') is not None:
grouping = ('product',)
grouping_filter = ([context['product']],)
key = (context['product'],)
elif context.get('product_template') is not None:
grouping = ('product.template',)
grouping_filter = ([context['product_template']],)
key = (context['product_template'],)
return grouping, grouping_filter, key
@classmethod
def get_quantity(cls, locations, name):
pool = Pool()
Product = pool.get('product.product')
Date_ = pool.get('ir.date')
trans_context = Transaction().context
def valid_context(name):
return (trans_context.get(name) is not None
and isinstance(trans_context[name], int))
context = {}
if (name == 'quantity'
and ((trans_context.get('stock_date_end') or datetime.date.max)
> Date_.today())):
context['stock_date_end'] = Date_.today()
if name == 'forecast_quantity':
context['forecast'] = True
if not trans_context.get('stock_date_end'):
context['stock_date_end'] = datetime.date.max
grouping, grouping_filter, key = cls._get_quantity_grouping()
if not grouping:
return {loc.id: None for loc in locations}
pbl = {}
for sub_locations in grouped_slice(locations):
location_ids = [l.id for l in sub_locations]
with Transaction().set_context(context):
pbl.update(Product.products_by_location(
location_ids,
grouping=grouping,
grouping_filter=grouping_filter,
with_childs=trans_context.get('with_childs', True)))
return dict((loc.id, pbl.get((loc.id,) + key, 0)) for loc in locations)
@classmethod
def search_quantity(cls, name, domain):
_, operator_, operand = domain
operator_ = {
'=': operator.eq,
'>=': operator.ge,
'>': operator.gt,
'<=': operator.le,
'<': operator.lt,
'!=': operator.ne,
'in': lambda v, l: v in l,
'not in': lambda v, l: v not in l,
}.get(operator_, lambda v, l: False)
ids = []
for location in cls.search([], order=[('left', 'ASC')]):
if operator_(getattr(location, name), operand):
ids.append(location.id)
return [('id', 'in', ids)]
@classmethod
def get_quantity_uom(cls, locations, name):
pool = Pool()
Product = pool.get('product.product')
Template = pool.get('product.template')
context = Transaction().context
value = None
uom = None
if context.get('product') is not None:
product = Product(context['product'])
uom = product.default_uom
elif context.get('product_template') is not None:
template = Template(context['product_template'])
uom = template.default_uom
if uom:
value = uom.id
return {l.id: value for l in locations}
@classmethod
def get_cost_value(cls, locations, name):
pool = Pool()
Product = pool.get('product.product')
Template = pool.get('product.template')
trans_context = Transaction().context
cost_values = {l.id: None for l in locations}
def valid_context(name):
return (trans_context.get(name) is not None
and isinstance(trans_context[name], int))
if not any(map(valid_context, ['product', 'product_template'])):
return cost_values
def get_record():
if trans_context.get('product') is not None:
return Product(trans_context['product'])
else:
return Template(trans_context['product_template'])
context = {}
if trans_context.get('stock_date_end') is not None:
# Use the last cost_price of the day
context['_datetime'] = datetime.datetime.combine(
trans_context['stock_date_end'], datetime.time.max)
# The date could be before the product creation
record = get_record()
if record.create_date > context['_datetime']:
return cost_values
with Transaction().set_context(context):
cost_price = get_record().cost_price
# The template may have more than one product
if cost_price is not None:
for location in locations:
cost_values[location.id] = round_price(
Decimal(str(location.quantity)) * cost_price)
return cost_values
@classmethod
def _set_warehouse_parent(cls, locations):
'''
Set the parent of child location of warehouse if not set
'''
to_update = set()
to_save = []
for location in locations:
if location.type == 'warehouse':
if not location.input_location.parent:
to_update.add(location.input_location)
if not location.output_location.parent:
to_update.add(location.output_location)
if not location.storage_location.parent:
to_update.add(location.storage_location)
if to_update:
for child_location in to_update:
child_location.parent = location
to_save.append(child_location)
to_update.clear()
cls.save(to_save)
@classmethod
def on_modification(cls, mode, locations, field_names=None):
super().on_modification(mode, locations, field_names=field_names)
if mode in {'create', 'write'}:
cls._set_warehouse_parent(locations)
cls._default_warehouse_cache.clear()
@classmethod
def check_modification(cls, mode, locations, values=None, external=False):
super().check_modification(
mode, locations, values=values, external=external)
if mode == 'write' and 'parent' in values:
warehouses = cls.search([
('type', '=', 'warehouse'),
])
cls.validate_fields(
warehouses,
{'storage_location', 'input_location', 'output_location'})
@classmethod
def delete(cls, locations):
# Delete also required children as CASCADING is done separately
extra_locations = []
for location in locations:
extra_locations.extend(filter(None, [
location.input_location,
location.output_location,
location.storage_location,
]))
super().delete(locations + extra_locations)
@classmethod
def copy(cls, locations, default=None):
if default is None:
default = {}
else:
default = default.copy()
res = []
for location in locations:
if location.type == 'warehouse':
wh_default = default.copy()
wh_default['type'] = 'view'
wh_default['input_location'] = None
wh_default['output_location'] = None
wh_default['storage_location'] = None
wh_default['childs'] = None
new_location, = super().copy([location],
default=wh_default)
with Transaction().set_context(
cp_warehouse_locations={
'input_location': location.input_location.id,
'output_location': location.output_location.id,
'storage_location': location.storage_location.id,
},
cp_warehouse_id=new_location.id):
cls.copy(location.childs,
default={'parent': new_location.id})
cls.write([new_location], {
'type': 'warehouse',
})
else:
new_location, = super().copy([location],
default=default)
warehouse_locations = Transaction().context.get(
'cp_warehouse_locations') or {}
if location.id in warehouse_locations.values():
cp_warehouse = cls(
Transaction().context['cp_warehouse_id'])
for field, loc_id in warehouse_locations.items():
if loc_id == location.id:
cls.write([cp_warehouse], {
field: new_location.id,
})
res.append(new_location)
return res
@classmethod
def view_attributes(cls):
storage_types = Eval('type').in_(['storage', 'warehouse', 'view'])
return super().view_attributes() + [
('/tree/field[@name="quantity"]',
'visual', If(
storage_types & (Eval('quantity', 0) < 0), 'danger', ''),
['type']),
('/tree/field[@name="forecast_quantity"]',
'visual', If(
storage_types & (Eval('forecast_quantity', 0) < 0),
'warning', ''),
['type']),
]
class ProductsByLocationsContext(ModelView):
__name__ = 'stock.products_by_locations.context'
company = fields.Many2One('company.company', "Company", required=True)
forecast_date = fields.Date(
'At Date',
help="The date for which the stock quantity is calculated.\n"
"* An empty value calculates as far ahead as possible.\n"
"* A date in the past will provide historical values.")
stock_date_end = fields.Function(fields.Date('At Date'),
'on_change_with_stock_date_end')
@classmethod
def default_company(cls):
return Transaction().context.get('company')
@staticmethod
def default_forecast_date():
Date_ = Pool().get('ir.date')
return Date_.today()
@fields.depends('forecast_date')
def on_change_with_stock_date_end(self, name=None):
if self.forecast_date is None:
return datetime.date.max
return self.forecast_date
class ProductsByLocations(DeactivableMixin, ModelSQL, ModelView):
__name__ = 'stock.products_by_locations'
product = fields.Many2One('product.product', "Product")
quantity = fields.Function(
fields.Float("Quantity", digits='default_uom'),
'get_product', searcher='search_product')
forecast_quantity = fields.Function(
fields.Float("Forecast Quantity", digits='default_uom'),
'get_product', searcher='search_product')
default_uom = fields.Function(
fields.Many2One(
'product.uom', "Default UoM",
help="The default Unit of Measure."),
'get_product', searcher='search_product')
cost_value = fields.Function(
fields.Numeric("Cost Value"), 'get_product')
consumable = fields.Function(
fields.Boolean("Consumable"), 'get_product',
searcher='search_product')
@classmethod
def __setup__(cls):
super().__setup__()
cls._order.insert(0, ('product', 'ASC'))
@classmethod
def table_query(cls):
pool = Pool()
Product = pool.get('product.product')
product = Product.__table__()
columns = []
for fname, field in cls._fields.items():
if not hasattr(field, 'set'):
if (isinstance(field, fields.Many2One)
and field.get_target() == Product):
column = Column(product, 'id')
else:
column = Column(product, fname)
columns.append(column.as_(fname))
return product.select(*columns)
def get_rec_name(self, name):
return self.product.rec_name
@classmethod
def search_rec_name(cls, name, clause):
return [('product.rec_name',) + tuple(clause[1:])]
def get_product(self, name):
value = getattr(self.product, name)
if isinstance(value, Model):
value = value.id
return value
@classmethod
def search_product(cls, name, clause):
nested = clause[0][len(name):]
return [('product.' + name + nested, *clause[1:])]
class LocationLeadTime(sequence_ordered(), ModelSQL, ModelView, MatchMixin):
__name__ = 'stock.location.lead_time'
warehouse_from = fields.Many2One('stock.location', 'Warehouse From',
ondelete='CASCADE',
domain=[
('type', '=', 'warehouse'),
('id', '!=', Eval('warehouse_to', -1)),
])
warehouse_to = fields.Many2One('stock.location', 'Warehouse To',
ondelete='CASCADE',
domain=[
('type', '=', 'warehouse'),
('id', '!=', Eval('warehouse_from', -1)),
])
lead_time = fields.TimeDelta(
"Lead Time",
domain=['OR',
('lead_time', '=', None),
('lead_time', '>=', TimeDelta()),
],
help="The time it takes to move stock between the warehouses.")
@classmethod
def get_lead_time(cls, pattern):
for record in cls.search([]):
if record.match(pattern):
return record.lead_time

273
modules/stock/location.xml Normal file
View File

@@ -0,0 +1,273 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<tryton>
<data>
<record model="ir.ui.view" id="location_view_form">
<field name="model">stock.location</field>
<field name="type">form</field>
<field name="name">location_form</field>
</record>
<record model="ir.ui.view" id="location_view_tree">
<field name="model">stock.location</field>
<field name="type">tree</field>
<field name="priority" eval="20"/>
<field name="field_childs">childs</field>
<field name="name">location_tree</field>
</record>
<record model="ir.ui.view" id="location_view_list">
<field name="model">stock.location</field>
<field name="type">tree</field>
<field name="priority" eval="10"/>
<field name="name">location_list</field>
</record>
<record model="ir.ui.view" id="location_view_list_warehouse">
<field name="model">stock.location</field>
<field name="type">tree</field>
<field name="priority" eval="20"/>
<field name="name">location_tree</field>
</record>
<record model="ir.action.act_window" id="act_warehouse_list">
<field name="name">Warehouses</field>
<field name="res_model">stock.location</field>
<field
name="context"
eval="{'with_childs': True, 'stock_skip_warehouse': False}"
pyson="1"/>
<field name="domain" eval="[('type', '=', 'warehouse')]" pyson="1"/>
</record>
<record model="ir.action.act_window.view" id="act_warehouse_list_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="location_view_list_warehouse"/>
<field name="act_window" ref="act_warehouse_list"/>
</record>
<record model="ir.action.act_window.view" id="act_warehouse_list_view2">
<field name="sequence" eval="20"/>
<field name="view" ref="location_view_form"/>
<field name="act_window" ref="act_warehouse_list"/>
</record>
<menuitem
parent="menu_stock"
action="act_warehouse_list"
sequence="30"
id="menu_warehouse_list"/>
<record model="ir.action.act_window" id="act_location_tree">
<field name="name">Locations</field>
<field name="res_model">stock.location</field>
<field name="search_value" eval="[('type', 'in', ['storage', 'view', 'warehouse'])]" pyson="1"/>
<field name="domain" eval="[('parent', '=', None)]" pyson="1"/>
</record>
<record model="ir.action.act_window.view" id="act_location_tree_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="location_view_tree"/>
<field name="act_window" ref="act_location_tree"/>
</record>
<record model="ir.action.act_window.view" id="act_location_tree_view2">
<field name="sequence" eval="20"/>
<field name="view" ref="location_view_form"/>
<field name="act_window" ref="act_location_tree"/>
</record>
<menuitem
parent="menu_stock"
action="act_location_tree"
sequence="30"
id="menu_location_tree"/>
<record model="ir.action.act_window" id="act_location_list">
<field name="name">Locations</field>
<field name="res_model">stock.location</field>
<field name="search_value" eval="[('type', 'in', ['storage', 'view', 'warehouse'])]" pyson="1"/>
</record>
<record model="ir.action.act_window.view" id="act_location_list_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="location_view_list_warehouse"/>
<field name="act_window" ref="act_location_list"/>
</record>
<record model="ir.action.act_window.view" id="act_location_list_view2">
<field name="sequence" eval="20"/>
<field name="view" ref="location_view_form"/>
<field name="act_window" ref="act_location_list"/>
</record>
<menuitem
parent="menu_location_tree"
action="act_location_list"
sequence="10"
id="menu_location_list"/>
<record model="ir.action.act_window" id="act_location_form">
<field name="name">Locations</field>
<field name="res_model">stock.location</field>
</record>
<record model="ir.action.act_window.view" id="act_location_form_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="location_view_list"/>
<field name="act_window" ref="act_location_form"/>
</record>
<record model="ir.action.act_window.view" id="act_location_form_view2">
<field name="sequence" eval="20"/>
<field name="view" ref="location_view_form"/>
<field name="act_window" ref="act_location_form"/>
</record>
<menuitem
parent="menu_configuration"
action="act_location_form"
sequence="20"
id="menu_location_form"/>
<record model="ir.ui.view"
id="products_by_locations_context_view_form">
<field
name="model">stock.products_by_locations.context</field>
<field name="type">form</field>
<field name="name">products_by_locations_context_form</field>
</record>
<record model="ir.ui.view" id="products_by_locations_view_list">
<field name="model">stock.products_by_locations</field>
<field name="type">tree</field>
<field name="name">products_by_locations_list</field>
</record>
<record model="ir.action.act_window" id="act_products_by_locations_relate">
<field name="name">Products</field>
<field name="res_model">stock.products_by_locations</field>
<field name="context" eval="{'locations': Eval('active_ids')}" pyson="1"/>
<field name="domain"
eval="['OR', ('quantity', '!=', 0.0), ('forecast_quantity', '!=', 0.0)]"
pyson="1"/>
<field name="search_value"
eval="[('consumable', '=', False)]"
pyson="1"/>
<field name="context_model">stock.products_by_locations.context</field>
</record>
<record model="ir.action.act_window.view" id="act_products_by_locations_relate_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="products_by_locations_view_list"/>
<field name="act_window" ref="act_products_by_locations_relate"/>
</record>
<record model="ir.action.keyword" id="act_products_by_locations_relate_keyword1">
<field name="keyword">tree_open</field>
<field name="model">stock.location,-1</field>
<field name="action" ref="act_products_by_locations_relate"/>
</record>
<record model="ir.action.keyword" id="act_products_by_locations_relate_keyword2">
<field name="keyword">form_relate</field>
<field name="model">stock.location,-1</field>
<field name="action" ref="act_products_by_locations_relate"/>
</record>
<record model="ir.model.access" id="access_location">
<field name="model">stock.location</field>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_location_admin">
<field name="model">stock.location</field>
<field name="group" ref="group_stock_admin"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_delete" eval="True"/>
</record>
<record model="ir.ui.view" id="location_lead_time_view_list">
<field name="model">stock.location.lead_time</field>
<field name="type">tree</field>
<field name="name">location_lead_time_list</field>
</record>
<record model="ir.ui.view" id="location_lead_time_view_form">
<field name="model">stock.location.lead_time</field>
<field name="type">form</field>
<field name="name">location_lead_time_form</field>
</record>
<record model="ir.action.act_window" id="act_location_lead_time_form">
<field name="name">Location Lead Times</field>
<field name="res_model">stock.location.lead_time</field>
</record>
<record model="ir.action.act_window.view"
id="act_location_lead_time_form_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="location_lead_time_view_list"/>
<field name="act_window" ref="act_location_lead_time_form"/>
</record>
<record model="ir.action.act_window.view"
id="act_location_lead_time_form_view2">
<field name="sequence" eval="20"/>
<field name="view" ref="location_lead_time_view_form"/>
<field name="act_window" ref="act_location_lead_time_form"/>
</record>
<menuitem
parent="menu_configuration"
action="act_location_lead_time_form"
sequence="50"
id="menu_location_lead_time_form"/>
<record model="ir.model.access" id="access_location_lead_time">
<field name="model">stock.location.lead_time</field>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_location_lead_time_admin">
<field name="model">stock.location.lead_time</field>
<field name="group" ref="group_stock_admin"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_delete" eval="True"/>
</record>
</data>
<data noupdate="1">
<!-- Default locations -->
<record model="stock.location" id="location_input">
<field name="name">Input Zone</field>
<field name="code">IN</field>
<field name="type">storage</field>
</record>
<record model="stock.location" id="location_output">
<field name="name">Output Zone</field>
<field name="code">OUT</field>
<field name="type">storage</field>
</record>
<record model="stock.location" id="location_storage">
<field name="name">Storage Zone</field>
<field name="code">STO</field>
<field name="type">storage</field>
</record>
<record model="stock.location" id="location_lost_found">
<field name="name">Lost and Found</field>
<field name="type">lost_found</field>
</record>
<record model="stock.location" id="location_warehouse">
<field name="name">Warehouse</field>
<field name="code">WH</field>
<field name="type">warehouse</field>
<field name="input_location" ref="location_input"/>
<field name="output_location" ref="location_output"/>
<field name="storage_location" ref="location_storage"/>
<field name="lost_found_location" ref="location_lost_found"/>
</record>
<record model="stock.location" id="location_supplier">
<field name="name">Supplier</field>
<field name="code">SUP</field>
<field name="type">supplier</field>
</record>
<record model="stock.location" id="location_customer">
<field name="name">Customer</field>
<field name="code">CUS</field>
<field name="type">customer</field>
</record>
<record model="stock.location" id="location_transit">
<field name="name">Transit</field>
<field name="type">storage</field>
</record>
</data>
</tryton>

153
modules/stock/message.xml Normal file
View File

@@ -0,0 +1,153 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<tryton>
<data grouped="1">
<record model="ir.message" id="msg_product_change_default_uom">
<field name="text">You cannot change the default unit of measure of a product which is associated with stock moves.</field>
</record>
<record model="ir.message" id="msg_product_change_type">
<field name="text">You cannot change the type of a product which is associated with stock moves.</field>
</record>
<record model="ir.message" id="msg_product_change_cost_price">
<field name="text">You cannot change the cost price of a product which is associated with stock moves.
You must use the "Modify Cost Price" wizard.</field>
</record>
<record model="ir.message" id="msg_product_quantities_max">
<field name="text">You cannot select more than %(max)s products to compute quantities.</field>
</record>
<record model="ir.message" id="msg_invalid_cost_price">
<field name="text">Invalid cost price "%(cost_price)s" for product "%(product)s" with exception "%(exception)s".</field>
</record>
<record model="ir.message" id="msg_invalid_cost_price_not_number">
<field name="text">The value "%(value)s" of "%(cost_price)s" for product "%(product)s" is not a number.</field>
</record>
<record model="ir.message" id="msg_location_invalid_type_for_moves">
<field name="text">You cannot change the type of location "%(location)s" to "%(type)s"
because the type does not support moves and location has existing moves.</field>
</record>
<record model="ir.message" id="msg_location_inactive_not_empty">
<field name="text">To inactivate location "%(location)s", you must empty it.</field>
</record>
<record model="ir.message" id="msg_period_close_date">
<field name="text">You cannot close periods with a date in the future or today.</field>
</record>
<record model="ir.message" id="msg_period_close_assigned_move">
<field name="text">To close the period "%(period)s", the assigned moves "%(moves)s" must be done or cancelled.</field>
</record>
<record model="ir.message" id="msg_shipment_planned_date">
<field name="text">Planned Date</field>
</record>
<record model="ir.message" id="msg_shipment_planned_date_help">
<field name="text">When the stock operation is expected to be completed.</field>
</record>
<record model="ir.message" id="msg_shipment_origin_planned_date">
<field name="text">Origin Planned Date</field>
</record>
<record model="ir.message" id="msg_shipment_origin_planned_date_help">
<field name="text">When the stock operation was initially expected to be completed.</field>
</record>
<record model="ir.message" id="msg_shipment_effective_date">
<field name="text">Effective Date</field>
</record>
<record model="ir.message" id="msg_shipment_effective_date_help">
<field name="text">When the stock operation was actually completed.</field>
</record>
<record model="ir.message" id="msg_shipment_delay">
<field name="text">Delay</field>
</record>
<record model="ir.message" id="msg_shipment_pack_inventory_done">
<field name="text">To pack shipment "%(shipment)s" you must do the inventory moves.</field>
</record>
<record model="ir.message" id="msg_shipment_delete_cancel">
<field name="text">To delete shipment "%(shipment)s" you must cancel it.</field>
</record>
<record model="ir.message" id="msg_shipment_check_quantity">
<field name="text">The quantities of shipment "%(shipment)s" are different by %(quantities)s.</field>
</record>
<record model="ir.message" id="msg_inventory_delete_cancel">
<field name="text">To delete inventory "%(inventory)s" you must cancel it.</field>
</record>
<record model="ir.message" id="msg_inventory_missing_empty_quantity">
<field name="text">To confirm the inventory "%(inventory)s" you must select an option for empty quantity.</field>
</record>
<record model="ir.message" id="msg_inventory_location_missing_lost_found">
<field name="text">To confirm the inventory "%(inventory)s" you must set a lost and found on a parent of location "%(location)s".</field>
</record>
<record model="ir.message" id="msg_inventory_line_unique">
<field name="text">Inventory line "%(line)s" is not unique on inventory "%(inventory)s".</field>
</record>
<record model="ir.message" id="msg_inventory_line_quantity_positive">
<field name="text">Inventory line quantity must be positive.</field>
</record>
<record model="ir.message" id="msg_inventory_line_delete_cancel">
<field name="text">To delete inventory line "%(line)s" you must cancel inventory "%(inventory)s".</field>
</record>
<record model="ir.message" id="msg_inventory_count_create_line">
<field name="text">No existing line found for "%(search)s".</field>
</record>
<record model="ir.message" id="msg_erase_party_shipment">
<field name="text">You cannot erase party "%(party)s" while they have pending shipments with company "%(company)s".</field>
</record>
<record model="ir.message" id="msg_move_delete_draft_cancel">
<field name="text">To delete stock move "%(move)s" you must cancel it or reset its state to draft.</field>
</record>
<record model="ir.message" id="msg_move_modify_period_close">
<field name="text">To modify stock move "%(move)s" you must reopen period "%(period)s".</field>
</record>
<record model="ir.message" id="msg_move_modify_assigned">
<field name="text">To modify stock move "%(move)s" you must reset its state to draft.</field>
</record>
<record model="ir.message" id="msg_move_modify_done">
<field name="text">You cannot modify stock move "%(move)s" because it is done.</field>
</record>
<record model="ir.message" id="msg_move_modify_cancelled">
<field name="text">You cannot modify stock move "%(move)s" because it is cancelled.</field>
</record>
<record model="ir.message" id="msg_move_no_origin">
<field name="text">Stock moves "%(moves)s" have no origin.</field>
</record>
<record model="ir.message" id="msg_move_quantity_positive">
<field name="text">Move quantity must be positive.</field>
</record>
<record model="ir.message" id="msg_move_internal_quantity_positive">
<field name="text">Internal move quantity must be positive.</field>
</record>
<record model="ir.message" id="msg_move_from_to_location">
<field name="text">The source and destination of stock move must be different.</field>
</record>
<record model="ir.message" id="msg_move_effective_date_in_the_future">
<field name="text">The moves "%(moves)s" have effective dates in the future.</field>
</record>
<record model="ir.message" id="msg_stock_reporting_company">
<field name="text">Company</field>
</record>
<record model="ir.message" id="msg_stock_reporting_number">
<field name="text">Number</field>
</record>
<record model="ir.message" id="msg_stock_reporting_cost">
<field name="text">Cost</field>
</record>
<record model="ir.message" id="msg_stock_reporting_revenue">
<field name="text">Revenue</field>
</record>
<record model="ir.message" id="msg_stock_reporting_profit">
<field name="text">Profit</field>
</record>
<record model="ir.message" id="msg_stock_reporting_margin">
<field name="text">Margin</field>
</record>
<record model="ir.message" id="msg_stock_reporting_margin_trend">
<field name="text">Margin Trend</field>
</record>
<record model="ir.message" id="msg_stock_reporting_currency">
<field name="text">Currency</field>
</record>
<record model="ir.message" id="msg_product_location_quantity">
<field name="text">The product "%(product)s" has still some quantities in locations "%(locations)s" for company "%(company)s".</field>
</record>
<record model="ir.message" id="msg_product_location_quantity_description">
<field name="text">It is recommended to clear the stock from the storage locations before deactivating the product.</field>
</record>
</data>
</tryton>

1842
modules/stock/move.py Normal file

File diff suppressed because it is too large Load Diff

175
modules/stock/move.xml Normal file
View File

@@ -0,0 +1,175 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<tryton>
<data>
<record model="ir.ui.view" id="move_view_form">
<field name="model">stock.move</field>
<field name="type">form</field>
<field name="name">move_form</field>
</record>
<record model="ir.ui.view" id="move_view_tree">
<field name="model">stock.move</field>
<field name="type">tree</field>
<field name="priority" eval="10"/>
<field name="name">move_tree</field>
</record>
<record model="ir.ui.view" id="move_view_tree_simple">
<field name="model">stock.move</field>
<field name="type">tree</field>
<field name="priority" eval="20"/>
<field name="name">move_tree_simple</field>
</record>
<record model="ir.ui.view" id="move_view_list_shipment_in">
<field name="model">stock.move</field>
<field name="type">tree</field>
<field name="priority" eval="20"/>
<field name="name">move_list_shipment_in</field>
</record>
<record model="ir.ui.view" id="move_view_list_shipment">
<field name="model">stock.move</field>
<field name="type">tree</field>
<field name="priority" eval="20"/>
<field name="name">move_list_shipment</field>
</record>
<record model="ir.action.act_window" id="act_move_form">
<field name="name">Moves</field>
<field name="res_model">stock.move</field>
<field name="search_value"
eval="[('create_date', '>=', DateTime(hour=0, minute=0, second=0, microsecond=0, delta_years=-1))]"
pyson="1"/>
</record>
<record model="ir.action.act_window.view" id="act_move_form_view1">
<field name="sequence" eval="1"/>
<field name="view" ref="move_view_tree"/>
<field name="act_window" ref="act_move_form"/>
</record>
<record model="ir.action.act_window.view" id="act_move_form_view2">
<field name="sequence" eval="2"/>
<field name="view" ref="move_view_form"/>
<field name="act_window" ref="act_move_form"/>
</record>
<record model="ir.action.act_window.domain" id="act_move_form_domain_all">
<field name="name">All</field>
<field name="sequence" eval="10"/>
<field name="domain"></field>
<field name="act_window" ref="act_move_form"/>
</record>
<record model="ir.action.act_window.domain" id="act_move_form_domain_from_supplier">
<field name="name">From Suppliers</field>
<field name="sequence" eval="20"/>
<field name="domain"
eval="[('from_location.type', '=', 'supplier')]" pyson="1"/>
<field name="act_window" ref="act_move_form"/>
</record>
<record model="ir.action.act_window.domain" id="act_move_form_domain_from_supplier_waiting">
<field name="name">From Suppliers Waiting</field>
<field name="sequence" eval="30"/>
<field name="domain"
eval="[('from_location.type', '=', 'supplier'), ('state', '=', 'draft'), ('shipment', '=', None)]"
pyson="1"/>
<field name="count" eval="True"/>
<field name="act_window" ref="act_move_form"/>
</record>
<record model="ir.action.act_window.domain" id="act_move_form_domain_to_customer">
<field name="name">To Customers</field>
<field name="sequence" eval="40"/>
<field name="domain"
eval="[('to_location.type', '=', 'customer')]"
pyson="1"/>
<field name="act_window" ref="act_move_form"/>
</record>
<menuitem
parent="menu_stock"
action="act_move_form"
sequence="50"
id="menu_move_form"/>
<record model="ir.ui.menu-res.group"
id="menu_move_form_group_stock">
<field name="menu" ref="menu_move_form"/>
<field name="group" ref="group_stock_admin"/>
</record>
<record model="ir.rule.group" id="rule_group_move_companies">
<field name="name">User in companies</field>
<field name="model">stock.move</field>
<field name="global_p" eval="True"/>
</record>
<record model="ir.rule" id="rule_move_companies">
<field name="domain"
eval="[('company', 'in', Eval('companies', []))]"
pyson="1"/>
<field name="rule_group" ref="rule_group_move_companies"/>
</record>
<record model="ir.model.access" id="access_move">
<field name="model">stock.move</field>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_move_group_stock">
<field name="model">stock.move</field>
<field name="group" ref="group_stock"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_delete" eval="True"/>
</record>
<record model="ir.model.button" id="move_cancel_button">
<field name="model">stock.move</field>
<field name="name">cancel</field>
<field name="string">Cancel</field>
<field name="confirm">Are you sure you want to cancel the move?</field>
</record>
<record model="ir.model.button" id="move_draft_button">
<field name="model">stock.move</field>
<field name="name">draft</field>
<field name="string">Reset to Draft</field>
</record>
<record model="ir.model.button" id="move_do_button">
<field name="model">stock.move</field>
<field name="name">do</field>
<field name="string">Do</field>
<field name="confirm">Are you sure you want to complete the move?</field>
</record>
<record model="ir.action.act_window" id="act_product_moves">
<field name="name">Stock Moves</field>
<field name="res_model">stock.move</field>
<field
name="domain"
eval="[If(Eval('active_ids', []) == [Eval('active_id')], (If(Eval('active_model') == 'product.template', 'product.template', 'product'), '=', Eval('active_id', -1)), (If(Eval('active_model') == 'product.template', 'product.template', 'product'), 'in', Eval('active_ids', [])))]"
pyson="1"/>
<field name="order" eval="[('effective_date', 'DESC NULLS FIRST')]" pyson="1"/>
</record>
<record model="ir.action.act_window.view" id="act_product_moves_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="move_view_tree"/>
<field name="act_window" ref="act_product_moves"/>
</record>
<record model="ir.action.act_window.view" id="act_product_moves_view2">
<field name="sequence" eval="20"/>
<field name="view" ref="move_view_form"/>
<field name="act_window" ref="act_product_moves"/>
</record>
<record model="ir.action.keyword" id="act_product_moves_keyword1">
<field name="keyword">form_relate</field>
<field name="model">product.product,-1</field>
<field name="action" ref="act_product_moves"/>
</record>
<record model="ir.action.keyword" id="act_product_moves_keyword2">
<field name="keyword">form_relate</field>
<field name="model">product.template,-1</field>
<field name="action" ref="act_product_moves"/>
</record>
</data>
</tryton>

165
modules/stock/party.py Normal file
View File

@@ -0,0 +1,165 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from trytond.i18n import gettext
from trytond.model import ModelSQL, ValueMixin, fields
from trytond.modules.party.exceptions import EraseError
from trytond.pool import Pool, PoolMeta
from trytond.transaction import Transaction
supplier_location = fields.Many2One(
'stock.location', "Supplier Location",
ondelete='RESTRICT', domain=[('type', '=', 'supplier')],
help="The default source location for stock received from the party.")
customer_location = fields.Many2One(
'stock.location', "Customer Location",
ondelete='RESTRICT', domain=[('type', '=', 'customer')],
help="The default destination location for stock sent to the party.")
class Party(metaclass=PoolMeta):
__name__ = 'party.party'
supplier_location = fields.MultiValue(supplier_location)
customer_location = fields.MultiValue(customer_location)
locations = fields.One2Many(
'party.party.location', 'party', "Locations")
delivered_to_warehouses = fields.Many2Many(
'party.party-delivered_to-stock.location', 'party', 'location',
"Delivered to Warehouses",
domain=[
('type', '=', 'warehouse'),
],
filter=[
('allow_pickup', '=', True),
])
@classmethod
def multivalue_model(cls, field):
pool = Pool()
if field in {'supplier_location', 'customer_location'}:
return pool.get('party.party.location')
return super().multivalue_model(field)
@classmethod
def default_supplier_location(cls, **pattern):
return cls.multivalue_model(
'supplier_location').default_supplier_location()
@classmethod
def default_customer_location(cls, **pattern):
return cls.multivalue_model(
'customer_location').default_customer_location()
def address_get(self, type=None):
pool = Pool()
Location = pool.get('stock.location')
context = Transaction().context
address = super().address_get(type=type)
if (type == 'delivery'
and context.get('warehouse')):
warehouse = Location(context['warehouse'])
if warehouse in self.delivered_to_warehouses:
address = warehouse.address
return address
class PartyLocation(ModelSQL, ValueMixin):
__name__ = 'party.party.location'
party = fields.Many2One('party.party', "Party", ondelete='CASCADE')
supplier_location = supplier_location
customer_location = customer_location
@classmethod
def default_supplier_location(cls):
pool = Pool()
ModelData = pool.get('ir.model.data')
try:
return ModelData.get_id('stock', 'location_supplier')
except KeyError:
return None
@classmethod
def default_customer_location(cls):
pool = Pool()
ModelData = pool.get('ir.model.data')
try:
return ModelData.get_id('stock', 'location_customer')
except KeyError:
return None
class PartyDeliveredToWarehouse(ModelSQL):
__name__ = 'party.party-delivered_to-stock.location'
party = fields.Many2One(
'party.party', "Party", required=True, ondelete='CASCADE')
location = fields.Many2One(
'stock.location', "Location", required=True, ondelete='CASCADE',
domain=[
('type', '=', 'warehouse'),
])
class Address(metaclass=PoolMeta):
__name__ = 'party.address'
delivery = fields.Boolean(
'Delivery',
help="Check to send deliveries to the address.")
warehouses = fields.One2Many(
'stock.location', 'address', "Warehouses", readonly=True)
class ContactMechanism(metaclass=PoolMeta):
__name__ = 'party.contact_mechanism'
delivery = fields.Boolean(
'Delivery',
help="Check to use for delivery.")
@classmethod
def usages(cls, _fields=None):
if _fields is None:
_fields = []
_fields.append('delivery')
return super().usages(_fields=_fields)
class Replace(metaclass=PoolMeta):
__name__ = 'party.replace'
@classmethod
def fields_to_replace(cls):
return super().fields_to_replace() + [
('stock.shipment.in', 'supplier'),
('stock.shipment.in.return', 'supplier'),
('stock.shipment.out', 'customer'),
('stock.shipment.out.return', 'customer'),
]
class Erase(metaclass=PoolMeta):
__name__ = 'party.erase'
def check_erase_company(self, party, company):
pool = Pool()
ShipmentIn = pool.get('stock.shipment.in')
ShipmentInReturn = pool.get('stock.shipment.in.return')
ShipmentOut = pool.get('stock.shipment.out')
ShipmentOutReturn = pool.get('stock.shipment.out.return')
super().check_erase_company(party, company)
for Shipment, field in [
(ShipmentIn, 'supplier'),
(ShipmentInReturn, 'supplier'),
(ShipmentOut, 'customer'),
(ShipmentOutReturn, 'customer'),
]:
shipments = Shipment.search([
(field, '=', party.id),
('company', '=', company.id),
('state', 'not in', ['done', 'cancelled']),
])
if shipments:
raise EraseError(
gettext('stock.msg_erase_party_shipment',
party=party.rec_name,
company=company.rec_name))

81
modules/stock/party.xml Normal file
View File

@@ -0,0 +1,81 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<tryton>
<data>
<record model="ir.ui.view" id="party_party_view_form">
<field name="model">party.party</field>
<field name="inherit" ref="party.party_view_form"/>
<field name="name">party_form</field>
</record>
<record model="ir.ui.view" id="address_view_tree">
<field name="model">party.address</field>
<field name="inherit" ref="party.address_view_tree"/>
<field name="name">party_address_tree</field>
</record>
<record model="ir.ui.view" id="address_view_form">
<field name="model">party.address</field>
<field name="inherit" ref="party.address_view_form"/>
<field name="name">party_address_form</field>
</record>
<record model="ir.ui.view" id="address_view_form_simple">
<field name="model">party.address</field>
<field name="inherit" ref="party.address_view_form_simple"/>
<field name="name">party_address_form</field>
</record>
<record model="ir.ui.view" id="address_view_tree_sequence">
<field name="model">party.address</field>
<field name="inherit" ref="party.address_view_tree_sequence"/>
<field name="name">party_address_tree</field>
</record>
<record model="ir.ui.view" id="contact_mechanism_view_tree">
<field name="model">party.contact_mechanism</field>
<field name="inherit" ref="party.contact_mechanism_view_tree"/>
<field name="name">party_contact_mechanism_tree</field>
</record>
<record model="ir.ui.view" id="contact_mechanism_view_form">
<field name="model">party.contact_mechanism</field>
<field name="inherit" ref="party.contact_mechanism_view_form"/>
<field name="name">party_contact_mechanism_form</field>
</record>
<record model="ir.ui.view" id="contact_mechanism_view_tree_sequence">
<field name="model">party.contact_mechanism</field>
<field name="inherit" ref="party.contact_mechanism_view_tree_sequence"/>
<field name="name">party_contact_mechanism_tree</field>
</record>
<record model="ir.action.act_window" id="act_shipment_out_form2">
<field name="name">Customer Shipments</field>
<field name="res_model">stock.shipment.out</field>
<field name="domain"
eval="[If(Eval('active_ids', []) == [Eval('active_id')], ('customer', '=', Eval('active_id')), ('customer', 'in', Eval('active_ids')))]"
pyson="1"/>
<field name="search_value" eval="[('state', 'not in', ['done', 'cancelled'])]" pyson="1"/>
</record>
<record model="ir.action.keyword"
id="act_open_purchase_keyword1">
<field name="keyword">form_relate</field>
<field name="model">party.party,-1</field>
<field name="action" ref="act_shipment_out_form2"/>
</record>
<record model="ir.action.act_window" id="act_shipment_out_form3">
<field name="name">Supplier Shipments</field>
<field name="res_model">stock.shipment.in</field>
<field name="domain"
eval="[If(Eval('active_ids', []) == [Eval('active_id')], ('supplier', '=', Eval('active_id')), ('supplier', 'in', Eval('active_ids')))]"
pyson="1"/>
<field name="search_value" eval="[('state', 'not in', ['done', 'cancelled'])]" pyson="1"/>
</record>
<record model="ir.action.keyword"
id="act_open_purchase_keyword2">
<field name="keyword">form_relate</field>
<field name="model">party.party,-1</field>
<field name="action" ref="act_shipment_out_form3"/>
</record>
</data>
</tryton>

200
modules/stock/period.py Normal file
View File

@@ -0,0 +1,200 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from itertools import chain, groupby
from sql import For, Literal
from trytond.i18n import gettext
from trytond.model import Index, ModelSQL, ModelView, Workflow, fields
from trytond.pool import Pool
from trytond.pyson import Eval
from trytond.tools import grouped_slice
from trytond.transaction import Transaction
from .exceptions import PeriodCloseError
class Period(Workflow, ModelSQL, ModelView):
__name__ = 'stock.period'
date = fields.Date('Date', required=True, states={
'readonly': Eval('state') == 'closed',
},
help="When the stock period ends.")
company = fields.Many2One(
'company.company', "Company", required=True,
help="The company the stock period is associated with.")
caches = fields.One2Many('stock.period.cache', 'period', 'Caches',
readonly=True)
state = fields.Selection([
('draft', 'Draft'),
('closed', 'Closed'),
], "State", readonly=True, sort=False,
help="The current state of the stock period.")
@classmethod
def __setup__(cls):
super().__setup__()
t = cls.__table__()
cls._sql_indexes.add(
Index(
t,
(t.company, Index.Range()),
(t.date, Index.Range(order='DESC')),
where=t.state == 'closed'))
cls._transitions |= set((
('draft', 'closed'),
('closed', 'draft'),
))
cls._buttons.update({
'draft': {
'invisible': Eval('state') == 'draft',
'depends': ['state'],
},
'close': {
'invisible': Eval('state') == 'closed',
'depends': ['state'],
},
})
@staticmethod
def default_company():
return Transaction().context.get('company')
@staticmethod
def default_state():
return 'draft'
@staticmethod
def groupings():
return [('product',)]
@staticmethod
def get_cache(grouping):
pool = Pool()
if all(g == 'product' or g.startswith('product.') for g in grouping):
return pool.get('stock.period.cache')
def get_rec_name(self, name):
return Pool().get('ir.lang').get().strftime(self.date)
@classmethod
@ModelView.button
@Workflow.transition('draft')
def draft(cls, periods):
for grouping in cls.groupings():
Cache = cls.get_cache(grouping)
caches = []
for sub_periods in grouped_slice(periods):
caches.append(Cache.search([
('period', 'in',
[p.id for p in sub_periods]),
], order=[]))
Cache.delete(list(chain(*caches)))
@classmethod
@ModelView.button
@Workflow.transition('closed')
def close(cls, periods):
pool = Pool()
Product = pool.get('product.product')
Location = pool.get('stock.location')
Move = pool.get('stock.move')
Date = pool.get('ir.date')
transaction = Transaction()
connection = transaction.connection
database = transaction.database
Move.lock()
if database.has_select_for():
move = Move.__table__()
query = move.select(Literal(1), for_=For('UPDATE', nowait=True))
with connection.cursor() as cursor:
cursor.execute(*query)
locations = Location.search([
('type', 'not in', ['warehouse', 'view']),
], order=[])
for company, c_periods in groupby(periods, key=lambda p: p.company):
c_periods = list(c_periods)
with Transaction().set_context(company=company.id):
today = Date.today()
recent_period = max(c_periods, key=lambda p: p.date)
recent_date = recent_period.date
if recent_date >= today:
raise PeriodCloseError(
gettext('stock.msg_period_close_date'))
if assigned_moves := Move.search([
('company', '=', company.id),
('state', '=', 'assigned'),
['OR', [
('effective_date', '=', None),
('planned_date', '<=', recent_date),
],
('effective_date', '<=', recent_date),
]],
limit=6):
names = ', '.join(m.rec_name for m in assigned_moves[:5])
if len(assigned_moves) > 5:
names += '...'
raise PeriodCloseError(
gettext('stock.msg_period_close_assigned_move',
period=recent_period.rec_name,
moves=names))
for grouping in cls.groupings():
Cache = cls.get_cache(grouping)
to_create = []
for period in periods:
with Transaction().set_context(
stock_date_end=period.date,
stock_date_start=None,
stock_assign=False,
forecast=False,
stock_destinations=None,
):
pbl = Product.products_by_location(
[l.id for l in locations], grouping=grouping)
for key, quantity in pbl.items():
if quantity:
values = {
'location': key[0],
'period': period.id,
'internal_quantity': quantity,
}
for i, field in enumerate(grouping, 1):
values[field] = key[i]
to_create.append(values)
if to_create:
Cache.create(to_create)
class Cache(ModelSQL, ModelView):
"It is used to store cached computation of stock quantities"
__name__ = 'stock.period.cache'
period = fields.Many2One(
'stock.period', "Period",
required=True, readonly=True, ondelete='CASCADE')
location = fields.Many2One(
'stock.location', "Location",
required=True, readonly=True, ondelete='CASCADE')
product = fields.Many2One(
'product.product', "Product",
required=True, readonly=True, ondelete='CASCADE')
internal_quantity = fields.Float('Internal Quantity', readonly=True)
@classmethod
def __setup__(cls):
super().__setup__()
t = cls.__table__()
cls._sql_indexes.update({
Index(
t,
(t.period, Index.Range()),
(t.location, Index.Range()),
(t.product, Index.Range()),
include=[t.internal_quantity]),
Index(
t,
(t.location, Index.Range())),
})

120
modules/stock/period.xml Normal file
View File

@@ -0,0 +1,120 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<tryton>
<data>
<record model="ir.ui.view" id="period_view_form">
<field name="model">stock.period</field>
<field name="type">form</field>
<field name="name">period_form</field>
</record>
<record model="ir.ui.view" id="period_view_list">
<field name="model">stock.period</field>
<field name="type">tree</field>
<field name="name">period_list</field>
</record>
<record model="ir.action.act_window" id="act_period_list">
<field name="name">Periods</field>
<field name="res_model">stock.period</field>
</record>
<record model="ir.action.act_window.view" id="act_period_list_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="period_view_list"/>
<field name="act_window" ref="act_period_list"/>
</record>
<record model="ir.action.act_window.view" id="act_period_list_view2">
<field name="sequence" eval="20"/>
<field name="view" ref="period_view_form"/>
<field name="act_window" ref="act_period_list"/>
</record>
<menuitem
parent="menu_configuration"
action="act_period_list"
sequence="30"
id="menu_period_list"/>
<record model="ir.rule.group" id="rule_group_period_companies">
<field name="name">User in companies</field>
<field name="model">stock.period</field>
<field name="global_p" eval="True"/>
</record>
<record model="ir.rule" id="rule_period_companies">
<field name="domain"
eval="[('company', 'in', Eval('companies', []))]"
pyson="1"/>
<field name="rule_group" ref="rule_group_period_companies"/>
</record>
<record model="ir.model.access" id="access_period">
<field name="model">stock.period</field>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_period_stock">
<field name="model">stock.period</field>
<field name="group" ref="group_stock"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_period_admin">
<field name="model">stock.period</field>
<field name="group" ref="group_stock_admin"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_delete" eval="True"/>
</record>
<record model="ir.model.button" id="period_draft_button">
<field name="model">stock.period</field>
<field name="name">draft</field>
<field name="string">Draft</field>
</record>
<record model="ir.model.button" id="period_close_button">
<field name="model">stock.period</field>
<field name="name">close</field>
<field name="string">Close</field>
</record>
<record model="ir.ui.view" id="period_cache_view_form">
<field name="model">stock.period.cache</field>
<field name="type">form</field>
<field name="name">period_cache_form</field>
</record>
<record model="ir.ui.view" id="period_cache_view_list">
<field name="model">stock.period.cache</field>
<field name="type">tree</field>
<field name="name">period_cache_list</field>
</record>
<record model="ir.model.access" id="access_period_cache_cache">
<field name="model">stock.period.cache</field>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_period_cache_cache_stock">
<field name="model">stock.period.cache</field>
<field name="group" ref="group_stock"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_period_cache_admin">
<field name="model">stock.period.cache</field>
<field name="group" ref="group_stock_admin"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_delete" eval="True"/>
</record>
</data>
</tryton>

View File

@@ -0,0 +1,612 @@
<?xml version="1.0" encoding="UTF-8"?>
<office:document xmlns:css3t="http://www.w3.org/TR/css3-text/" xmlns:grddl="http://www.w3.org/2003/g/data-view#" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xforms="http://www.w3.org/2002/xforms" xmlns:dom="http://www.w3.org/2001/xml-events" xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" xmlns:ooow="http://openoffice.org/2004/writer" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:drawooo="http://openoffice.org/2010/draw" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" xmlns:tableooo="http://openoffice.org/2009/table" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:rpt="http://openoffice.org/2005/report" xmlns:formx="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:officeooo="http://openoffice.org/2009/office" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text">
<office:meta><meta:generator>LibreOffice/7.6.4.1$Linux_X86_64 LibreOffice_project/60$Build-1</meta:generator><meta:initial-creator>Bertrand Chenal</meta:initial-creator><meta:creation-date>2009-03-26T13:54:48</meta:creation-date><dc:date>2024-04-19T23:16:38.105151902</dc:date><meta:editing-cycles>55</meta:editing-cycles><meta:editing-duration>PT7H43M47S</meta:editing-duration><meta:document-statistic meta:table-count="1" meta:image-count="0" meta:object-count="0" meta:page-count="3" meta:paragraph-count="49" meta:word-count="117" meta:character-count="1209" meta:non-whitespace-character-count="1140"/><meta:user-defined meta:name="Info 1"/><meta:user-defined meta:name="Info 2"/><meta:user-defined meta:name="Info 3"/><meta:user-defined meta:name="Info 4"/></office:meta>
<office:settings>
<config:config-item-set config:name="ooo:view-settings">
<config:config-item config:name="ViewAreaTop" config:type="long">0</config:config-item>
<config:config-item config:name="ViewAreaLeft" config:type="long">0</config:config-item>
<config:config-item config:name="ViewAreaWidth" config:type="long">25269</config:config-item>
<config:config-item config:name="ViewAreaHeight" config:type="long">23973</config:config-item>
<config:config-item config:name="ShowRedlineChanges" config:type="boolean">true</config:config-item>
<config:config-item config:name="InBrowseMode" config:type="boolean">false</config:config-item>
<config:config-item-map-indexed config:name="Views">
<config:config-item-map-entry>
<config:config-item config:name="ViewId" config:type="string">view2</config:config-item>
<config:config-item config:name="ViewLeft" config:type="long">5955</config:config-item>
<config:config-item config:name="ViewTop" config:type="long">10248</config:config-item>
<config:config-item config:name="VisibleLeft" config:type="long">0</config:config-item>
<config:config-item config:name="VisibleTop" config:type="long">0</config:config-item>
<config:config-item config:name="VisibleRight" config:type="long">25268</config:config-item>
<config:config-item config:name="VisibleBottom" config:type="long">23971</config:config-item>
<config:config-item config:name="ZoomType" config:type="short">0</config:config-item>
<config:config-item config:name="ViewLayoutColumns" config:type="short">0</config:config-item>
<config:config-item config:name="ViewLayoutBookMode" config:type="boolean">false</config:config-item>
<config:config-item config:name="ZoomFactor" config:type="short">100</config:config-item>
<config:config-item config:name="IsSelectedFrame" config:type="boolean">false</config:config-item>
<config:config-item config:name="KeepRatio" config:type="boolean">false</config:config-item>
<config:config-item config:name="AnchoredTextOverflowLegacy" config:type="boolean">false</config:config-item>
<config:config-item config:name="LegacySingleLineFontwork" config:type="boolean">false</config:config-item>
<config:config-item config:name="ConnectorUseSnapRect" config:type="boolean">false</config:config-item>
<config:config-item config:name="IgnoreBreakAfterMultilineField" config:type="boolean">false</config:config-item>
</config:config-item-map-entry>
</config:config-item-map-indexed>
</config:config-item-set>
<config:config-item-set config:name="ooo:configuration-settings">
<config:config-item config:name="PrintRightPages" config:type="boolean">true</config:config-item>
<config:config-item config:name="PrintProspectRTL" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintLeftPages" config:type="boolean">true</config:config-item>
<config:config-item config:name="PrintPaperFromSetup" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintControls" config:type="boolean">true</config:config-item>
<config:config-item config:name="PrintProspect" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintBlackFonts" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintAnnotationMode" config:type="short">0</config:config-item>
<config:config-item config:name="PrintEmptyPages" config:type="boolean">true</config:config-item>
<config:config-item config:name="PrintSingleJobs" config:type="boolean">false</config:config-item>
<config:config-item config:name="AutoFirstLineIndentDisregardLineSpace" config:type="boolean">false</config:config-item>
<config:config-item config:name="HeaderSpacingBelowLastPara" config:type="boolean">false</config:config-item>
<config:config-item config:name="ProtectBookmarks" config:type="boolean">false</config:config-item>
<config:config-item config:name="ContinuousEndnotes" config:type="boolean">false</config:config-item>
<config:config-item config:name="DisableOffPagePositioning" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintTables" config:type="boolean">true</config:config-item>
<config:config-item config:name="SubtractFlysAnchoredAtFlys" config:type="boolean">true</config:config-item>
<config:config-item config:name="ApplyParagraphMarkFormatToNumbering" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintFaxName" config:type="string"/>
<config:config-item config:name="SurroundTextWrapSmall" config:type="boolean">false</config:config-item>
<config:config-item config:name="TreatSingleColumnBreakAsPageBreak" config:type="boolean">false</config:config-item>
<config:config-item config:name="PropLineSpacingShrinksFirstLine" config:type="boolean">false</config:config-item>
<config:config-item config:name="TabOverSpacing" config:type="boolean">false</config:config-item>
<config:config-item config:name="TabOverMargin" config:type="boolean">false</config:config-item>
<config:config-item config:name="EmbedComplexScriptFonts" config:type="boolean">true</config:config-item>
<config:config-item config:name="EmbedLatinScriptFonts" config:type="boolean">true</config:config-item>
<config:config-item config:name="EmbedOnlyUsedFonts" config:type="boolean">false</config:config-item>
<config:config-item config:name="EmbedFonts" config:type="boolean">false</config:config-item>
<config:config-item config:name="ClippedPictures" config:type="boolean">false</config:config-item>
<config:config-item config:name="FrameAutowidthWithMorePara" config:type="boolean">false</config:config-item>
<config:config-item config:name="FloattableNomargins" config:type="boolean">false</config:config-item>
<config:config-item config:name="UnbreakableNumberings" config:type="boolean">false</config:config-item>
<config:config-item config:name="AllowPrintJobCancel" config:type="boolean">true</config:config-item>
<config:config-item config:name="UseFormerObjectPositioning" config:type="boolean">false</config:config-item>
<config:config-item config:name="UseOldNumbering" config:type="boolean">false</config:config-item>
<config:config-item config:name="RsidRoot" config:type="int">1812667</config:config-item>
<config:config-item config:name="PrinterPaperFromSetup" config:type="boolean">false</config:config-item>
<config:config-item config:name="CurrentDatabaseDataSource" config:type="string"/>
<config:config-item config:name="UpdateFromTemplate" config:type="boolean">true</config:config-item>
<config:config-item config:name="AddFrameOffsets" config:type="boolean">false</config:config-item>
<config:config-item config:name="LoadReadonly" config:type="boolean">false</config:config-item>
<config:config-item config:name="Rsid" config:type="int">2677670</config:config-item>
<config:config-item config:name="FootnoteInColumnToPageEnd" config:type="boolean">true</config:config-item>
<config:config-item config:name="ProtectFields" config:type="boolean">false</config:config-item>
<config:config-item config:name="SaveGlobalDocumentLinks" config:type="boolean">false</config:config-item>
<config:config-item config:name="ClipAsCharacterAnchoredWriterFlyFrames" config:type="boolean">false</config:config-item>
<config:config-item config:name="LinkUpdateMode" config:type="short">1</config:config-item>
<config:config-item config:name="AddExternalLeading" config:type="boolean">true</config:config-item>
<config:config-item config:name="PrintGraphics" config:type="boolean">true</config:config-item>
<config:config-item config:name="EmbedSystemFonts" config:type="boolean">false</config:config-item>
<config:config-item config:name="IsLabelDocument" config:type="boolean">false</config:config-item>
<config:config-item config:name="AddParaLineSpacingToTableCells" config:type="boolean">false</config:config-item>
<config:config-item config:name="UseFormerTextWrapping" config:type="boolean">false</config:config-item>
<config:config-item config:name="HyphenateURLs" config:type="boolean">true</config:config-item>
<config:config-item config:name="AddParaTableSpacingAtStart" config:type="boolean">true</config:config-item>
<config:config-item config:name="TabsRelativeToIndent" config:type="boolean">true</config:config-item>
<config:config-item config:name="FieldAutoUpdate" config:type="boolean">true</config:config-item>
<config:config-item config:name="SaveVersionOnClose" config:type="boolean">false</config:config-item>
<config:config-item config:name="ChartAutoUpdate" config:type="boolean">true</config:config-item>
<config:config-item config:name="ImagePreferredDPI" config:type="int">0</config:config-item>
<config:config-item config:name="PrinterSetup" config:type="base64Binary"/>
<config:config-item config:name="SmallCapsPercentage66" config:type="boolean">true</config:config-item>
<config:config-item config:name="AlignTabStopPosition" config:type="boolean">true</config:config-item>
<config:config-item config:name="DropCapPunctuation" config:type="boolean">false</config:config-item>
<config:config-item config:name="MathBaselineAlignment" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrinterName" config:type="string"/>
<config:config-item config:name="CharacterCompressionType" config:type="short">0</config:config-item>
<config:config-item config:name="AddParaTableSpacing" config:type="boolean">true</config:config-item>
<config:config-item config:name="DoNotJustifyLinesWithManualBreak" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintHiddenText" config:type="boolean">false</config:config-item>
<config:config-item config:name="IsKernAsianPunctuation" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrinterIndependentLayout" config:type="string">high-resolution</config:config-item>
<config:config-item config:name="TabOverflow" config:type="boolean">false</config:config-item>
<config:config-item config:name="AddParaSpacingToTableCells" config:type="boolean">true</config:config-item>
<config:config-item config:name="AddVerticalFrameOffsets" config:type="boolean">false</config:config-item>
<config:config-item config:name="TabAtLeftIndentForParagraphsInList" config:type="boolean">false</config:config-item>
<config:config-item config:name="ApplyUserData" config:type="boolean">true</config:config-item>
<config:config-item config:name="MsWordCompMinLineHeightByFly" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintTextPlaceholder" config:type="boolean">false</config:config-item>
<config:config-item config:name="IgnoreFirstLineIndentInNumbering" config:type="boolean">false</config:config-item>
<config:config-item config:name="UseFormerLineSpacing" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintPageBackground" config:type="boolean">true</config:config-item>
<config:config-item config:name="RedlineProtectionKey" config:type="base64Binary"/>
<config:config-item config:name="EmbedAsianScriptFonts" config:type="boolean">true</config:config-item>
<config:config-item config:name="BackgroundParaOverDrawings" config:type="boolean">false</config:config-item>
<config:config-item config:name="SaveThumbnail" config:type="boolean">true</config:config-item>
<config:config-item config:name="ConsiderTextWrapOnObjPos" config:type="boolean">false</config:config-item>
<config:config-item config:name="EmbeddedDatabaseName" config:type="string"/>
<config:config-item config:name="ProtectForm" config:type="boolean">false</config:config-item>
<config:config-item config:name="DoNotResetParaAttrsForNumFont" config:type="boolean">false</config:config-item>
<config:config-item config:name="MsWordCompTrailingBlanks" config:type="boolean">false</config:config-item>
<config:config-item config:name="EmptyDbFieldHidesPara" config:type="boolean">false</config:config-item>
<config:config-item config:name="TableRowKeep" config:type="boolean">false</config:config-item>
<config:config-item config:name="NoNumberingShowFollowBy" config:type="boolean">false</config:config-item>
<config:config-item config:name="InvertBorderSpacing" config:type="boolean">false</config:config-item>
<config:config-item config:name="IgnoreTabsAndBlanksForLineCalculation" config:type="boolean">false</config:config-item>
<config:config-item config:name="DoNotCaptureDrawObjsOnPage" config:type="boolean">false</config:config-item>
<config:config-item config:name="GutterAtTop" config:type="boolean">false</config:config-item>
<config:config-item config:name="StylesNoDefault" config:type="boolean">false</config:config-item>
<config:config-item config:name="UnxForceZeroExtLeading" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintReversed" config:type="boolean">false</config:config-item>
<config:config-item config:name="UseOldPrinterMetrics" config:type="boolean">false</config:config-item>
<config:config-item config:name="CurrentDatabaseCommandType" config:type="int">0</config:config-item>
<config:config-item config:name="PrintDrawings" config:type="boolean">true</config:config-item>
<config:config-item config:name="OutlineLevelYieldsNumbering" config:type="boolean">false</config:config-item>
<config:config-item config:name="CurrentDatabaseCommand" config:type="string"/>
<config:config-item config:name="CollapseEmptyCellPara" config:type="boolean">true</config:config-item>
</config:config-item-set>
</office:settings>
<office:scripts>
<office:script script:language="ooo:Basic">
<ooo:libraries xmlns:ooo="http://openoffice.org/2004/office" xmlns:xlink="http://www.w3.org/1999/xlink"/>
</office:script>
</office:scripts>
<office:font-face-decls>
<style:font-face style:name="Andale Sans UI" svg:font-family="&apos;Andale Sans UI&apos;" style:font-family-generic="system" style:font-pitch="variable"/>
<style:font-face style:name="DejaVu Sans" svg:font-family="&apos;DejaVu Sans&apos;" style:font-family-generic="system" style:font-pitch="variable"/>
<style:font-face style:name="Liberation Sans" svg:font-family="&apos;Liberation Sans&apos;" style:font-adornments="Regular" style:font-family-generic="swiss" style:font-pitch="variable"/>
<style:font-face style:name="Liberation Serif" svg:font-family="&apos;Liberation Serif&apos;" style:font-family-generic="roman" style:font-pitch="variable"/>
<style:font-face style:name="Liberation Serif1" svg:font-family="&apos;Liberation Serif&apos;" style:font-adornments="Bold" style:font-family-generic="roman" style:font-pitch="variable"/>
<style:font-face style:name="Liberation Serif2" svg:font-family="&apos;Liberation Serif&apos;" style:font-adornments="Regular" style:font-family-generic="roman" style:font-pitch="variable"/>
<style:font-face style:name="StarSymbol" svg:font-family="StarSymbol"/>
<style:font-face style:name="Thorndale AMT" svg:font-family="&apos;Thorndale AMT&apos;" style:font-family-generic="roman" style:font-pitch="variable"/>
</office:font-face-decls>
<office:styles>
<style:default-style style:family="graphic">
<style:graphic-properties svg:stroke-color="#000000" draw:fill-color="#99ccff" fo:wrap-option="no-wrap" draw:shadow-offset-x="0.3cm" draw:shadow-offset-y="0.3cm" draw:start-line-spacing-horizontal="0.283cm" draw:start-line-spacing-vertical="0.283cm" draw:end-line-spacing-horizontal="0.283cm" draw:end-line-spacing-vertical="0.283cm" style:writing-mode="lr-tb" style:flow-with-text="false"/>
<style:paragraph-properties style:text-autospace="ideograph-alpha" style:line-break="strict" loext:tab-stop-distance="0cm" style:writing-mode="lr-tb" style:font-independent-line-spacing="false">
<style:tab-stops/>
</style:paragraph-properties>
<style:text-properties style:use-window-font-color="true" loext:opacity="0%" style:font-name="Thorndale AMT" fo:font-size="12pt" fo:language="en" fo:country="US" style:letter-kerning="true" style:font-name-asian="Andale Sans UI" style:font-size-asian="10.5pt" style:language-asian="zxx" style:country-asian="none" style:font-name-complex="Andale Sans UI" style:font-size-complex="12pt" style:language-complex="zxx" style:country-complex="none"/>
</style:default-style>
<style:default-style style:family="paragraph">
<style:paragraph-properties fo:hyphenation-ladder-count="no-limit" style:text-autospace="ideograph-alpha" style:punctuation-wrap="hanging" style:line-break="strict" style:tab-stop-distance="1.251cm" style:writing-mode="lr-tb"/>
<style:text-properties style:use-window-font-color="true" loext:opacity="0%" style:font-name="Thorndale AMT" fo:font-size="12pt" fo:language="en" fo:country="US" style:letter-kerning="true" style:font-name-asian="Andale Sans UI" style:font-size-asian="10.5pt" style:language-asian="zxx" style:country-asian="none" style:font-name-complex="Andale Sans UI" style:font-size-complex="12pt" style:language-complex="zxx" style:country-complex="none" fo:hyphenate="false" fo:hyphenation-remain-char-count="2" fo:hyphenation-push-char-count="2" loext:hyphenation-no-caps="false" loext:hyphenation-no-last-word="false" loext:hyphenation-word-char-count="5" loext:hyphenation-zone="no-limit"/>
</style:default-style>
<style:default-style style:family="table">
<style:table-properties table:border-model="collapsing"/>
</style:default-style>
<style:default-style style:family="table-row">
<style:table-row-properties fo:keep-together="auto"/>
</style:default-style>
<style:style style:name="Standard" style:family="paragraph" style:class="text">
<style:text-properties style:font-name="Liberation Sans" fo:font-family="&apos;Liberation Sans&apos;" style:font-style-name="Regular" style:font-family-generic="swiss" style:font-pitch="variable" style:font-size-asian="10.5pt"/>
</style:style>
<style:style style:name="Heading" style:family="paragraph" style:parent-style-name="Standard" style:next-style-name="Text_20_body" style:class="text">
<style:paragraph-properties fo:margin-top="0.423cm" fo:margin-bottom="0.212cm" style:contextual-spacing="false" fo:keep-with-next="always"/>
<style:text-properties style:font-name="Liberation Serif2" fo:font-family="&apos;Liberation Serif&apos;" style:font-style-name="Regular" style:font-family-generic="roman" style:font-pitch="variable" fo:font-size="16pt" style:font-name-asian="DejaVu Sans" style:font-family-asian="&apos;DejaVu Sans&apos;" style:font-family-generic-asian="system" style:font-pitch-asian="variable" style:font-size-asian="14pt" style:font-name-complex="DejaVu Sans" style:font-family-complex="&apos;DejaVu Sans&apos;" style:font-family-generic-complex="system" style:font-pitch-complex="variable" style:font-size-complex="14pt"/>
</style:style>
<style:style style:name="Text_20_body" style:display-name="Text body" style:family="paragraph" style:parent-style-name="Standard" style:class="text">
<style:paragraph-properties fo:margin-top="0cm" fo:margin-bottom="0.212cm" style:contextual-spacing="false"/>
<style:text-properties style:font-name="Liberation Sans" fo:font-family="&apos;Liberation Sans&apos;" style:font-style-name="Regular" style:font-family-generic="swiss" style:font-pitch="variable" style:font-size-asian="10.5pt"/>
</style:style>
<style:style style:name="List" style:family="paragraph" style:parent-style-name="Text_20_body" style:class="list">
<style:text-properties style:font-size-asian="12pt"/>
</style:style>
<style:style style:name="Caption" style:family="paragraph" style:parent-style-name="Standard" style:class="extra">
<style:paragraph-properties fo:margin-top="0.212cm" fo:margin-bottom="0.212cm" style:contextual-spacing="false" text:number-lines="false" text:line-number="0"/>
<style:text-properties fo:font-size="12pt" fo:font-style="italic" style:font-size-asian="12pt" style:font-style-asian="italic" style:font-size-complex="12pt" style:font-style-complex="italic"/>
</style:style>
<style:style style:name="Index" style:family="paragraph" style:parent-style-name="Standard" style:class="index">
<style:paragraph-properties text:number-lines="false" text:line-number="0"/>
<style:text-properties style:font-size-asian="12pt"/>
</style:style>
<style:style style:name="Heading_20_1" style:display-name="Heading 1" style:family="paragraph" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:class="text">
<style:text-properties fo:font-size="16pt" fo:font-weight="bold" style:font-size-asian="115%" style:font-weight-asian="bold" style:font-size-complex="115%" style:font-weight-complex="bold"/>
</style:style>
<style:style style:name="Table_20_Contents" style:display-name="Table Contents" style:family="paragraph" style:parent-style-name="Standard" style:class="extra">
<style:paragraph-properties text:number-lines="false" text:line-number="0"/>
<style:text-properties style:font-size-asian="10.5pt"/>
</style:style>
<style:style style:name="Table_20_Heading" style:display-name="Table Heading" style:family="paragraph" style:parent-style-name="Table_20_Contents" style:class="extra" style:master-page-name="">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false" style:page-number="auto" text:number-lines="false" text:line-number="0"/>
<style:text-properties style:font-name="Liberation Serif1" fo:font-family="&apos;Liberation Serif&apos;" style:font-style-name="Bold" style:font-family-generic="roman" style:font-pitch="variable" fo:font-weight="bold" style:font-size-asian="10.5pt" style:font-weight-asian="bold" style:font-weight-complex="bold"/>
</style:style>
<style:style style:name="Header_20_and_20_Footer" style:display-name="Header and Footer" style:family="paragraph" style:parent-style-name="Standard" style:class="extra">
<style:paragraph-properties text:number-lines="false" text:line-number="0">
<style:tab-stops>
<style:tab-stop style:position="8.795cm" style:type="center"/>
<style:tab-stop style:position="17.59cm" style:type="right"/>
</style:tab-stops>
</style:paragraph-properties>
</style:style>
<style:style style:name="Header" style:family="paragraph" style:parent-style-name="Standard" style:class="extra">
<style:paragraph-properties text:number-lines="false" text:line-number="0">
<style:tab-stops>
<style:tab-stop style:position="8.795cm" style:type="center"/>
<style:tab-stop style:position="17.59cm" style:type="right"/>
</style:tab-stops>
</style:paragraph-properties>
<style:text-properties fo:font-size="9pt" style:font-size-asian="10.5pt"/>
</style:style>
<style:style style:name="Heading_20_2" style:display-name="Heading 2" style:family="paragraph" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:class="text">
<style:text-properties fo:font-size="14pt" fo:font-style="italic" fo:font-weight="bold" style:font-size-asian="14pt" style:font-style-asian="italic" style:font-weight-asian="bold" style:font-size-complex="14pt" style:font-style-complex="italic" style:font-weight-complex="bold"/>
</style:style>
<style:style style:name="Footer" style:family="paragraph" style:parent-style-name="Standard" style:class="extra">
<style:paragraph-properties text:number-lines="false" text:line-number="0">
<style:tab-stops>
<style:tab-stop style:position="8.795cm" style:type="center"/>
<style:tab-stop style:position="17.59cm" style:type="right"/>
</style:tab-stops>
</style:paragraph-properties>
<style:text-properties fo:font-size="9pt" style:font-size-asian="10.5pt"/>
</style:style>
<style:style style:name="Heading_20_3" style:display-name="Heading 3" style:family="paragraph" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:class="text">
<style:text-properties fo:font-size="14pt" fo:font-weight="bold" style:font-size-asian="14pt" style:font-weight-asian="bold" style:font-size-complex="14pt" style:font-weight-complex="bold"/>
</style:style>
<style:style style:name="Text_20_body_20_indent" style:display-name="Text body indent" style:family="paragraph" style:parent-style-name="Text_20_body" style:class="text">
<style:paragraph-properties fo:margin-left="0.499cm" fo:margin-right="0cm" fo:margin-top="0cm" fo:margin-bottom="0cm" style:contextual-spacing="false" fo:text-indent="0cm" style:auto-text-indent="false"/>
</style:style>
<style:style style:name="Text" style:family="paragraph" style:parent-style-name="Caption" style:class="extra"/>
<style:style style:name="Quotations" style:family="paragraph" style:parent-style-name="Standard" style:class="html">
<style:paragraph-properties fo:margin-left="1cm" fo:margin-right="1cm" fo:margin-top="0cm" fo:margin-bottom="0.499cm" style:contextual-spacing="false" fo:text-indent="0cm" style:auto-text-indent="false"/>
</style:style>
<style:style style:name="Title" style:family="paragraph" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:class="chapter">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
<style:text-properties fo:font-size="28pt" fo:font-weight="bold" style:font-size-asian="28pt" style:font-weight-asian="bold" style:font-size-complex="28pt" style:font-weight-complex="bold"/>
</style:style>
<style:style style:name="Subtitle" style:family="paragraph" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:class="chapter">
<style:paragraph-properties fo:margin-top="0.106cm" fo:margin-bottom="0.212cm" style:contextual-spacing="false" fo:text-align="center" style:justify-single-word="false"/>
<style:text-properties fo:font-size="18pt" style:font-size-asian="18pt" style:font-size-complex="18pt"/>
</style:style>
<style:style style:name="Frame_20_contents" style:display-name="Frame contents" style:family="paragraph" style:parent-style-name="Standard" style:class="extra"/>
<style:style style:name="Placeholder" style:family="text">
<style:text-properties fo:font-variant="small-caps" fo:color="#008080" loext:opacity="100%" style:text-underline-style="dotted" style:text-underline-width="auto" style:text-underline-color="font-color"/>
</style:style>
<style:style style:name="Bullet_20_Symbols" style:display-name="Bullet Symbols" style:family="text">
<style:text-properties style:font-name="StarSymbol" fo:font-family="StarSymbol" fo:font-size="9pt" style:font-name-asian="StarSymbol" style:font-family-asian="StarSymbol" style:font-size-asian="9pt" style:font-name-complex="StarSymbol" style:font-family-complex="StarSymbol" style:font-size-complex="9pt"/>
</style:style>
<style:style style:name="Frame" style:family="graphic">
<style:graphic-properties text:anchor-type="paragraph" svg:x="0cm" svg:y="0cm" fo:margin-left="0.201cm" fo:margin-right="0.201cm" fo:margin-top="0.201cm" fo:margin-bottom="0.201cm" style:wrap="parallel" style:number-wrapped-paragraphs="no-limit" style:wrap-contour="false" style:vertical-pos="top" style:vertical-rel="paragraph-content" style:horizontal-pos="center" style:horizontal-rel="paragraph-content" fo:background-color="transparent" draw:fill="none" draw:fill-color="#99ccff" fo:padding="0.15cm" fo:border="0.06pt solid #000000"/>
</style:style>
<text:outline-style style:name="Outline">
<text:outline-level-style text:level="1" loext:num-list-format="%1%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
<text:outline-level-style text:level="2" loext:num-list-format="%2%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
<text:outline-level-style text:level="3" loext:num-list-format="%3%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
<text:outline-level-style text:level="4" loext:num-list-format="%4%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
<text:outline-level-style text:level="5" loext:num-list-format="%5%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
<text:outline-level-style text:level="6" loext:num-list-format="%6%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
<text:outline-level-style text:level="7" loext:num-list-format="%7%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
<text:outline-level-style text:level="8" loext:num-list-format="%8%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
<text:outline-level-style text:level="9" loext:num-list-format="%9%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
<text:outline-level-style text:level="10" loext:num-list-format="%10%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
</text:outline-style>
<text:notes-configuration text:note-class="footnote" style:num-format="1" text:start-value="0" text:footnotes-position="page" text:start-numbering-at="document"/>
<text:notes-configuration text:note-class="endnote" style:num-format="i" text:start-value="0"/>
<text:linenumbering-configuration text:number-lines="false" text:offset="0.499cm" style:num-format="1" text:number-position="left" text:increment="5"/>
<loext:theme loext:name="Office Theme">
<loext:theme-colors loext:name="LibreOffice">
<loext:color loext:name="dark1" loext:color="#000000"/>
<loext:color loext:name="light1" loext:color="#ffffff"/>
<loext:color loext:name="dark2" loext:color="#000000"/>
<loext:color loext:name="light2" loext:color="#ffffff"/>
<loext:color loext:name="accent1" loext:color="#18a303"/>
<loext:color loext:name="accent2" loext:color="#0369a3"/>
<loext:color loext:name="accent3" loext:color="#a33e03"/>
<loext:color loext:name="accent4" loext:color="#8e03a3"/>
<loext:color loext:name="accent5" loext:color="#c99c00"/>
<loext:color loext:name="accent6" loext:color="#c9211e"/>
<loext:color loext:name="hyperlink" loext:color="#0000ee"/>
<loext:color loext:name="followed-hyperlink" loext:color="#551a8b"/>
</loext:theme-colors>
</loext:theme>
</office:styles>
<office:automatic-styles>
<style:style style:name="Table1" style:family="table">
<style:table-properties style:width="16.999cm" fo:margin-left="0cm" fo:margin-right="0cm" fo:margin-top="0.381cm" fo:margin-bottom="0cm" table:align="margins"/>
</style:style>
<style:style style:name="Table1.A" style:family="table-column">
<style:table-column-properties style:column-width="4.683cm" style:rel-column-width="18053*"/>
</style:style>
<style:style style:name="Table1.C" style:family="table-column">
<style:table-column-properties style:column-width="4.685cm" style:rel-column-width="18060*"/>
</style:style>
<style:style style:name="Table1.D" style:family="table-column">
<style:table-column-properties style:column-width="2.949cm" style:rel-column-width="11369*"/>
</style:style>
<style:style style:name="Table1.A1" style:family="table-cell">
<style:table-cell-properties fo:background-color="#cccccc" fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="0.05pt solid #000000" fo:border-bottom="0.05pt solid #000000">
<style:background-image/>
</style:table-cell-properties>
</style:style>
<style:style style:name="Table1.D1" style:family="table-cell">
<style:table-cell-properties fo:background-color="#cccccc" fo:padding="0.097cm" fo:border="0.05pt solid #000000">
<style:background-image/>
</style:table-cell-properties>
</style:style>
<style:style style:name="Table1.A2" style:family="table-cell">
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
</style:style>
<style:style style:name="Table1.C2" style:family="table-cell">
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
</style:style>
<style:style style:name="Table1.D2" style:family="table-cell">
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="0.05pt solid #000000" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
</style:style>
<style:style style:name="Table1.A3" style:family="table-cell">
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
</style:style>
<style:style style:name="Table1.B3" style:family="table-cell">
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
</style:style>
<style:style style:name="Table1.C3" style:family="table-cell">
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
</style:style>
<style:style style:name="Table1.D3" style:family="table-cell">
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="0.05pt solid #000000" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
</style:style>
<style:style style:name="Table1.A4" style:family="table-cell">
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
</style:style>
<style:style style:name="Table1.C4" style:family="table-cell">
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
</style:style>
<style:style style:name="Table1.D4" style:family="table-cell">
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="0.05pt solid #000000" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
</style:style>
<style:style style:name="P1" style:family="paragraph" style:parent-style-name="Header">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
<style:text-properties officeooo:paragraph-rsid="00201d29"/>
</style:style>
<style:style style:name="P2" style:family="paragraph" style:parent-style-name="Footer">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
<style:text-properties officeooo:paragraph-rsid="00201d29"/>
</style:style>
<style:style style:name="P3" style:family="paragraph" style:parent-style-name="Header">
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false"/>
<style:text-properties fo:font-size="12pt" officeooo:paragraph-rsid="0028dba6" style:font-size-asian="12pt" style:font-size-complex="12pt"/>
</style:style>
<style:style style:name="P4" style:family="paragraph" style:parent-style-name="Frame_20_contents">
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false"/>
</style:style>
<style:style style:name="P5" style:family="paragraph" style:parent-style-name="Footer">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
<style:text-properties officeooo:paragraph-rsid="00215c08"/>
</style:style>
<style:style style:name="P6" style:family="paragraph" style:parent-style-name="Footer">
<style:paragraph-properties fo:text-align="end" style:justify-single-word="false"/>
</style:style>
<style:style style:name="P7" style:family="paragraph" style:parent-style-name="Header">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
<style:text-properties officeooo:paragraph-rsid="00201d29"/>
</style:style>
<style:style style:name="P8" style:family="paragraph" style:parent-style-name="Footer">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
<style:text-properties officeooo:paragraph-rsid="00201d29"/>
</style:style>
<style:style style:name="P9" style:family="paragraph" style:parent-style-name="Footer">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
<style:text-properties officeooo:paragraph-rsid="00215c08"/>
</style:style>
<style:style style:name="P10" style:family="paragraph" style:parent-style-name="Footer">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
<style:text-properties officeooo:paragraph-rsid="00282b04"/>
</style:style>
<style:style style:name="P11" style:family="paragraph" style:parent-style-name="Header">
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false"/>
<style:text-properties fo:font-size="12pt" officeooo:paragraph-rsid="00282b04" style:font-size-asian="12pt" style:font-size-complex="12pt"/>
</style:style>
<style:style style:name="P12" style:family="paragraph" style:parent-style-name="Header">
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false"/>
<style:text-properties fo:font-size="12pt" officeooo:paragraph-rsid="00201d29" style:font-size-asian="12pt" style:font-size-complex="12pt"/>
</style:style>
<style:style style:name="P13" style:family="paragraph" style:parent-style-name="Frame_20_contents">
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false"/>
</style:style>
<style:style style:name="P14" style:family="paragraph" style:parent-style-name="Footer">
<style:paragraph-properties fo:text-align="end" style:justify-single-word="false"/>
</style:style>
<style:style style:name="P15" style:family="paragraph" style:parent-style-name="Table_20_Contents">
<style:paragraph-properties fo:text-align="end" style:justify-single-word="false"/>
</style:style>
<style:style style:name="P16" style:family="paragraph" style:parent-style-name="Table_20_Heading">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
<style:text-properties style:font-name="Liberation Serif"/>
</style:style>
<style:style style:name="P17" style:family="paragraph" style:parent-style-name="Standard">
<style:paragraph-properties fo:break-before="page"/>
</style:style>
<style:style style:name="P18" style:family="paragraph" style:parent-style-name="Heading_20_1">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false" fo:break-before="page"/>
<style:text-properties style:text-underline-style="solid" style:text-underline-width="auto" style:text-underline-color="font-color"/>
</style:style>
<style:style style:name="P19" style:family="paragraph" style:parent-style-name="Heading_20_1">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
<style:text-properties style:text-underline-style="solid" style:text-underline-width="auto" style:text-underline-color="font-color"/>
</style:style>
<style:style style:name="P20" style:family="paragraph" style:parent-style-name="Heading_20_1">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
<style:text-properties style:text-underline-style="solid" style:text-underline-width="auto" style:text-underline-color="font-color" officeooo:paragraph-rsid="00279246"/>
</style:style>
<style:style style:name="P21" style:family="paragraph" style:parent-style-name="Standard">
<style:text-properties officeooo:paragraph-rsid="001d944d"/>
</style:style>
<style:style style:name="P22" style:family="paragraph" style:parent-style-name="Frame_20_contents">
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false"/>
</style:style>
<style:style style:name="P23" style:family="paragraph" style:parent-style-name="Header">
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false"/>
<style:text-properties fo:font-size="12pt" officeooo:paragraph-rsid="0028dba6" style:font-size-asian="12pt" style:font-size-complex="12pt"/>
</style:style>
<style:style style:name="T1" style:family="text">
<style:text-properties fo:font-weight="bold" style:font-weight-asian="bold" style:font-weight-complex="bold"/>
</style:style>
<style:style style:name="T2" style:family="text">
<style:text-properties fo:font-weight="bold" officeooo:rsid="00236359" style:font-weight-asian="bold" style:font-weight-complex="bold"/>
</style:style>
<style:style style:name="T3" style:family="text">
<style:text-properties fo:font-weight="normal" style:font-weight-asian="normal" style:font-weight-complex="normal"/>
</style:style>
<style:style style:name="T4" style:family="text">
<style:text-properties officeooo:rsid="00279246"/>
</style:style>
<style:style style:name="fr1" style:family="graphic" style:parent-style-name="Frame">
<style:graphic-properties fo:margin-left="0cm" fo:margin-right="0cm" fo:margin-top="0cm" fo:margin-bottom="0cm" style:wrap="parallel" style:number-wrapped-paragraphs="no-limit" style:vertical-pos="middle" style:vertical-rel="baseline" style:horizontal-pos="left" style:horizontal-rel="paragraph" draw:opacity="100%" fo:padding="0cm" fo:border="none" draw:wrap-influence-on-position="once-concurrent" loext:allow-overlap="true">
<style:columns fo:column-count="1" fo:column-gap="0cm"/>
</style:graphic-properties>
</style:style>
<style:page-layout style:name="pm1">
<style:page-layout-properties fo:page-width="20.999cm" fo:page-height="29.699cm" style:num-format="1" style:print-orientation="portrait" fo:margin-top="2cm" fo:margin-bottom="2cm" fo:margin-left="2cm" fo:margin-right="2cm" style:writing-mode="lr-tb" style:layout-grid-color="#c0c0c0" style:layout-grid-lines="20" style:layout-grid-base-height="0.706cm" style:layout-grid-ruby-height="0.353cm" style:layout-grid-mode="none" style:layout-grid-ruby-below="false" style:layout-grid-print="false" style:layout-grid-display="false" style:footnote-max-height="0cm" loext:margin-gutter="0cm">
<style:footnote-sep style:width="0.018cm" style:distance-before-sep="0.101cm" style:distance-after-sep="0.101cm" style:line-style="none" style:adjustment="left" style:rel-width="25%" style:color="#000000"/>
</style:page-layout-properties>
<style:header-style>
<style:header-footer-properties fo:min-height="0cm" fo:margin-left="0cm" fo:margin-right="0cm" fo:margin-bottom="0.499cm"/>
</style:header-style>
<style:footer-style>
<style:header-footer-properties fo:min-height="0cm" fo:margin-left="0cm" fo:margin-right="0cm" fo:margin-top="0.499cm"/>
</style:footer-style>
</style:page-layout>
<style:style style:name="dp1" style:family="drawing-page">
<style:drawing-page-properties draw:background-size="full"/>
</style:style>
</office:automatic-styles>
<office:master-styles>
<style:master-page style:name="Standard" style:page-layout-name="pm1" draw:style-name="dp1">
<style:header>
<text:p text:style-name="P1"><text:placeholder text:placeholder-type="text">&lt;if test=&quot;company and company.header&quot;&gt;</text:placeholder></text:p>
<text:p text:style-name="P1"><text:placeholder text:placeholder-type="text">&lt;for each=&quot;line in company.header_used.split(&apos;\n&apos;)&quot;&gt;</text:placeholder></text:p>
<text:p text:style-name="P1"><text:placeholder text:placeholder-type="text">&lt;line&gt;</text:placeholder></text:p>
<text:p text:style-name="P1"><text:placeholder text:placeholder-type="text">&lt;/for&gt;</text:placeholder></text:p>
<text:p text:style-name="P2"><text:placeholder text:placeholder-type="text">&lt;/if&gt;</text:placeholder></text:p>
<text:p text:style-name="P3"><text:placeholder text:placeholder-type="text">&lt;choose&gt;</text:placeholder></text:p>
<text:p text:style-name="P3"><text:placeholder text:placeholder-type="text">&lt;when test=&quot;company and company.logo&quot;&gt;</text:placeholder></text:p>
<text:p text:style-name="P3"><draw:frame draw:style-name="fr1" draw:name="image:company.get_logo_cm(7, 3.5)" text:anchor-type="as-char" svg:width="7.001cm" draw:z-index="2">
<draw:text-box fo:min-height="3cm">
<text:p text:style-name="P4"/>
</draw:text-box>
</draw:frame></text:p>
<text:p text:style-name="P3"><text:placeholder text:placeholder-type="text">&lt;/when&gt;</text:placeholder></text:p>
<text:p text:style-name="P3"><text:placeholder text:placeholder-type="text">&lt;when test=&quot;company&quot;&gt;</text:placeholder></text:p>
<text:p text:style-name="P3"><text:placeholder text:placeholder-type="text">&lt;company.rec_name&gt;</text:placeholder></text:p>
<text:p text:style-name="P3"><text:placeholder text:placeholder-type="text">&lt;/when&gt;</text:placeholder></text:p>
<text:p text:style-name="P3"><text:placeholder text:placeholder-type="text">&lt;/choose&gt;</text:placeholder></text:p>
</style:header>
<style:footer>
<text:p text:style-name="P5"><text:placeholder text:placeholder-type="text">&lt;if test=&quot;company and company.footer&quot;&gt;</text:placeholder></text:p>
<text:p text:style-name="P5"><text:placeholder text:placeholder-type="text">&lt;for each=&quot;line in company.footer_used.split(&apos;\n&apos;)&quot;&gt;</text:placeholder></text:p>
<text:p text:style-name="P5"><text:placeholder text:placeholder-type="text">&lt;line&gt;</text:placeholder></text:p>
<text:p text:style-name="P5"><text:placeholder text:placeholder-type="text">&lt;/for&gt;</text:placeholder></text:p>
<text:p text:style-name="P5"><text:placeholder text:placeholder-type="text">&lt;/if&gt;</text:placeholder></text:p>
<text:p text:style-name="P6"><text:page-number text:select-page="current">3</text:page-number>/<text:page-count>3</text:page-count></text:p>
</style:footer>
</style:master-page>
</office:master-styles>
<office:body>
<office:text text:use-soft-page-breaks="true">
<office:forms form:automatic-focus="false" form:apply-design-mode="false"/>
<text:sequence-decls>
<text:sequence-decl text:display-outline-level="0" text:name="Illustration"/>
<text:sequence-decl text:display-outline-level="0" text:name="Table"/>
<text:sequence-decl text:display-outline-level="0" text:name="Text"/>
<text:sequence-decl text:display-outline-level="0" text:name="Drawing"/>
<text:sequence-decl text:display-outline-level="0" text:name="Figure"/>
</text:sequence-decls>
<text:p text:style-name="P17"><text:placeholder text:placeholder-type="text">&lt;for each=&quot;shipment in records&quot;&gt;</text:placeholder></text:p>
<text:p text:style-name="P17"><text:placeholder text:placeholder-type="text">&lt;choose&gt;</text:placeholder></text:p>
<text:p text:style-name="Standard"><text:placeholder text:placeholder-type="text">&lt;when test=&quot;shipment.state in [&apos;draft&apos;, &apos;waiting&apos;]&quot;&gt;</text:placeholder></text:p>
<text:p text:style-name="P20"><text:span text:style-name="T4">Draft </text:span>Picking List</text:p>
<text:p text:style-name="Standard"><text:placeholder text:placeholder-type="text">&lt;/when&gt;</text:placeholder></text:p>
<text:p text:style-name="Standard"><text:placeholder text:placeholder-type="text">&lt;when test=&quot;shipment.state == &apos;assigned&apos;&quot;&gt;</text:placeholder></text:p>
<text:p text:style-name="P20">Picking List No: <text:span text:style-name="T4"><text:placeholder text:placeholder-type="text">&lt;shipment.number&gt;</text:placeholder></text:span></text:p>
<text:p text:style-name="Standard"><text:placeholder text:placeholder-type="text">&lt;/when&gt;</text:placeholder></text:p>
<text:p text:style-name="Standard"><text:placeholder text:placeholder-type="text">&lt;when test=&quot;shipment.state == &apos;cancelled&apos;&quot;&gt;</text:placeholder></text:p>
<text:p text:style-name="P20"><text:span text:style-name="T4">Cancelled </text:span>Picking List</text:p>
<text:p text:style-name="Standard"><text:placeholder text:placeholder-type="text">&lt;/when&gt;</text:placeholder></text:p>
<text:p text:style-name="Standard"><text:placeholder text:placeholder-type="text">&lt;otherwise&gt;</text:placeholder></text:p>
<text:p text:style-name="P19"><text:span text:style-name="T4">Done </text:span>Picking List No: <text:span text:style-name="T4"><text:placeholder text:placeholder-type="text">&lt;shipment.number&gt;</text:placeholder></text:span></text:p>
<text:p text:style-name="Standard"><text:placeholder text:placeholder-type="text">&lt;/otherwise&gt;</text:placeholder></text:p>
<text:p text:style-name="Standard"><text:placeholder text:placeholder-type="text">&lt;/choose&gt;</text:placeholder></text:p>
<text:p text:style-name="Standard"><text:span text:style-name="T1">Reference:</text:span><text:span text:style-name="T3"> </text:span><text:span text:style-name="T3"><text:placeholder text:placeholder-type="text">&lt;shipment.origins or &apos;&apos;&gt;</text:placeholder></text:span><text:span text:style-name="T3"><text:placeholder text:placeholder-type="text">&lt;&apos;, &apos; if (shipment.origins and shipment.reference) else &apos;&apos;&gt;</text:placeholder></text:span><text:span text:style-name="T3"><text:placeholder text:placeholder-type="text">&lt;shipment.reference or &apos;&apos;&gt;</text:placeholder></text:span></text:p>
<text:p text:style-name="Standard"><text:span text:style-name="T1">Customer:</text:span><text:span text:style-name="T3"> </text:span><text:span text:style-name="T3"><text:placeholder text:placeholder-type="text">&lt;shipment.customer.rec_name&gt;</text:placeholder></text:span></text:p>
<text:p text:style-name="Standard"><text:span text:style-name="T1">Planned Date:</text:span><text:span text:style-name="T3"> </text:span><text:span text:style-name="T3"><text:placeholder text:placeholder-type="text">&lt;shipment.planned_date and format_date(shipment.planned_date, user.language) or &apos;&apos;&gt;</text:placeholder></text:span></text:p>
<text:p text:style-name="Standard"><text:span text:style-name="T1">Warehouse:</text:span><text:span text:style-name="T3"> </text:span><text:span text:style-name="T3"><text:placeholder text:placeholder-type="text">&lt;shipment.warehouse.rec_name&gt;</text:placeholder></text:span></text:p>
<table:table table:name="Table1" table:style-name="Table1">
<table:table-column table:style-name="Table1.A" table:number-columns-repeated="2"/>
<table:table-column table:style-name="Table1.C"/>
<table:table-column table:style-name="Table1.D"/>
<table:table-header-rows>
<text:soft-page-break/>
<table:table-row>
<table:table-cell table:style-name="Table1.A1" office:value-type="string">
<text:p text:style-name="P16">From Location</text:p>
</table:table-cell>
<table:table-cell table:style-name="Table1.A1" office:value-type="string">
<text:p text:style-name="P16">To Location</text:p>
</table:table-cell>
<table:table-cell table:style-name="Table1.A1" office:value-type="string">
<text:p text:style-name="P16">Product</text:p>
</table:table-cell>
<table:table-cell table:style-name="Table1.D1" office:value-type="string">
<text:p text:style-name="P16">Quantity</text:p>
</table:table-cell>
</table:table-row>
</table:table-header-rows>
<table:table-row>
<table:table-cell table:style-name="Table1.A2" table:number-columns-spanned="2" office:value-type="string">
<text:p text:style-name="Table_20_Contents"><text:placeholder text:placeholder-type="text">&lt;for each=&quot;move in moves(shipment)&quot;&gt;</text:placeholder></text:p>
</table:table-cell>
<table:covered-table-cell/>
<table:table-cell table:style-name="Table1.C2" office:value-type="string">
<text:p text:style-name="Table_20_Contents"/>
</table:table-cell>
<table:table-cell table:style-name="Table1.D2" office:value-type="string">
<text:p text:style-name="Table_20_Contents"/>
</table:table-cell>
</table:table-row>
<table:table-row>
<table:table-cell table:style-name="Table1.A3" office:value-type="string">
<text:p text:style-name="Table_20_Contents"><text:placeholder text:placeholder-type="text">&lt;move.from_location_name&gt;</text:placeholder></text:p>
</table:table-cell>
<table:table-cell table:style-name="Table1.A3" office:value-type="string">
<text:p text:style-name="Table_20_Contents"><text:placeholder text:placeholder-type="text">&lt;move.to_location_name&gt;</text:placeholder></text:p>
</table:table-cell>
<table:table-cell table:style-name="Table1.C2" office:value-type="string">
<text:p text:style-name="Table_20_Contents"><text:placeholder text:placeholder-type="text">&lt;move.product.rec_name&gt;</text:placeholder></text:p>
</table:table-cell>
<table:table-cell table:style-name="Table1.D2" office:value-type="string">
<text:p text:style-name="P15"><text:placeholder text:placeholder-type="text">&lt;format_number_symbol(move.quantity, user.language, move.unit, digits=move.unit.digits)&gt;</text:placeholder></text:p>
</table:table-cell>
</table:table-row>
<table:table-row>
<table:table-cell table:style-name="Table1.A2" table:number-columns-spanned="2" office:value-type="string">
<text:p text:style-name="Table_20_Contents"><text:placeholder text:placeholder-type="text">&lt;/for&gt;</text:placeholder></text:p>
</table:table-cell>
<table:covered-table-cell/>
<table:table-cell table:style-name="Table1.C2" office:value-type="string">
<text:p text:style-name="Table_20_Contents"/>
</table:table-cell>
<table:table-cell table:style-name="Table1.D2" office:value-type="string">
<text:p text:style-name="Table_20_Contents"/>
</table:table-cell>
</table:table-row>
</table:table>
<text:p text:style-name="Text_20_body"><text:placeholder text:placeholder-type="text">&lt;/for&gt;</text:placeholder></text:p>
</office:text>
</office:body>
</office:document>

1353
modules/stock/product.py Normal file

File diff suppressed because it is too large Load Diff

423
modules/stock/product.xml Normal file
View File

@@ -0,0 +1,423 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<tryton>
<data>
<record model="ir.ui.view" id="product_view_list_stock_move">
<field name="model">product.product</field>
<field name="type">tree</field>
<field name="priority" eval="20"/>
<field name="name">product_list_stock_move</field>
</record>
<record model="ir.action.wizard" id="wizard_recompute_cost_price">
<field name="name">Recompute Cost Price</field>
<field name="wiz_name">product.recompute_cost_price</field>
</record>
<record model="ir.action.keyword"
id="wizard_recompute_cost_price_product_keyword1">
<field name="keyword">form_action</field>
<field name="model">product.product,-1</field>
<field name="action" ref="wizard_recompute_cost_price"/>
</record>
<record model="ir.action.keyword"
id="wizard_recompute_cost_price_template_keyword1">
<field name="keyword">form_action</field>
<field name="model">product.template,-1</field>
<field name="action" ref="wizard_recompute_cost_price"/>
</record>
<record model="ir.action-res.group"
id="wizard_recompute_cost_price-group_product_admin">
<field name="action" ref="wizard_recompute_cost_price"/>
<field name="group" ref="product.group_product_admin"/>
</record>
<record model="ir.ui.view" id="recompute_cost_price_start_view_form">
<field name="model">product.recompute_cost_price.start</field>
<field name="type">form</field>
<field name="name">recompute_cost_price_start_form</field>
</record>
<record model="ir.ui.view" id="product_cost_price_revision_view_form">
<field name="model">product.cost_price.revision</field>
<field name="type">form</field>
<field name="name">product_cost_price_revision_form</field>
</record>
<record model="ir.ui.view" id="product_cost_price_revision_view_list">
<field name="model">product.cost_price.revision</field>
<field name="type">tree</field>
<field name="name">product_cost_price_revision_list</field>
</record>
<record model="ir.action.act_window" id="act_product_cost_price_revision">
<field name="name">Cost Price Revision</field>
<field name="res_model">product.cost_price.revision</field>
<field
name="domain"
eval="[If(Eval('active_ids', []) == [Eval('active_id')], (If(Eval('active_model') == 'product.template', 'template', 'product'), '=', Eval('active_id', -1)), (If(Eval('active_model') == 'product.template', 'template', 'product'), 'in', Eval('active_ids', [])))]"
pyson="1"/>
</record>
<record model="ir.action.act_window.view" id="act_product_cost_price_revision_list_view">
<field name="sequence" eval="10"/>
<field name="view" ref="product_cost_price_revision_view_list"/>
<field name="act_window" ref="act_product_cost_price_revision"/>
</record>
<record model="ir.action.act_window.view" id="act_product_cost_price_revision_form_view">
<field name="sequence" eval="20"/>
<field name="view" ref="product_cost_price_revision_view_form"/>
<field name="act_window" ref="act_product_cost_price_revision"/>
</record>
<record model="ir.action.keyword" id="act_product_cost_price_revision_keyword1">
<field name="keyword">form_relate</field>
<field name="model">product.template,-1</field>
<field name="action" ref="act_product_cost_price_revision"/>
</record>
<record model="ir.action.keyword" id="act_product_cost_price_revision_keyword2">
<field name="keyword">form_relate</field>
<field name="model">product.product,-1</field>
<field name="action" ref="act_product_cost_price_revision"/>
</record>
<record model="ir.rule.group" id="rule_group_product_cost_price_revision_companies">
<field name="name">User in companies</field>
<field name="model">product.cost_price.revision</field>
<field name="global_p" eval="True"/>
</record>
<record model="ir.rule" id="rule_product_cost_price_revision_companies">
<field name="domain"
eval="[('company', 'in', Eval('companies', []))]"
pyson="1"/>
<field name="rule_group" ref="rule_group_product_cost_price_revision_companies"/>
</record>
<record model="ir.model.access" id="access_product_cost_price_revision">
<field name="model">product.cost_price.revision</field>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_product_cost_price_revision_admin">
<field name="model">product.cost_price.revision</field>
<field name="group" ref="product.group_product_admin"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_delete" eval="True"/>
</record>
<record model="ir.ui.view" id="product_modify_cost_price_start_form">
<field name="model">product.modify_cost_price.start</field>
<field name="type">form</field>
<field name="name">product_modify_cost_price_start_form</field>
</record>
<record model="ir.action.wizard" id="wizard_product_modify_cost_price">
<field name="name">Modify Cost Price</field>
<field name="wiz_name">product.modify_cost_price</field>
</record>
<record model="ir.action.keyword" id="product_modify_cost_price_keyword1">
<field name="keyword">form_action</field>
<field name="model">product.product,-1</field>
<field name="action" ref="wizard_product_modify_cost_price"/>
</record>
<record model="ir.action.keyword" id="product_modify_cost_price_keyword2">
<field name="keyword">form_action</field>
<field name="model">product.template,-1</field>
<field name="action" ref="wizard_product_modify_cost_price"/>
</record>
<record model="ir.action-res.group" id="wizard_product_modify_cost_price-group_account">
<field name="action" ref="wizard_product_modify_cost_price"/>
<field name="group" ref="product.group_product_admin" />
</record>
<record model="ir.ui.view" id="location_quantity_view_tree">
<field name="model">stock.location</field>
<field name="type">tree</field>
<field name="field_childs">childs</field>
<field name="priority" eval="20"/>
<field name="name">location_quantity_tree</field>
</record>
<record model="ir.ui.view" id="location_quantity_view_form">
<field name="model">stock.location</field>
<field name="type">form</field>
<field name="priority" eval="20"/>
<field name="name">location_quantity_form</field>
</record>
<record model="ir.ui.view" id="location_quantity_view_list">
<field name="model">stock.location</field>
<field name="type">tree</field>
<field name="priority" eval="20"/>
<field name="name">location_quantity_tree</field>
</record>
<record model="ir.action.act_window" id="act_location_quantity_tree">
<field name="name">Stock Locations Tree</field>
<field name="res_model">stock.location</field>
<field name="domain" eval="[('parent', '=', None)]" pyson="1"/>
<field name="search_value" eval="[('type', 'in', ['storage', 'view', 'warehouse'])]" pyson="1"/>
<field name="context" pyson="1"
eval="If(Eval('active_model') == 'product.template', {'product_template': Eval('active_id'), 'stock_skip_warehouse': False}, {'product': Eval('active_id'), 'stock_skip_warehouse': False})"/>
<field name="context_model">product.by_location.context</field>
</record>
<record model="ir.action.act_window.view" id="act_location_quantity_tree_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="location_quantity_view_tree"/>
<field name="act_window" ref="act_location_quantity_tree"/>
</record>
<record model="ir.action.act_window.view" id="act_location_quantity_tree_view2">
<field name="sequence" eval="20"/>
<field name="view" ref="location_quantity_view_form"/>
<field name="act_window" ref="act_location_quantity_tree"/>
</record>
<record model="ir.action.keyword"
id="act_location_quantity_tree_keyword1">
<field name="keyword">form_relate</field>
<field name="model">product.product,-1</field>
<field name="action" ref="act_location_quantity_tree"/>
</record>
<record model="ir.action.keyword"
id="act_location_quantity_tree_keyword2">
<field name="keyword">form_relate</field>
<field name="model">product.template,-1</field>
<field name="action" ref="act_location_quantity_tree"/>
</record>
<record model="ir.action-res.group"
id="act_location-quantity_tree-group_stock">
<field name="action" ref="act_location_quantity_tree"/>
<field name="group" ref="group_stock"/>
</record>
<record model="ir.action.act_window" id="act_location_quantity_list">
<field name="name">Stock Locations List</field>
<field name="res_model">stock.location</field>
<field name="search_value" eval="[('type', '=', 'storage'), ['OR', ('quantity', '!=', 0), ('forecast_quantity', '!=', 0)]]" pyson="1"/>
<field name="context" pyson="1"
eval="If(Eval('active_model') == 'product.template', {'product_template': Eval('active_id'), 'with_childs': False, 'stock_skip_warehouse': False}, {'product': Eval('active_id'), 'with_childs': False, 'stock_skip_warehouse': False})"/>
<field name="context_model">product.by_location.context</field>
</record>
<record model="ir.action.act_window.view" id="act_location_quantity_list_view">
<field name="sequence" eval="10"/>
<field name="view" ref="location_quantity_view_list"/>
<field name="act_window" ref="act_location_quantity_list"/>
</record>
<record model="ir.action.keyword"
id="act_location_quantity_list_keyword1">
<field name="keyword">form_relate</field>
<field name="model">product.product,-1</field>
<field name="action" ref="act_location_quantity_list"/>
</record>
<record model="ir.action.keyword"
id="act_location_quantity_list_keyword2">
<field name="keyword">form_relate</field>
<field name="model">product.template,-1</field>
<field name="action" ref="act_location_quantity_list"/>
</record>
<record model="ir.action-res.group"
id="act_location-quantity_list-group_stock">
<field name="action" ref="act_location_quantity_list"/>
<field name="group" ref="group_stock"/>
</record>
<record model="ir.ui.view" id="product_by_location_context_view_form">
<field name="model">product.by_location.context</field>
<field name="type">form</field>
<field name="name">product_by_location_context_form</field>
</record>
<record model="ir.action.wizard" id="wizard_open_product_quantities_by_warehouse">
<field name="name">Products Quantities By Warehouse</field>
<field name="wiz_name">stock.product_quantities_warehouse.open</field>
</record>
<record model="ir.action.keyword" id="wizard_open_product_quantities_by_warehouse_keyword_shipment_in_return">
<field name="keyword">form_relate</field>
<field name="model">stock.shipment.in.return,-1</field>
<field name="action" ref="wizard_open_product_quantities_by_warehouse"/>
</record>
<record model="ir.action.keyword" id="wizard_open_product_quantities_by_warehouse_keyword_shipment_out">
<field name="keyword">form_relate</field>
<field name="model">stock.shipment.out,-1</field>
<field name="action" ref="wizard_open_product_quantities_by_warehouse"/>
</record>
<record model="ir.action.keyword" id="wizard_open_product_quantities_by_warehouse_keyword_shipment_internal">
<field name="keyword">form_relate</field>
<field name="model">stock.shipment.internal,-1</field>
<field name="action" ref="wizard_open_product_quantities_by_warehouse"/>
</record>
<record model="ir.ui.view" id="product_quantities_warehouse_view_graph">
<field name="model">stock.product_quantities_warehouse</field>
<field name="type">graph</field>
<field name="name">product_quantities_warehouse_graph</field>
</record>
<record model="ir.ui.view" id="product_quantities_warehouse_view_list">
<field name="model">stock.product_quantities_warehouse</field>
<field name="type">tree</field>
<field name="name">product_quantities_warehouse_list</field>
</record>
<record model="ir.action.act_window"
id="act_product_quantities_warehouse">
<field name="name">Stock Quantities By Warehouse</field>
<field name="res_model">stock.product_quantities_warehouse</field>
<field name="context" pyson="1"
eval="If(Eval('active_model') == 'product.template', {'product_template': Eval('active_ids')}, {'product': Eval('active_ids')})"/>
<field name="search_value" eval="[('date', '>=', Date())]"
pyson="1"/>
<field name="context_model">stock.product_quantities_warehouse.context</field>
</record>
<record model="ir.action.act_window.view"
id="act_product_quantities_warehouse_graph_view">
<field name="sequence" eval="10"/>
<field name="view" ref="product_quantities_warehouse_view_graph"/>
<field name="act_window" ref="act_product_quantities_warehouse"/>
</record>
<record model="ir.action.act_window.view"
id="act_product_quantities_warehouse_list_view">
<field name="sequence" eval="20"/>
<field name="view" ref="product_quantities_warehouse_view_list"/>
<field name="act_window" ref="act_product_quantities_warehouse"/>
</record>
<record model="ir.rule.group" id="rule_group_product_quantities_warehouse_companies">
<field name="name">User in companies</field>
<field name="model">stock.product_quantities_warehouse</field>
<field name="global_p" eval="True"/>
</record>
<record model="ir.rule" id="rule_product_quantities_warehouse_companies">
<field
name="domain"
eval="[('company', 'in', Eval('companies', []))]"
pyson="1"/>
<field name="rule_group" ref="rule_group_product_quantities_warehouse_companies"/>
</record>
<record model="ir.model.access" id="access_product_quantities_warehouse">
<field name="model">stock.product_quantities_warehouse</field>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_product_quantities_warehouse_group_stock">
<field name="model">stock.product_quantities_warehouse</field>
<field name="group" ref="group_stock"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.ui.view" id="product_quantities_warehouse_context_view_form">
<field
name="model">stock.product_quantities_warehouse.context</field>
<field name="type">form</field>
<field name="name">product_quantities_warehouse_context_form</field>
</record>
<record model="ir.action.keyword"
id="act_product_quantities_warehouse_keyword1">
<field name="keyword">form_relate</field>
<field name="model">product.product,-1</field>
<field name="action" ref="act_product_quantities_warehouse"/>
</record>
<record model="ir.action.keyword"
id="act_product_quantities_warehouse_keyword2">
<field name="keyword">form_relate</field>
<field name="model">product.template,-1</field>
<field name="action" ref="act_product_quantities_warehouse"/>
</record>
<record model="ir.action-res.group"
id="act_product_quantities_warehouse-group_stock">
<field name="action" ref="act_product_quantities_warehouse"/>
<field name="group" ref="group_stock"/>
</record>
<record model="ir.action.wizard" id="wizard_open_product_quantities_by_warehouse_move">
<field name="name">Stock Moves</field>
<field name="wiz_name">stock.product_quantities_warehouse.move.open</field>
</record>
<record model="ir.action.keyword" id="wizard_open_product_quantities_by_warehouse_move_keyword1">
<field name="keyword">tree_open</field>
<field name="model">stock.product_quantities_warehouse,-1</field>
<field name="action" ref="wizard_open_product_quantities_by_warehouse_move"/>
</record>
<record model="ir.ui.view" id="product_quantities_warehouse_move_view_list">
<field name="model">stock.product_quantities_warehouse.move</field>
<field name="type">tree</field>
<field name="name">product_quantities_warehouse_move_list</field>
</record>
<record model="ir.action.act_window" id="act_product_quantities_warehouse_move">
<field name="name">Stock Moves By Warehouse</field>
<field name="res_model">stock.product_quantities_warehouse.move</field>
<field
name="context"
eval="If(Eval('active_model') == 'product.template', {'product_template': Eval('active_ids')}, {'product': Eval('active_ids')})"
pyson="1"/>
<field name="search_value" eval="[('date', '>=', Date())]" pyson="1"/>
<field name="context_model">stock.product_quantities_warehouse.context</field>
</record>
<record model="ir.action.act_window.view" id="act_product_quantities_warehouse_move_list_view">
<field name="sequence" eval="10"/>
<field name="view" ref="product_quantities_warehouse_move_view_list"/>
<field name="act_window" ref="act_product_quantities_warehouse_move"/>
</record>
<record model="ir.action.keyword" id="act_product_quantities_warehouse_move_keyword1">
<field name="keyword">form_relate</field>
<field name="model">product.product,-1</field>
<field name="action" ref="act_product_quantities_warehouse_move"/>
</record>
<record model="ir.action.keyword" id="act_product_quantities_warehouse_move_keyword2">
<field name="keyword">form_relate</field>
<field name="model">product.template,-1</field>
<field name="action" ref="act_product_quantities_warehouse_move"/>
</record>
<record model="ir.action-res.group" id="act_product_quantities_warehouse_move-group_stock">
<field name="action" ref="act_product_quantities_warehouse_move"/>
<field name="group" ref="group_stock"/>
</record>
<record model="ir.rule.group" id="rule_group_product_quantities_warehouse_move_companies">
<field name="name">User in companies</field>
<field name="model">stock.product_quantities_warehouse.move</field>
<field name="global_p" eval="True"/>
</record>
<record model="ir.rule" id="rule_product_quantities_warehouse_move_companies">
<field
name="domain"
eval="[('company', 'in', Eval('companies', []))]"
pyson="1"/>
<field name="rule_group" ref="rule_group_product_quantities_warehouse_move_companies"/>
</record>
<record model="ir.model.access" id="access_product_quantities_warehouse_move">
<field name="model">stock.product_quantities_warehouse.move</field>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_product_quantities_warehouse_move_group_stock">
<field name="model">stock.product_quantities_warehouse.move</field>
<field name="group" ref="group_stock"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
</data>
<data noupdate="1">
<record model="ir.cron" id="cron_recompute_cost_price_from_moves">
<field name="method">product.product|recompute_cost_price_from_moves</field>
<field name="interval_number" eval="1"/>
<field name="interval_type">days</field>
</record>
</data>
</tryton>

51
modules/stock/res.py Normal file
View File

@@ -0,0 +1,51 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from trytond.model import fields
from trytond.pool import Pool, PoolMeta
from trytond.transaction import Transaction
class User(metaclass=PoolMeta):
__name__ = 'res.user'
warehouse = fields.Many2One('stock.location',
"Current Warehouse",
domain=[('type', '=', 'warehouse')],
help="The warehouse that the user works at.")
@classmethod
def __setup__(cls):
super().__setup__()
cls._context_fields.insert(0, 'warehouse')
@classmethod
def _get_preferences(cls, user, context_only=False):
preferences = super()._get_preferences(user, context_only=context_only)
if user.warehouse:
preferences['warehouse'] = user.warehouse.id
return preferences
def get_status_bar(self, name):
pool = Pool()
Location = pool.get('stock.location')
status = super().get_status_bar(name)
if (self.warehouse
and len(Location.search([('type', '=', 'warehouse')])) > 1):
status += ' - %s' % self.warehouse.rec_name
return status
@classmethod
def read(cls, ids, fields_names):
context = Transaction().context
user_id = Transaction().user
if user_id == 0 and 'user' in Transaction().context:
user_id = Transaction().context['user']
result = super().read(ids, fields_names)
if ('warehouse' in fields_names
and context.get('warehouse')
and user_id in ids):
for values in result:
if values['id'] == user_id:
values['warehouse'] = context['warehouse']
return result

17
modules/stock/res.xml Normal file
View File

@@ -0,0 +1,17 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<tryton>
<data>
<record model="ir.ui.view" id="user_view_form">
<field name="model">res.user</field>
<field name="inherit" ref="res.user_view_form"/>
<field name="name">user_form</field>
</record>
<record model="ir.ui.view" id="user_view_form_preferences">
<field name="model">res.user</field>
<field name="inherit" ref="res.user_view_form_preferences"/>
<field name="name">user_form_preferences</field>
</record>
</data>
</tryton>

3169
modules/stock/shipment.py Normal file

File diff suppressed because it is too large Load Diff

973
modules/stock/shipment.xml Normal file
View File

@@ -0,0 +1,973 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<tryton>
<data>
<menuitem
name="Shipments"
parent="menu_stock"
sequence="10"
id="menu_shipment"/>
<record model="ir.ui.view" id="shipment_in_view_form">
<field name="model">stock.shipment.in</field>
<field name="type">form</field>
<field name="name">shipment_in_form</field>
</record>
<record model="ir.ui.view" id="shipment_in_view_tree">
<field name="model">stock.shipment.in</field>
<field name="type">tree</field>
<field name="name">shipment_in_tree</field>
</record>
<record model="ir.action.act_window" id="act_shipment_in_form">
<field name="name">Supplier Shipments</field>
<field name="res_model">stock.shipment.in</field>
<field name="search_value"></field>
</record>
<record model="ir.action.act_window.view"
id="act_shipment_in_form_view1">
<field name="sequence" eval="1"/>
<field name="view" ref="shipment_in_view_tree"/>
<field name="act_window" ref="act_shipment_in_form"/>
</record>
<record model="ir.action.act_window.view"
id="act_shipment_in_form_view2">
<field name="sequence" eval="2"/>
<field name="view" ref="shipment_in_view_form"/>
<field name="act_window" ref="act_shipment_in_form"/>
</record>
<record model="ir.action.act_window.domain" id="act_shipment_in_form_domain_draft">
<field name="name">Draft</field>
<field name="sequence" eval="10"/>
<field name="domain" eval="[('state', '=', 'draft')]" pyson="1"/>
<field name="count" eval="True"/>
<field name="act_window" ref="act_shipment_in_form"/>
</record>
<record model="ir.action.act_window.domain" id="act_shipment_in_form_domain_received">
<field name="name">Received</field>
<field name="sequence" eval="20"/>
<field name="domain" eval="[('state', '=', 'received')]" pyson="1"/>
<field name="count" eval="True"/>
<field name="act_window" ref="act_shipment_in_form"/>
</record>
<record model="ir.action.act_window.domain" id="act_shipment_in_form_domain_all">
<field name="name">All</field>
<field name="sequence" eval="9999"/>
<field name="domain"></field>
<field name="act_window" ref="act_shipment_in_form"/>
</record>
<menuitem
parent="menu_shipment"
action="act_shipment_in_form"
sequence="10"
id="menu_shipment_in_form"/>
<record model="ir.ui.view" id="shipment_in_return_view_form">
<field name="model">stock.shipment.in.return</field>
<field name="type">form</field>
<field name="name">shipment_in_return_form</field>
</record>
<record model="ir.ui.view" id="shipment_in_return_view_tree">
<field name="model">stock.shipment.in.return</field>
<field name="type">tree</field>
<field name="name">shipment_in_return_tree</field>
</record>
<record model="ir.action.act_window" id="act_shipment_in_return_form">
<field name="name">Supplier Returns</field>
<field name="res_model">stock.shipment.in.return</field>
<field name="search_value"></field>
</record>
<record model="ir.action.act_window.view"
id="act_shipment_in_return_form_view1">
<field name="sequence" eval="1"/>
<field name="view" ref="shipment_in_return_view_tree"/>
<field name="act_window" ref="act_shipment_in_return_form"/>
</record>
<record model="ir.action.act_window.view"
id="act_shipment_in_return_form_view2">
<field name="sequence" eval="2"/>
<field name="view" ref="shipment_in_return_view_form"/>
<field name="act_window" ref="act_shipment_in_return_form"/>
</record>
<record model="ir.action.act_window.domain"
id="act_shipment_in_return_form_domain_draft">
<field name="name">Draft</field>
<field name="sequence" eval="10"/>
<field name="domain" eval="[('state', '=', 'draft')]" pyson="1"/>
<field name="count" eval="True"/>
<field name="act_window" ref="act_shipment_in_return_form"/>
</record>
<record model="ir.action.act_window.domain"
id="act_shipment_in_return_form_domain_waiting">
<field name="name">Waiting</field>
<field name="sequence" eval="20"/>
<field name="domain" eval="[('state', '=', 'waiting')]" pyson="1"/>
<field name="count" eval="True"/>
<field name="act_window" ref="act_shipment_in_return_form"/>
</record>
<record model="ir.action.act_window.domain"
id="act_shipment_in_return_form_domain_available">
<field name="name">Partially Assigned</field>
<field name="sequence" eval="30"/>
<field name="domain" eval="[('partially_assigned', '=', 'True')]" pyson="1"/>
<field name="count" eval="True"/>
<field name="act_window" ref="act_shipment_in_return_form"/>
</record>
<record model="ir.action.act_window.domain"
id="act_shipment_in_return_form_domain_assigned">
<field name="name">Assigned</field>
<field name="sequence" eval="40"/>
<field name="domain" eval="[('state', '=', 'assigned')]" pyson="1"/>
<field name="count" eval="True"/>
<field name="act_window" ref="act_shipment_in_return_form"/>
</record>
<record model="ir.action.act_window.domain"
id="act_shipment_in_return_form_domain_all">
<field name="name">All</field>
<field name="sequence" eval="9999"/>
<field name="domain"></field>
<field name="act_window" ref="act_shipment_in_return_form"/>
</record>
<menuitem
parent="menu_shipment"
action="act_shipment_in_return_form"
sequence="10"
id="menu_shipment_in_return_form"/>
<record model="ir.ui.view" id="shipment_out_view_form">
<field name="model">stock.shipment.out</field>
<field name="type">form</field>
<field name="name">shipment_out_form</field>
</record>
<record model="ir.ui.view" id="shipment_out_view_tree">
<field name="model">stock.shipment.out</field>
<field name="type">tree</field>
<field name="name">shipment_out_tree</field>
</record>
<record model="ir.action.act_window" id="act_shipment_out_form">
<field name="name">Customer Shipments</field>
<field name="res_model">stock.shipment.out</field>
<field name="search_value"></field>
</record>
<record model="ir.action.act_window.view"
id="act_shipment_out_form_view1">
<field name="sequence" eval="1"/>
<field name="view" ref="shipment_out_view_tree"/>
<field name="act_window" ref="act_shipment_out_form"/>
</record>
<record model="ir.action.act_window.view"
id="act_shipment_out_form_view2">
<field name="sequence" eval="2"/>
<field name="view" ref="shipment_out_view_form"/>
<field name="act_window" ref="act_shipment_out_form"/>
</record>
<record model="ir.action.act_window.domain" id="act_shipment_out_form_domain_draft">
<field name="name">Draft</field>
<field name="sequence" eval="10"/>
<field name="domain" eval="[('state', '=', 'draft')]" pyson="1"/>
<field name="count" eval="True"/>
<field name="act_window" ref="act_shipment_out_form"/>
</record>
<record model="ir.action.act_window.domain" id="act_shipment_out_form_domain_waiting">
<field name="name">Waiting</field>
<field name="sequence" eval="20"/>
<field name="domain" eval="[('state', '=', 'waiting')]" pyson="1"/>
<field name="count" eval="True"/>
<field name="act_window" ref="act_shipment_out_form"/>
</record>
<record model="ir.action.act_window.domain" id="act_shipment_out_form_domain_available">
<field name="name">Partially Assigned</field>
<field name="sequence" eval="30"/>
<field name="domain" eval="[('partially_assigned', '=', True)]" pyson="1"/>
<field name="count" eval="True"/>
<field name="act_window" ref="act_shipment_out_form"/>
</record>
<record model="ir.action.act_window.domain" id="act_shipment_out_form_domain_assigned">
<field name="name">Assigned</field>
<field name="sequence" eval="40"/>
<field name="domain" eval="[('state', '=', 'assigned')]" pyson="1"/>
<field name="count" eval="True"/>
<field name="act_window" ref="act_shipment_out_form"/>
</record>
<record model="ir.action.act_window.domain" id="act_shipment_out_form_domain_picked">
<field name="name">Picked</field>
<field name="sequence" eval="50"/>
<field name="domain" eval="[('state', '=', 'picked')]" pyson="1"/>
<field name="count" eval="True"/>
<field name="act_window" ref="act_shipment_out_form"/>
</record>
<record model="ir.action.act_window.domain" id="act_shipment_out_form_domain_packed">
<field name="name">Packed</field>
<field name="sequence" eval="60"/>
<field name="domain" eval="[('state', '=', 'packed')]" pyson="1"/>
<field name="count" eval="True"/>
<field name="act_window" ref="act_shipment_out_form"/>
</record>
<record model="ir.action.act_window.domain" id="act_shipment_out_form_domain_shipped">
<field name="name">Shipped</field>
<field name="sequence" eval="70"/>
<field name="domain" eval="[('state', '=', 'shipped')]" pyson="1"/>
<field name="count" eval="True"/>
<field name="act_window" ref="act_shipment_out_form"/>
</record>
<record model="ir.action.act_window.domain" id="act_shipment_out_form_domain_all">
<field name="name">All</field>
<field name="sequence" eval="9999"/>
<field name="domain"></field>
<field name="act_window" ref="act_shipment_out_form"/>
</record>
<menuitem
parent="menu_shipment"
action="act_shipment_out_form"
sequence="10"
id="menu_shipment_out_form"/>
<record model="ir.ui.view" id="shipment_internal_view_form">
<field name="model">stock.shipment.internal</field>
<field name="type">form</field>
<field name="name">shipment_internal_form</field>
</record>
<record model="ir.ui.view" id="shipment_internal_view_tree">
<field name="model">stock.shipment.internal</field>
<field name="type">tree</field>
<field name="name">shipment_internal_tree</field>
</record>
<record model="ir.action.act_window" id="act_shipment_internal_form">
<field name="name">Internal Shipments</field>
<field name="res_model">stock.shipment.internal</field>
<field name="search_value"></field>
</record>
<record model="ir.action.act_window.view"
id="act_shipment_internal_form_view1">
<field name="sequence" eval="1"/>
<field name="view" ref="shipment_internal_view_tree"/>
<field name="act_window" ref="act_shipment_internal_form"/>
</record>
<record model="ir.action.act_window.view"
id="act_shipment_internal_form_view2">
<field name="sequence" eval="2"/>
<field name="view" ref="shipment_internal_view_form"/>
<field name="act_window" ref="act_shipment_internal_form"/>
</record>
<record model="ir.action.act_window.domain"
id="act_shipment_internal_form_domain_requests">
<field name="name">Requests</field>
<field name="sequence" eval="5"/>
<field name="domain" eval="[('state', '=', 'request')]" pyson="1"/>
<field name="count" eval="True"/>
<field name="act_window" ref="act_shipment_internal_form"/>
</record>
<record model="ir.action.act_window.domain"
id="act_shipment_internal_form_domain_draft">
<field name="name">Draft</field>
<field name="sequence" eval="10"/>
<field name="domain" eval="[('state', '=', 'draft')]" pyson="1"/>
<field name="count" eval="True"/>
<field name="act_window" ref="act_shipment_internal_form"/>
</record>
<record model="ir.action.act_window.domain"
id="act_shipment_internal_form_domain_waiting">
<field name="name">Waiting</field>
<field name="sequence" eval="20"/>
<field name="domain" eval="[('state', '=', 'waiting')]" pyson="1"/>
<field name="count" eval="True"/>
<field name="act_window" ref="act_shipment_internal_form"/>
</record>
<record model="ir.action.act_window.domain"
id="act_shipment_internal_form_domain_available">
<field name="name">Partially Assigned</field>
<field name="sequence" eval="30"/>
<field name="domain" eval="[('partially_assigned', '=', True)]" pyson="1"/>
<field name="count" eval="True"/>
<field name="act_window" ref="act_shipment_internal_form"/>
</record>
<record model="ir.action.act_window.domain"
id="act_shipment_internal_form_domain_assigned">
<field name="name">Assigned</field>
<field name="sequence" eval="40"/>
<field name="domain" eval="[('state', '=', 'assigned')]" pyson="1"/>
<field name="count" eval="True"/>
<field name="act_window" ref="act_shipment_internal_form"/>
</record>
<record model="ir.action.act_window.domain" id="act_shipment_internal_form_domain_packed">
<field name="name">Packed</field>
<field name="sequence" eval="50"/>
<field name="domain" eval="[('state', '=', 'packed')]" pyson="1"/>
<field name="count" eval="True"/>
<field name="act_window" ref="act_shipment_internal_form"/>
</record>
<record model="ir.action.act_window.domain"
id="act_shipment_internal_form_domain_shipped">
<field name="name">Shipped</field>
<field name="sequence" eval="60"/>
<field name="domain" eval="[('state', '=', 'shipped')]" pyson="1"/>
<field name="act_window" ref="act_shipment_internal_form"/>
</record>
<record model="ir.action.act_window.domain"
id="act_shipment_internal_form_domain_all">
<field name="name">All</field>
<field name="sequence" eval="9999"/>
<field name="domain"></field>
<field name="act_window" ref="act_shipment_internal_form"/>
</record>
<menuitem
parent="menu_shipment"
action="act_shipment_internal_form"
sequence="20"
id="menu_shipment_internal_form"/>
<record model="ir.ui.view" id="shipment_out_return_view_form">
<field name="model">stock.shipment.out.return</field>
<field name="type">form</field>
<field name="name">shipment_out_return_form</field>
</record>
<record model="ir.ui.view" id="shipment_out_return_view_tree">
<field name="model">stock.shipment.out.return</field>
<field name="type">tree</field>
<field name="name">shipment_out_return_tree</field>
</record>
<record model="ir.action.act_window" id="act_shipment_out_return_form">
<field name="name">Customer Returns</field>
<field name="res_model">stock.shipment.out.return</field>
<field name="search_value"></field>
</record>
<record model="ir.action.act_window.view"
id="act_shipment_out_return_form_view1">
<field name="sequence" eval="1"/>
<field name="view" ref="shipment_out_return_view_tree"/>
<field name="act_window" ref="act_shipment_out_return_form"/>
</record>
<record model="ir.action.act_window.view"
id="act_shipment_out_return_form_view2">
<field name="sequence" eval="2"/>
<field name="view" ref="shipment_out_return_view_form"/>
<field name="act_window" ref="act_shipment_out_return_form"/>
</record>
<record model="ir.action.act_window.domain"
id="act_shipment_out_return_form_domain_draft">
<field name="name">Draft</field>
<field name="sequence" eval="10"/>
<field name="domain" eval="[('state', '=', 'draft')]" pyson="1"/>
<field name="count" eval="True"/>
<field name="act_window" ref="act_shipment_out_return_form"/>
</record>
<record model="ir.action.act_window.domain"
id="act_shipment_out_return_form_domain_received">
<field name="name">Received</field>
<field name="sequence" eval="20"/>
<field name="domain" eval="[('state', '=', 'received')]" pyson="1"/>
<field name="count" eval="True"/>
<field name="act_window" ref="act_shipment_out_return_form"/>
</record>
<record model="ir.action.act_window.domain"
id="act_shipment_out_return_form_domain_all">
<field name="name">All</field>
<field name="sequence" eval="9999"/>
<field name="domain"></field>
<field name="act_window" ref="act_shipment_out_return_form"/>
</record>
<menuitem
parent="menu_shipment"
action="act_shipment_out_return_form"
sequence="10"
id="menu_shipment_out_return_form"/>
<record model="ir.action.report" id="report_shipment_out_delivery_note">
<field name="name">Delivery Note</field>
<field name="model">stock.shipment.out</field>
<field name="report_name">stock.shipment.out.delivery_note</field>
<field name="report">stock/delivery_note.fodt</field>
</record>
<record model="ir.action.keyword"
id="report_shipment_out_delivery_note_keyword">
<field name="keyword">form_print</field>
<field name="model">stock.shipment.out,-1</field>
<field name="action" ref="report_shipment_out_delivery_note"/>
</record>
<record model="ir.action.report" id="report_shipment_out_picking_list">
<field name="name">Picking List</field>
<field name="model">stock.shipment.out</field>
<field name="report_name">stock.shipment.out.picking_list</field>
<field name="report">stock/picking_list.fodt</field>
</record>
<record model="ir.action.keyword"
id="report_shipment_out_picking_list_keyword">
<field name="keyword">form_print</field>
<field name="model">stock.shipment.out,-1</field>
<field name="action" ref="report_shipment_out_picking_list"/>
</record>
<record model="ir.action.report" id="report_shipment_in_restocking_list">
<field name="name">Restocking List</field>
<field name="model">stock.shipment.in</field>
<field name="report_name">stock.shipment.in.restocking_list</field>
<field name="report">stock/supplier_restocking_list.fodt</field>
</record>
<record model="ir.action.keyword"
id="report_shipment_in_restocking_list_keyword">
<field name="keyword">form_print</field>
<field name="model">stock.shipment.in,-1</field>
<field name="action" ref="report_shipment_in_restocking_list"/>
</record>
<record model="ir.action.report" id="report_shipment_out_return_restocking_list">
<field name="name">Restocking List</field>
<field name="model">stock.shipment.out.return</field>
<field name="report_name">stock.shipment.out.return.restocking_list</field>
<field name="report">stock/customer_return_restocking_list.fodt</field>
</record>
<record model="ir.action.keyword"
id="report_shipment_out_return_restocking_list_keyword">
<field name="keyword">form_print</field>
<field name="model">stock.shipment.out.return,-1</field>
<field name="action" ref="report_shipment_out_return_restocking_list"/>
</record>
<record model="ir.action.report" id="report_shipment_internal">
<field name="name">Internal Shipment</field>
<field name="model">stock.shipment.internal</field>
<field name="report_name">stock.shipment.internal.report</field>
<field name="report">stock/internal_shipment.fodt</field>
</record>
<record model="ir.action.keyword"
id="report_shipment_internal_keyword">
<field name="keyword">form_print</field>
<field name="model">stock.shipment.internal,-1</field>
<field name="action" ref="report_shipment_internal"/>
</record>
<!-- Sequence shipment out -->
<record model="ir.sequence.type" id="sequence_type_shipment_out">
<field name="name">Customer Shipment</field>
</record>
<record model="ir.sequence.type-res.group"
id="sequence_type_shipment_out_group_admin">
<field name="sequence_type" ref="sequence_type_shipment_out"/>
<field name="group" ref="res.group_admin"/>
</record>
<record model="ir.sequence.type-res.group"
id="sequence_type_shipment_out_group_stock_admin">
<field name="sequence_type" ref="sequence_type_shipment_out"/>
<field name="group" ref="group_stock_admin"/>
</record>
<record model="ir.sequence" id="sequence_shipment_out">
<field name="name">Customer Shipment</field>
<field name="sequence_type" ref="sequence_type_shipment_out"/>
</record>
<record model="ir.sequence.type" id="sequence_type_shipment_in">
<field name="name">Supplier Shipment</field>
</record>
<record model="ir.sequence.type-res.group"
id="sequence_type_shipment_in_group_admin">
<field name="sequence_type" ref="sequence_type_shipment_in"/>
<field name="group" ref="res.group_admin"/>
</record>
<record model="ir.sequence.type-res.group"
id="sequence_type_shipment_in_group_stock_admin">
<field name="sequence_type" ref="sequence_type_shipment_in"/>
<field name="group" ref="group_stock_admin"/>
</record>
<record model="ir.sequence" id="sequence_shipment_in">
<field name="name">Supplier Shipment</field>
<field name="sequence_type" ref="sequence_type_shipment_in"/>
</record>
<record model="ir.sequence.type" id="sequence_type_shipment_internal">
<field name="name">Internal Shipment</field>
</record>
<record model="ir.sequence.type-res.group"
id="sequence_type_shipment_internal_group_admin">
<field name="sequence_type" ref="sequence_type_shipment_internal"/>
<field name="group" ref="res.group_admin"/>
</record>
<record model="ir.sequence.type-res.group"
id="sequence_type_shipment_internal_group_stock_admin">
<field name="sequence_type" ref="sequence_type_shipment_internal"/>
<field name="group" ref="group_stock_admin"/>
</record>
<record model="ir.sequence" id="sequence_shipment_internal">
<field name="name">Internal Shipment</field>
<field name="sequence_type" ref="sequence_type_shipment_internal"/>
</record>
<record model="ir.sequence.type" id="sequence_type_shipment_out_return">
<field name="name">Customer Return Shipment</field>
</record>
<record model="ir.sequence.type-res.group"
id="sequence_type_shipment_out_return_group_admin">
<field name="sequence_type" ref="sequence_type_shipment_out_return"/>
<field name="group" ref="res.group_admin"/>
</record>
<record model="ir.sequence.type-res.group"
id="sequence_type_shipment_out_return_group_stock_admin">
<field name="sequence_type" ref="sequence_type_shipment_out_return"/>
<field name="group" ref="group_stock_admin"/>
</record>
<record model="ir.sequence" id="sequence_shipment_out_return">
<field name="name">Customer Return Shipment</field>
<field name="sequence_type" ref="sequence_type_shipment_out_return"/>
</record>
<!-- Sequence shipment in return -->
<record model="ir.sequence.type" id="sequence_type_shipment_in_return">
<field name="name">Supplier Return Shipment</field>
</record>
<record model="ir.sequence.type-res.group"
id="sequence_type_shipment_in_return_group_admin">
<field name="sequence_type" ref="sequence_type_shipment_in_return"/>
<field name="group" ref="res.group_admin"/>
</record>
<record model="ir.sequence.type-res.group"
id="sequence_type_shipment_in_return_group_stock_admin">
<field name="sequence_type" ref="sequence_type_shipment_in_return"/>
<field name="group" ref="group_stock_admin"/>
</record>
<record model="ir.sequence" id="sequence_shipment_in_return">
<field name="name">Supplier Return Shipment</field>
<field name="sequence_type" ref="sequence_type_shipment_in_return"/>
</record>
<record model="ir.model.access" id="access_shipment_in">
<field name="model">stock.shipment.in</field>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_shipment_in_group_stock">
<field name="model">stock.shipment.in</field>
<field name="group" ref="group_stock"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_shipment_in_group_stock_admin">
<field name="model">stock.shipment.in</field>
<field name="group" ref="group_stock_admin"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_delete" eval="True"/>
</record>
<record model="ir.model.button" id="shipment_in_cancel_button">
<field name="model">stock.shipment.in</field>
<field name="name">cancel</field>
<field name="string">Cancel</field>
<field name="confirm">Are you sure you want to cancel the shipment?</field>
</record>
<record model="ir.model.button" id="shipment_in_draft_button">
<field name="model">stock.shipment.in</field>
<field name="name">draft</field>
<field name="string">Reset to Draft</field>
</record>
<record model="ir.model.button" id="shipment_in_receive_button">
<field name="model">stock.shipment.in</field>
<field name="name">receive</field>
<field name="string">Receive</field>
<field name="confirm">Are you sure you want to receive the shipment?</field>
</record>
<record model="ir.model.button" id="shipment_in_done_button">
<field name="model">stock.shipment.in</field>
<field name="name">do</field>
<field name="string">Complete</field>
</record>
<record model="ir.model.access" id="access_shipment_out">
<field name="model">stock.shipment.out</field>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_shipment_out_group_stock">
<field name="model">stock.shipment.out</field>
<field name="group" ref="group_stock"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_shipment_out_group_stock_admin">
<field name="model">stock.shipment.out</field>
<field name="group" ref="group_stock_admin"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_delete" eval="True"/>
</record>
<record model="ir.model.button" id="shipment_out_cancel_button">
<field name="model">stock.shipment.out</field>
<field name="name">cancel</field>
<field name="string">Cancel</field>
<field name="confirm">Are you sure you want to cancel the shipment?</field>
</record>
<record model="ir.model.button" id="shipment_out_draft_button">
<field name="model">stock.shipment.out</field>
<field name="name">draft</field>
<field name="string">Draft</field>
</record>
<record model="ir.model.button" id="shipment_out_wait_button">
<field name="model">stock.shipment.out</field>
<field name="name">wait</field>
<field name="string">Wait</field>
</record>
<record model="ir.model.button" id="shipment_out_pick_button">
<field name="model">stock.shipment.out</field>
<field name="name">pick</field>
<field name="string">Pick</field>
</record>
<record model="ir.model.button" id="shipment_out_pack_button">
<field name="model">stock.shipment.out</field>
<field name="name">pack</field>
<field name="string">Pack</field>
</record>
<record model="ir.model.button" id="shipment_out_ship_button">
<field name="model">stock.shipment.out</field>
<field name="name">ship</field>
<field name="string">Ship</field>
<field name="confirm">Are you sure you want to ship the shipment?</field>
</record>
<record model="ir.model.button" id="shipment_out_done_button">
<field name="model">stock.shipment.out</field>
<field name="name">do</field>
<field name="string">Complete</field>
<field name="confirm">Are you sure you want to complete the shipment?</field>
</record>
<record model="ir.model.button" id="shipment_out_assign_try_button">
<field name="model">stock.shipment.out</field>
<field name="name">assign_try</field>
</record>
<record model="ir.model.button" id="shipment_out_assign_force_button">
<field name="model">stock.shipment.out</field>
<field name="name">assign_force</field>
</record>
<record model="ir.model.button-res.group"
id="shipment_out_assign_force_button_group_stock_force_assignment">
<field name="button" ref="shipment_out_assign_force_button"/>
<field name="group" ref="group_stock_force_assignment"/>
</record>
<record model="ir.model.button" id="shipment_out_assign_wizard_button">
<field name="model">stock.shipment.out</field>
<field name="name">assign_wizard</field>
<field name="string">Assign</field>
</record>
<record model="ir.model.access" id="access_shipment_internal">
<field name="model">stock.shipment.internal</field>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_shipment_internal_group_stock">
<field name="model">stock.shipment.internal</field>
<field name="group" ref="group_stock"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_shipment_internal_group_stock_admin">
<field name="model">stock.shipment.internal</field>
<field name="group" ref="group_stock_admin"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_delete" eval="True"/>
</record>
<record model="ir.model.button" id="shipment_internal_cancel_button">
<field name="model">stock.shipment.internal</field>
<field name="name">cancel</field>
<field name="string">Cancel</field>
<field name="confirm">Are you sure you want to cancel the shipment?</field>
</record>
<record model="ir.model.button" id="shipment_internal_draft_button">
<field name="model">stock.shipment.internal</field>
<field name="name">draft</field>
<field name="string">Draft</field>
</record>
<record model="ir.model.button" id="shipment_internal_wait_button">
<field name="model">stock.shipment.internal</field>
<field name="name">wait</field>
<field name="string">Wait</field>
</record>
<record model="ir.model.button" id="shipment_internal_done_button">
<field name="model">stock.shipment.internal</field>
<field name="name">do</field>
<field name="string">Complete</field>
</record>
<record model="ir.model.button" id="shipment_internal_assign_try_button">
<field name="model">stock.shipment.internal</field>
<field name="name">assign_try</field>
</record>
<record model="ir.model.button" id="shipment_internal_assign_force_button">
<field name="model">stock.shipment.internal</field>
<field name="name">assign_force</field>
</record>
<record model="ir.model.button-res.group"
id="shipment_internal_assign_force_button_group_stock_force_assignment">
<field name="button" ref="shipment_internal_assign_force_button"/>
<field name="group" ref="group_stock_force_assignment"/>
</record>
<record model="ir.model.button"
id="shipment_internal_assign_wizard_button">
<field name="model">stock.shipment.internal</field>
<field name="name">assign_wizard</field>
<field name="string">Assign</field>
</record>
<record model="ir.model.button-res.group"
id="shipment_internal_assign_wizard_button_group_stock">
<field name="button" ref="shipment_internal_assign_wizard_button"/>
<field name="group" ref="group_stock"/>
</record>
<record model="ir.model.button" id="shipment_internal_pack_button">
<field name="model">stock.shipment.internal</field>
<field name="name">pack</field>
<field name="string">Pack</field>
</record>
<record model="ir.model.button" id="shipment_internal_ship_button">
<field name="model">stock.shipment.internal</field>
<field name="name">ship</field>
<field name="string">Ship</field>
</record>
<record model="ir.model.access" id="access_shipment_out_return">
<field name="model">stock.shipment.out.return</field>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_shipment_out_return_group_stock">
<field name="model">stock.shipment.out.return</field>
<field name="group" ref="group_stock"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_shipment_out_return_group_stock_admin">
<field name="model">stock.shipment.out.return</field>
<field name="group" ref="group_stock_admin"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_delete" eval="True"/>
</record>
<record model="ir.model.button" id="shipment_out_return_cancel_button">
<field name="model">stock.shipment.out.return</field>
<field name="name">cancel</field>
<field name="string">Cancel</field>
<field name="confirm">Are you sure you want to cancel the shipment?</field>
</record>
<record model="ir.model.button" id="shipment_out_return_draft_button">
<field name="model">stock.shipment.out.return</field>
<field name="name">draft</field>
<field name="string">Draft</field>
</record>
<record model="ir.model.button" id="shipment_out_return_receive_button">
<field name="model">stock.shipment.out.return</field>
<field name="name">receive</field>
<field name="string">Receive</field>
<field name="confirm">Are you sure you want to receive the shipment?</field>
</record>
<record model="ir.model.button" id="shipment_out_return_done_button">
<field name="model">stock.shipment.out.return</field>
<field name="name">do</field>
<field name="string">Complete</field>
</record>
<record model="ir.model.access" id="access_shipment_in_return">
<field name="model">stock.shipment.in.return</field>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_shipment_in_return_group_stock">
<field name="model">stock.shipment.in.return</field>
<field name="group" ref="group_stock"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_shipment_in_return_group_stock_admin">
<field name="model">stock.shipment.in.return</field>
<field name="group" ref="group_stock_admin"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_delete" eval="True"/>
</record>
<record model="ir.model.button" id="shipment_in_return_cancel_button">
<field name="model">stock.shipment.in.return</field>
<field name="name">cancel</field>
<field name="string">Cancel</field>
<field name="confirm">Are you sure you want to cancel the shipment?</field>
</record>
<record model="ir.model.button" id="shipment_in_return_draft_button">
<field name="model">stock.shipment.in.return</field>
<field name="name">draft</field>
<field name="string">Draft</field>
</record>
<record model="ir.model.button" id="shipment_in_return_wait_button">
<field name="model">stock.shipment.in.return</field>
<field name="name">wait</field>
<field name="string">Wait</field>
</record>
<record model="ir.model.button" id="shipment_in_return_done_button">
<field name="model">stock.shipment.in.return</field>
<field name="name">do</field>
<field name="string">Complete</field>
<field name="confirm">Are you sure you want to complete the shipment?</field>
</record>
<record model="ir.model.button" id="shipment_in_return_assign_try_button">
<field name="model">stock.shipment.in.return</field>
<field name="name">assign_try</field>
</record>
<record model="ir.model.button" id="shipment_in_return_assign_force_button">
<field name="model">stock.shipment.in.return</field>
<field name="name">assign_force</field>
</record>
<record model="ir.model.button-res.group"
id="shipment_in_return_assign_force_button_group_stock_force_assignment">
<field name="button" ref="shipment_in_return_assign_force_button"/>
<field name="group" ref="group_stock_force_assignment"/>
</record>
<record model="ir.model.button"
id="shipment_in_return_assign_wizard_button">
<field name="model">stock.shipment.in.return</field>
<field name="name">assign_wizard</field>
<field name="string">Assign</field>
</record>
<record model="ir.rule.group" id="rule_group_shipment_in_companies">
<field name="name">User in companies</field>
<field name="model">stock.shipment.in</field>
<field name="global_p" eval="True"/>
</record>
<record model="ir.rule" id="rule_shipment_in_companies">
<field name="domain"
eval="[('company', 'in', Eval('companies', []))]"
pyson="1"/>
<field name="rule_group" ref="rule_group_shipment_in_companies"/>
</record>
<record model="ir.rule.group" id="rule_group_shipment_in_return_companies">
<field name="name">User in companies</field>
<field name="model">stock.shipment.in.return</field>
<field name="global_p" eval="True"/>
</record>
<record model="ir.rule" id="rule_shipment_in_return_companies">
<field name="domain"
eval="[('company', 'in', Eval('companies', []))]"
pyson="1"/>
<field name="rule_group" ref="rule_group_shipment_in_return_companies"/>
</record>
<record model="ir.rule.group" id="rule_group_shipment_out_companies">
<field name="name">User in companies</field>
<field name="model">stock.shipment.out</field>
<field name="global_p" eval="True"/>
</record>
<record model="ir.rule" id="rule_shipment_out_companies">
<field name="domain"
eval="[('company', 'in', Eval('companies', []))]"
pyson="1"/>
<field name="rule_group" ref="rule_group_shipment_out_companies"/>
</record>
<record model="ir.rule.group" id="rule_group_shipment_out_return_companies">
<field name="name">User in companies</field>
<field name="model">stock.shipment.out.return</field>
<field name="global_p" eval="True"/>
</record>
<record model="ir.rule" id="rule_shipment_out_return_companies">
<field name="domain"
eval="[('company', 'in', Eval('companies', []))]"
pyson="1"/>
<field name="rule_group" ref="rule_group_shipment_out_return_companies"/>
</record>
<record model="ir.rule.group" id="rule_group_shipment_internal_companies">
<field name="name">User in companies</field>
<field name="model">stock.shipment.internal</field>
<field name="global_p" eval="True"/>
</record>
<record model="ir.rule" id="rule_shipment_internal_companies">
<field name="domain"
eval="[('company', 'in', Eval('companies', []))]"
pyson="1"/>
<field name="rule_group" ref="rule_group_shipment_internal_companies"/>
</record>
<record model="ir.ui.view" id="shipment_assign_partial_view_form">
<field name="model">stock.shipment.assign.partial</field>
<field name="type">form</field>
<field name="name">shipment_assign_partial_form</field>
</record>
<record model="ir.action.wizard" id="wizard_shipment_in_return_assign">
<field name="name">Assign Supplier Return Shipment</field>
<field name="wiz_name">stock.shipment.assign</field>
<field name="model">stock.shipment.in.return</field>
</record>
<record model="ir.action.wizard" id="wizard_shipment_out_assign">
<field name="name">Assign Customer Shipment</field>
<field name="wiz_name">stock.shipment.assign</field>
<field name="model">stock.shipment.out</field>
</record>
<record model="ir.action.wizard" id="wizard_shipment_internal_assign">
<field name="name">Assign Internal Shipment</field>
<field name="wiz_name">stock.shipment.assign</field>
<field name="model">stock.shipment.internal</field>
</record>
</data>
</tryton>

77
modules/stock/stock.xml Normal file
View File

@@ -0,0 +1,77 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<tryton>
<data>
<record model="res.group" id="group_stock">
<field name="name">Stock</field>
</record>
<record model="res.group" id="group_stock_admin">
<field name="name">Stock Administration</field>
<field name="parent" ref="group_stock"/>
</record>
<record model="res.group" id="group_stock_force_assignment">
<field name="name">Stock Force Assignment</field>
<field name="parent" ref="group_stock"/>
</record>
<record model="res.user-res.group"
id="user_admin_group_stock_admin">
<field name="user" ref="res.user_admin"/>
<field name="group" ref="group_stock_admin"/>
</record>
<record model="res.user-res.group"
id="user_admin_group_stock">
<field name="user" ref="res.user_admin"/>
<field name="group" ref="group_stock"/>
</record>
<record model="res.user-res.group"
id="user_admin_group_stock_force_assignment">
<field name="user" ref="res.user_admin"/>
<field name="group" ref="group_stock_force_assignment"/>
</record>
<record model="ir.ui.icon" id="stock_icon">
<field name="name">tryton-stock</field>
<field name="path">icons/tryton-stock.svg</field>
</record>
<record model="ir.ui.icon" id="stock_shipment_in">
<field name="name">tryton-shipment-in</field>
<field name="path">icons/tryton-shipment-in.svg</field>
</record>
<record model="ir.ui.icon" id="stock_shipment_out">
<field name="name">tryton-shipment-out</field>
<field name="path">icons/tryton-shipment-out.svg</field>
</record>
<menuitem
name="Inventory &amp; Stock"
sequence="60"
id="menu_stock"
icon="tryton-stock"/>
<record model="ir.ui.menu-res.group"
id="menu_stock_group_stock">
<field name="menu" ref="menu_stock"/>
<field name="group" ref="group_stock"/>
</record>
<menuitem
name="Configuration"
parent="menu_stock"
sequence="0"
id="menu_configuration"
icon="tryton-settings"/>
<record model="ir.ui.menu-res.group"
id="menu_configuration_group_stock_admin">
<field name="menu" ref="menu_configuration"/>
<field name="group" ref="group_stock_admin"/>
</record>
<menuitem
name="Reporting"
parent="menu_stock"
sequence="100"
id="menu_reporting"
active="1"/>
</data>
</tryton>

View File

@@ -0,0 +1,526 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
import datetime as dt
from dateutil.relativedelta import relativedelta
from sql import Literal, Null, Window
from sql.aggregate import Min, Sum
from sql.conditionals import Case, Coalesce, Greatest, Least, NullIf
from sql.functions import Function, NthValue, Round
from sql.operators import Concat
from trytond import backend
from trytond.model import ModelSQL, ModelView, fields
from trytond.pool import Pool
from trytond.pyson import Eval
from trytond.transaction import Transaction
class _SQLite_JulianDay(Function):
__slots__ = ()
_function = 'JULIANDAY'
class _InventoryContextMixin(ModelView):
company = fields.Many2One('company.company', "Company", required=True)
location = fields.Many2One(
'stock.location', "Location", required=True,
domain=[
('type', 'in', ['warehouse', 'storage', 'view']),
])
@classmethod
def default_company(cls):
return Transaction().context.get('company')
@classmethod
def default_location(cls):
pool = Pool()
Location = pool.get('stock.location')
return Transaction().context.get(
'location', Location.get_default_warehouse())
class _InventoryMixin:
__slots__ = ()
company = fields.Many2One('company.company', "Company")
product = fields.Reference("Product", [
('product.product', "Variant"),
('product.template', "Product"),
])
unit = fields.Function(
fields.Many2One('product.uom', "Unit"),
'on_change_with_unit')
input_quantity = fields.Float("Input Quantity")
output_quantity = fields.Float("Output Quantity")
quantity = fields.Float("Quantity")
@classmethod
def table_query(cls):
pool = Pool()
Location = pool.get('stock.location')
Move = pool.get('stock.move')
Period = pool.get('stock.period')
PeriodCache = pool.get('stock.period.cache')
Product = pool.get('product.product')
transaction = Transaction()
context = transaction.context
move = Move.__table__()
from_location = Location.__table__()
to_location = Location.__table__()
period_cache = PeriodCache.__table__()
product_table = Product.__table__()
if location := context.get('location'):
location = Location(location)
left, right = location.left, location.right
else:
left = right = -1
if date := context.get('date'):
from_date = to_date = date
else:
from_date = context.get('from_date') or dt.date.min
to_date = context.get('to_date') or dt.date.max
company = context.get('company')
if periods := Period.search([
('company', '=', company),
('date', '<=', from_date),
('state', '=', 'closed'),
],
order=[('date', 'DESC')],
limit=1):
period, = periods
else:
period = None
quantities = (
move
.join(from_location,
condition=move.from_location == from_location.id)
.join(to_location,
condition=move.to_location == to_location.id)
)
if context.get('product_type') == 'product.product':
product = Concat('product.product,', move.product)
else:
product = Concat('product.template,', product_table.template)
quantities = (
quantities
.join(product_table,
condition=move.product == product_table.id)
)
move_date = Coalesce(move.effective_date, move.planned_date)
input_clause = (
(to_location.left >= left) & (to_location.right <= right))
output_clause = (
(from_location.left >= left) & (from_location.right <= right))
start_quantity = Sum(
move.quantity * Case((output_clause, -1), else_=1),
filter_=(move_date < from_date))
input_quantity = Sum(
move.quantity,
filter_=input_clause & (move_date >= from_date))
output_quantity = Sum(
move.quantity,
filter_=output_clause & (move_date >= from_date))
date_column = Greatest(move_date, from_date)
if context.get('product_type') == 'product.product':
partition = [move.product]
else:
partition = [product_table.template]
# Use NthValue instead of LastValue to get NULL for the last row
next_date_column = NthValue(
date_column, 2, window=Window(
partition,
order_by=[date_column.asc],
frame='ROWS', start=0, end=1))
state_clause = cls._state_clause(move, to_date)
quantities = quantities.select(
Min(move.id).as_('id'),
product.as_('product'),
date_column.as_('date'),
next_date_column.as_('next_date'),
start_quantity.as_('start_quantity'),
input_quantity.as_('input_quantity'),
output_quantity.as_('output_quantity'),
*cls._quantities_columns(move, product_table),
where=(((input_clause & ~output_clause)
| (output_clause & ~input_clause))
& (move_date <= to_date)
& state_clause
& (move.company == company)),
group_by=[
*partition,
date_column,
*cls._quantities_group_by(move, product_table),
],
)
quantity = Sum(
Coalesce(quantities.start_quantity, 0)
+ Coalesce(quantities.input_quantity, 0)
- Coalesce(quantities.output_quantity, 0),
window=Window(
[quantities.product],
order_by=[*cls._quantities_order_by(quantities)]))
if period:
quantities.where &= move_date > period.date
if context.get('product_type') != 'product.product':
period_cache_product = Product.__table__()
cache = (
period_cache
.join(period_cache_product,
condition=period_cache.product
== period_cache_product.id)
.select(
period_cache_product.template.as_('product'),
Sum(period_cache.internal_quantity
).as_('quantity'),
group_by=[period_cache_product.template]))
else:
cache = (
period_cache
.select(
period_cache.product.as_('product'),
Sum(period_cache.internal_quantity).as_('quantity'),
group_by=[period_cache.product]))
location_cache = Location.__table__()
cache.where = (
(period_cache.period == period.id)
& period_cache.location.in_(
location_cache.select(
location_cache.id,
where=(location_cache.left >= left)
& (location_cache.right <= right))))
query = quantities.join(cache, 'LEFT',
condition=(
cache.product
== cls.product.sql_id(quantities.product, cls)))
quantity += Coalesce(cache.quantity, 0)
else:
query = quantities
return (query
.select(
quantities.id.as_('id'),
Literal(company).as_('company'),
quantities.product.as_('product'),
quantities.input_quantity.as_('input_quantity'),
quantities.output_quantity.as_('output_quantity'),
quantity.as_('quantity'),
*cls._columns(quantities),
where=(
(Coalesce(quantities.next_date, dt.date.max) >= from_date)
| (quantities.date >= from_date))))
@classmethod
def _quantities_columns(cls, move, product):
yield from []
@classmethod
def _quantities_group_by(cls, move, product):
yield from []
@classmethod
def _quantities_order_by(cls, quantities):
yield quantities.date.asc
@classmethod
def _columns(cls, quantities):
yield from []
@classmethod
def _where(cls, quantities):
return Literal(True)
@classmethod
def _state_clause(cls, move, date):
pool = Pool()
Date = pool.get('ir.date')
today = Date.today()
forcast = date > today
move_date = Coalesce(move.effective_date, move.planned_date)
state_clause = (
(move_date <= today) & (move.state == 'done'))
state_clause |= (
((move_date >= today) if forcast else (move_date > today))
& (move.state.in_(['done', 'assigned', 'draft'])))
return state_clause
@fields.depends('product')
def on_change_with_unit(self, name=None):
if self.product:
return self.product.default_uom
class InventoryContext(_InventoryContextMixin):
__name__ = 'stock.reporting.inventory.context'
date = fields.Date("Date", required=True)
product_type = fields.Selection([
('product.product', "Variant"),
('product.template', "Product"),
], "Product Type", required=True)
@classmethod
def default_date(cls):
return Pool().get('ir.date').today()
@classmethod
def default_product_type(cls):
return Transaction().context.get('product_type') or 'product.template'
class Inventory(_InventoryMixin, ModelSQL, ModelView):
__name__ = 'stock.reporting.inventory'
class _InventoryRangeContextMixin(_InventoryContextMixin):
from_date = fields.Date(
"From Date", required=True,
domain=[
('from_date', '<=', Eval('to_date')),
])
to_date = fields.Date(
"To Date", required=True,
domain=[
('to_date', '>=', Eval('from_date')),
])
@classmethod
def default_from_date(cls):
pool = Pool()
Date = pool.get('ir.date')
context = Transaction().context
if 'from_date' in context:
return context['from_date']
return Date.today() - relativedelta(day=1, months=6)
@classmethod
def default_to_date(cls):
pool = Pool()
Date = pool.get('ir.date')
context = Transaction().context
if 'to_date' in context:
return context['to_date']
return Date.today() + relativedelta(day=1, months=6)
class InventoryRangeContext(_InventoryRangeContextMixin):
__name__ = 'stock.reporting.inventory.range.context'
class InventoryMove(_InventoryMixin, ModelSQL, ModelView):
__name__ = 'stock.reporting.inventory.move'
date = fields.Date("Date")
move = fields.Many2One('stock.move', "Move")
origin = fields.Reference("Origin", selection='get_origin')
document = fields.Function(
fields.Reference("Document", selection='get_documents'),
'get_document')
@classmethod
def __setup__(cls):
super().__setup__()
cls._order = [
('date', 'DESC'),
('move', 'DESC NULLS LAST'),
('id', 'DESC'),
]
@classmethod
def _quantities_columns(cls, move, product):
from_date = Transaction().context.get('from_date') or dt.date.min
move_date = Coalesce(move.effective_date, move.planned_date)
yield from super()._quantities_columns(move, product)
yield Case(
(move_date < from_date, Null),
else_=move.id).as_('move')
yield Case(
(move_date < from_date, Null),
else_=move.origin).as_('origin')
@classmethod
def _quantities_order_by(cls, quantities):
yield from super()._quantities_order_by(quantities)
yield quantities.move.asc.nulls_first
@classmethod
def _quantities_group_by(cls, move, product):
from_date = Transaction().context.get('from_date') or dt.date.min
move_date = Coalesce(move.effective_date, move.planned_date)
yield from super()._quantities_group_by(move, product)
yield Case(
(move_date < from_date, Null),
else_=move.id)
yield Case(
(move_date < from_date, Null),
else_=move.origin)
@classmethod
def _columns(cls, quantities):
yield from super()._columns(quantities)
yield quantities.date.as_('date')
yield quantities.move.as_('move')
yield quantities.origin.as_('origin')
@classmethod
def get_origin(cls):
pool = Pool()
Move = pool.get('stock.move')
return Move.get_origin()
@classmethod
def _get_document_models(cls):
pool = Pool()
Move = pool.get('stock.move')
return [m for m, _ in Move.get_shipment() if m]
@classmethod
def get_documents(cls):
pool = Pool()
Model = pool.get('ir.model')
get_name = Model.get_name
models = cls._get_document_models()
return [(None, '')] + [(m, get_name(m)) for m in models]
def get_document(self, name):
if self.move and self.move.shipment:
return str(self.move.shipment)
def get_rec_name(self, name):
name = super().get_rec_name(name)
if self.move:
name = self.move.rec_name
return name
class InventoryDaily(_InventoryMixin, ModelSQL, ModelView):
__name__ = 'stock.reporting.inventory.daily'
from_date = fields.Date("From Date")
to_date = fields.Date("To Date")
@classmethod
def __setup__(cls):
super().__setup__()
cls._order = [
('from_date', 'DESC'),
('id', 'DESC'),
]
@classmethod
def _quantities_order_by(cls, quantities):
yield from []
yield quantities.date.asc
@classmethod
def _columns(cls, quantities):
transaction = Transaction()
context = transaction.context
to_date = context.get('to_date') or dt.date.max
yield from super()._columns(quantities)
yield quantities.date.as_('from_date')
yield Least(quantities.next_date, to_date).as_('to_date')
class InventoryTurnoverContext(_InventoryRangeContextMixin):
__name__ = 'stock.reporting.inventory.turnover.context'
product_type = fields.Selection([
('product.product', "Variant"),
('product.template', "Product"),
], "Product Type", required=True)
@classmethod
def default_product_type(cls):
return Transaction().context.get('product_type') or 'product.template'
class InventoryTurnover(ModelSQL, ModelView):
__name__ = 'stock.reporting.inventory.turnover'
company = fields.Many2One('company.company', "Company")
product = fields.Reference("Product", [
('product.product', "Variant"),
('product.template', "Product"),
])
unit = fields.Function(
fields.Many2One('product.uom', "Unit"),
'on_change_with_unit')
output_quantity = fields.Float("Output Quantity", digits=(None, 3))
average_quantity = fields.Float("Average Quantity", digits=(None, 3))
turnover = fields.Float("Turnover", digits=(None, 3))
@classmethod
def table_query(cls):
pool = Pool()
Inventory = pool.get('stock.reporting.inventory.daily')
transaction = Transaction()
context = transaction.context
inventory = Inventory.__table__()
from_date = context.get('from_date') or dt.date.min
to_date = context.get('to_date') or dt.date.max
company = context.get('company')
days = (to_date - from_date).days + 1
output_quantity = Sum(inventory.output_quantity) / days
inventory_from_date = inventory.from_date
inventory_to_date = inventory.to_date
if backend.name == 'sqlite':
inventory_from_date = _SQLite_JulianDay(inventory_from_date)
inventory_to_date = _SQLite_JulianDay(inventory_to_date)
average_quantity = (
Sum(Case(
(inventory.quantity >= 0, inventory.quantity),
else_=0)
* (inventory_to_date - inventory_from_date
+ Case((inventory.to_date == to_date, 1), else_=0)))
/ days)
def round_sql(expression, digits=2):
factor = 10 ** digits
return Round(expression * factor) / factor
return (inventory
.select(
cls.product.sql_id(inventory.product, cls).as_('id'),
Literal(company).as_('company'),
inventory.product.as_('product'),
round_sql(
output_quantity,
cls.output_quantity.digits[1]).as_('output_quantity'),
round_sql(
average_quantity,
cls.average_quantity.digits[1]).as_('average_quantity'),
round_sql(
output_quantity / NullIf(average_quantity, 0),
cls.turnover.digits[1]).as_('turnover'),
group_by=[inventory.product]))
@fields.depends('product')
def on_change_with_unit(self, name=None):
if self.product:
return self.product.default_uom

View File

@@ -0,0 +1,315 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<tryton>
<data>
<record model="ir.ui.view" id="reporting_inventory_context_view_form">
<field name="model">stock.reporting.inventory.context</field>
<field name="type">form</field>
<field name="name">reporting_inventory_context_form</field>
</record>
<record model="ir.ui.view" id="reporting_inventory_view_list">
<field name="model">stock.reporting.inventory</field>
<field name="type">tree</field>
<field name="name">reporting_inventory_list</field>
</record>
<record model="ir.ui.view" id="reporting_inventory_view_form">
<field name="model">stock.reporting.inventory</field>
<field name="type">form</field>
<field name="name">reporting_inventory_form</field>
</record>
<record model="ir.action.act_window" id="act_reporting_inventory">
<field name="name">Inventory</field>
<field name="res_model">stock.reporting.inventory</field>
<field name="context_model">stock.reporting.inventory.context</field>
</record>
<record model="ir.action.act_window.view" id="act_reporting_inventory_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="reporting_inventory_view_list"/>
<field name="act_window" ref="act_reporting_inventory"/>
</record>
<record model="ir.action.act_window.view" id="act_reporting_inventory_view2">
<field name="sequence" eval="20"/>
<field name="view" ref="reporting_inventory_view_form"/>
<field name="act_window" ref="act_reporting_inventory"/>
</record>
<menuitem
parent="menu_reporting"
action="act_reporting_inventory"
sequence="20"
id="menu_reporting_inventory"/>
<record model="ir.rule.group" id="rule_group_reporting_inventory_companies">
<field name="name">User in companies</field>
<field name="model">stock.reporting.inventory</field>
<field name="global_p" eval="True"/>
</record>
<record model="ir.rule" id="rule_reporting_inventory_companies">
<field
name="domain"
eval="[('company', 'in', Eval('companies', []))]"
pyson="1"/>
<field name="rule_group" ref="rule_group_reporting_inventory_companies"/>
</record>
<record model="ir.model.access" id="access_reporting_inventory">
<field name="model">stock.reporting.inventory</field>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_reporting_inventory_group_stock">
<field name="model">stock.reporting.inventory</field>
<field name="group" ref="group_stock"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.ui.view" id="reporting_inventory_range_context_view_form">
<field name="model">stock.reporting.inventory.range.context</field>
<field name="type">form</field>
<field name="name">reporting_inventory_range_context_form</field>
</record>
<record model="ir.ui.view" id="reporting_inventory_move_view_list">
<field name="model">stock.reporting.inventory.move</field>
<field name="type">tree</field>
<field name="name">reporting_inventory_move_list</field>
</record>
<record model="ir.ui.view" id="reporting_inventory_move_view_form">
<field name="model">stock.reporting.inventory.move</field>
<field name="type">form</field>
<field name="name">reporting_inventory_move_form</field>
</record>
<record model="ir.action.act_window" id="act_reporting_inventory_move">
<field name="name">Stock Inventory Moves</field>
<field name="res_model">stock.reporting.inventory.move</field>
<field name="context_model">stock.reporting.inventory.range.context</field>
<field
name="domain"
eval="[('product', '=', (Eval('active_model', 'product.template'), Eval('active_id', -1)))]"
pyson="1"/>
<field
name="context"
eval="{'product_type': Eval('active_model', 'product.template'), 'date': None}"
pyson="1"/>
</record>
<record model="ir.action.act_window.view" id="act_reporting_inventory_move_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="reporting_inventory_move_view_list"/>
<field name="act_window" ref="act_reporting_inventory_move"/>
</record>
<record model="ir.action.act_window.view" id="act_reporting_inventory_move_view2">
<field name="sequence" eval="20"/>
<field name="view" ref="reporting_inventory_move_view_form"/>
<field name="act_window" ref="act_reporting_inventory_move"/>
</record>
<record model="ir.action.keyword" id="act_reporting_inventory_move_keyword_product">
<field name="keyword">form_relate</field>
<field name="model">product.product,-1</field>
<field name="action" ref="act_reporting_inventory_move"/>
</record>
<record model="ir.action.keyword" id="act_reporting_inventory_move_keyword_template">
<field name="keyword">form_relate</field>
<field name="model">product.template,-1</field>
<field name="action" ref="act_reporting_inventory_move"/>
</record>
<record model="ir.rule.group" id="rule_group_reporting_inventory_move_companies">
<field name="name">User in companies</field>
<field name="model">stock.reporting.inventory.move</field>
<field name="global_p" eval="True"/>
</record>
<record model="ir.rule" id="rule_reporting_inventory_move_companies">
<field
name="domain"
eval="[('company', 'in', Eval('companies', []))]"
pyson="1"/>
<field name="rule_group" ref="rule_group_reporting_inventory_move_companies"/>
</record>
<record model="ir.model.access" id="access_reporting_inventory_move">
<field name="model">stock.reporting.inventory.move</field>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_reporting_inventory_move_group_stock">
<field name="model">stock.reporting.inventory.move</field>
<field name="group" ref="group_stock"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.ui.view" id="reporting_inventory_daily_view_list">
<field name="model">stock.reporting.inventory.daily</field>
<field name="type">tree</field>
<field name="name">reporting_inventory_daily_list</field>
</record>
<record model="ir.ui.view" id="reporting_inventory_daily_view_form">
<field name="model">stock.reporting.inventory.daily</field>
<field name="type">form</field>
<field name="name">reporting_inventory_daily_form</field>
</record>
<record model="ir.ui.view" id="reporting_inventory_daily_view_graph">
<field name="model">stock.reporting.inventory.daily</field>
<field name="type">graph</field>
<field name="name">reporting_inventory_daily_graph</field>
</record>
<record model="ir.action.act_window" id="act_reporting_inventory_daily">
<field name="name">Stock Inventory Daily</field>
<field name="res_model">stock.reporting.inventory.daily</field>
<field name="context_model">stock.reporting.inventory.range.context</field>
<field
name="domain"
eval="[('product', '=', (Eval('active_model', 'product.template'), Eval('active_id', -1)))]"
pyson="1"/>
<field
name="context"
eval="{'product_type': Eval('active_model', 'product.template'), 'date': None}"
pyson="1"/>
</record>
<record model="ir.action.act_window.view" id="act_reporting_inventory_daily_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="reporting_inventory_daily_view_list"/>
<field name="act_window" ref="act_reporting_inventory_daily"/>
</record>
<record model="ir.action.act_window.view" id="act_reporting_inventory_daily_view2">
<field name="sequence" eval="20"/>
<field name="view" ref="reporting_inventory_daily_view_graph"/>
<field name="act_window" ref="act_reporting_inventory_daily"/>
</record>
<record model="ir.action.act_window.view" id="act_reporting_inventory_daily_view3">
<field name="sequence" eval="30"/>
<field name="view" ref="reporting_inventory_daily_view_form"/>
<field name="act_window" ref="act_reporting_inventory_daily"/>
</record>
<record model="ir.action.keyword" id="act_reporting_inventory_daily_keyword_product">
<field name="keyword">form_relate</field>
<field name="model">product.product,-1</field>
<field name="action" ref="act_reporting_inventory_daily"/>
</record>
<record model="ir.action.keyword" id="act_reporting_inventory_daily_keyword_template">
<field name="keyword">form_relate</field>
<field name="model">product.template,-1</field>
<field name="action" ref="act_reporting_inventory_daily"/>
</record>
<record model="ir.rule.group" id="rule_group_reporting_inventory_daily_companies">
<field name="name">User in companies</field>
<field name="model">stock.reporting.inventory.daily</field>
<field name="global_p" eval="True"/>
</record>
<record model="ir.rule" id="rule_reporting_inventory_daily_companies">
<field
name="domain"
eval="[('company', 'in', Eval('companies', []))]"
pyson="1"/>
<field name="rule_group" ref="rule_group_reporting_inventory_daily_companies"/>
</record>
<record model="ir.model.access" id="access_reporting_inventory_daily">
<field name="model">stock.reporting.inventory.daily</field>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_reporting_inventory_daily_group_stock">
<field name="model">stock.reporting.inventory.daily</field>
<field name="group" ref="group_stock"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.ui.view" id="reporting_inventory_turnover_context_view_form">
<field name="model">stock.reporting.inventory.turnover.context</field>
<field name="type">form</field>
<field name="name">reporting_inventory_turnover_context_form</field>
</record>
<record model="ir.ui.view" id="reporting_inventory_turnover_view_list">
<field name="model">stock.reporting.inventory.turnover</field>
<field name="type">tree</field>
<field name="name">reporting_inventory_turnover_list</field>
</record>
<record model="ir.ui.view" id="reporting_inventory_turnover_view_form">
<field name="model">stock.reporting.inventory.turnover</field>
<field name="type">form</field>
<field name="name">reporting_inventory_turnover_form</field>
</record>
<record model="ir.action.act_window" id="act_reporting_inventory_turnover">
<field name="name">Inventory Turnover</field>
<field name="res_model">stock.reporting.inventory.turnover</field>
<field name="context_model">stock.reporting.inventory.turnover.context</field>
<field
name="context"
eval="{'from_date': Date(delta_years=-1), 'to_date': Date()}"
pyson="1"/>
</record>
<record model="ir.action.act_window.view" id="act_reporting_inventory_turnover_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="reporting_inventory_turnover_view_list"/>
<field name="act_window" ref="act_reporting_inventory_turnover"/>
</record>
<record model="ir.action.act_window.view" id="act_reporting_inventory_turnover_view2">
<field name="sequence" eval="20"/>
<field name="view" ref="reporting_inventory_turnover_view_form"/>
<field name="act_window" ref="act_reporting_inventory_turnover"/>
</record>
<menuitem
parent="menu_reporting"
action="act_reporting_inventory_turnover"
sequence="30"
id="menu_reporting_inventory_turnover"/>
<record model="ir.rule.group" id="rule_group_reporting_inventory_turnover_companies">
<field name="name">User in companies</field>
<field name="model">stock.reporting.inventory.turnover</field>
<field name="global_p" eval="True"/>
</record>
<record model="ir.rule" id="rule_reporting_inventory_turnover_companies">
<field
name="domain"
eval="[('company', 'in', Eval('companies', []))]"
pyson="1"/>
<field name="rule_group" ref="rule_group_reporting_inventory_turnover_companies"/>
</record>
<record model="ir.model.access" id="access_reporting_inventory_turnover">
<field name="model">stock.reporting.inventory.turnover</field>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_reporting_inventory_turnover_group_stock">
<field name="model">stock.reporting.inventory.turnover</field>
<field name="group" ref="group_stock"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
</data>
</tryton>

View File

@@ -0,0 +1,633 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from collections import defaultdict
from decimal import Decimal
from dateutil.relativedelta import relativedelta
from sql import Literal, Null, With
from sql.aggregate import Max, Min, Sum
from sql.conditionals import Case, Coalesce
from sql.functions import Ceil, DateTrunc, Log, Power, Round
from trytond.i18n import lazy_gettext
from trytond.model import ModelSQL, ModelView, fields, sum_tree
from trytond.modules.currency.fields import Monetary
from trytond.pool import Pool
from trytond.pyson import Eval, If
from trytond.tools import grouped_slice, pairwise_longest, reduce_ids
from trytond.tools.chart import sparkline
from trytond.transaction import Transaction
class Abstract(ModelSQL, ModelView):
company = fields.Many2One(
'company.company', lazy_gettext('stock.msg_stock_reporting_company'))
cost = Monetary(
lazy_gettext('stock.msg_stock_reporting_cost'),
currency='currency', digits='currency')
revenue = Monetary(
lazy_gettext('stock.msg_stock_reporting_revenue'),
currency='currency', digits='currency')
profit = Monetary(
lazy_gettext('stock.msg_stock_reporting_profit'),
currency='currency', digits='currency')
margin = fields.Numeric(
lazy_gettext('stock.msg_stock_reporting_margin'),
digits=(None, 4),
states={
'invisible': ~Eval('margin'),
})
margin_trend = fields.Function(fields.Char(
lazy_gettext('stock.msg_stock_reporting_margin_trend')),
'get_trend')
time_series = None
currency = fields.Many2One(
'currency.currency',
lazy_gettext('stock.msg_stock_reporting_currency'))
@classmethod
def table_query(cls):
from_item, tables, withs = cls._joins()
return from_item.select(*cls._columns(tables, withs),
where=cls._where(tables, withs),
group_by=cls._group_by(tables, withs),
with_=withs.values())
@classmethod
def _joins(cls):
pool = Pool()
Company = pool.get('company.company')
Currency = pool.get('currency.currency')
Move = pool.get('stock.move')
Location = pool.get('stock.location')
tables = {}
tables['move'] = move = Move.__table__()
tables['move.company'] = company = Company.__table__()
tables['move.company.currency'] = currency = Currency.__table__()
tables['move.from_location'] = from_location = Location.__table__()
tables['move.to_location'] = to_location = Location.__table__()
withs = {}
withs['currency_rate'] = currency_rate = With(
query=Currency.currency_rate_sql())
withs['currency_rate_company'] = currency_rate_company = With(
query=Currency.currency_rate_sql())
from_item = (move
.join(currency_rate, type_='LEFT',
condition=(move.currency == currency_rate.currency)
& (currency_rate.start_date <= move.effective_date)
& ((currency_rate.end_date == Null)
| (currency_rate.end_date > move.effective_date))
)
.join(company,
condition=move.company == company.id)
.join(currency,
condition=company.currency == currency.id)
.join(currency_rate_company,
condition=(company.currency == currency_rate_company.currency)
& (currency_rate_company.start_date <= move.effective_date)
& ((currency_rate_company.end_date == Null)
| (currency_rate_company.end_date > move.effective_date))
)
.join(from_location,
condition=(move.from_location == from_location.id))
.join(to_location,
condition=(move.to_location == to_location.id)))
return from_item, tables, withs
@classmethod
def _columns(cls, tables, withs):
move = tables['move']
from_location = tables['move.from_location']
to_location = tables['move.to_location']
currency = tables['move.company.currency']
sign = Case(
(from_location.type.in_(cls._to_location_types())
& to_location.type.in_(cls._from_location_types()),
-1),
else_=1)
cost = cls._column_cost(tables, withs, sign)
revenue = cls._column_revenue(tables, withs, sign)
profit = revenue - cost
margin = Case(
(revenue != 0, profit / revenue),
else_=Null)
return [
cls._column_id(tables, withs).as_('id'),
move.company.as_('company'),
cls.cost.sql_cast(
Round(cost, currency.digits)).as_('cost'),
cls.revenue.sql_cast(
Round(revenue, currency.digits)).as_('revenue'),
cls.profit.sql_cast(
Round(profit, currency.digits)).as_('profit'),
cls.margin.sql_cast(
Round(margin, cls.margin.digits[1])).as_('margin'),
currency.id.as_('currency'),
]
@classmethod
def _column_id(cls, tables, withs):
move = tables['move']
return Min(move.id)
@classmethod
def _column_cost(cls, tables, withs, sign):
move = tables['move']
return Sum(
sign * cls.cost.sql_cast(move.internal_quantity)
* Coalesce(move.cost_price, 0))
@classmethod
def _column_revenue(cls, tables, withs, sign):
move = tables['move']
currency = withs['currency_rate']
currency_company = withs['currency_rate_company']
return Sum(
sign * cls.revenue.sql_cast(move.quantity)
* Coalesce(move.unit_price, 0)
* Coalesce(currency_company.rate / currency.rate, 0))
@classmethod
def _group_by(cls, tables, withs):
move = tables['move']
currency = tables['move.company.currency']
return [move.company, currency.id, currency.digits]
@classmethod
def _where(cls, tables, withs):
context = Transaction().context
move = tables['move']
from_location = tables['move.from_location']
to_location = tables['move.to_location']
where = move.company == context.get('company')
where &= ((
from_location.type.in_(cls._from_location_types())
& to_location.type.in_(cls._to_location_types()))
| (
from_location.type.in_(cls._to_location_types())
& to_location.type.in_(cls._from_location_types())))
where &= move.state == 'done'
from_date = context.get('from_date')
if from_date:
where &= move.effective_date >= from_date
to_date = context.get('to_date')
if to_date:
where &= move.effective_date <= to_date
return where
@classmethod
def _from_location_types(cls):
return ['storage', 'drop']
@classmethod
def _to_location_types(cls):
types = ['customer']
if Transaction().context.get('include_lost'):
types += ['lost_found']
return types
@property
def time_series_all(self):
delta = self._period_delta()
for ts, next_ts in pairwise_longest(self.time_series or []):
yield ts
if delta and next_ts:
date = ts.date + delta
while date < next_ts.date:
yield None
date += delta
@classmethod
def _period_delta(cls):
context = Transaction().context
return {
'year': relativedelta(years=1),
'month': relativedelta(months=1),
'day': relativedelta(days=1),
}.get(context.get('period'))
def get_trend(self, name):
name = name[:-len('_trend')]
return sparkline(
[getattr(ts, name) or 0 if ts else 0
for ts in self.time_series_all])
@classmethod
def view_attributes(cls):
return super().view_attributes() + [
('/tree/field[@name="profit"]', 'visual',
If(Eval('profit', 0) < 0, 'danger', '')),
('/tree/field[@name="margin"]', 'visual',
If(Eval('margin', 0) < 0, 'danger', '')),
]
class AbstractTimeseries(Abstract):
date = fields.Date("Date")
@classmethod
def __setup__(cls):
super().__setup__()
cls._order.insert(0, ('date', 'ASC'))
@classmethod
def _columns(cls, tables, withs):
return super()._columns(tables, withs) + [
cls._column_date(tables, withs).as_('date')]
@classmethod
def _column_date(cls, tables, withs):
context = Transaction().context
move = tables['move']
date = DateTrunc(context.get('period'), move.effective_date)
date = cls.date.sql_cast(date)
return date
@classmethod
def _group_by(cls, tables, withs):
return super()._group_by(tables, withs) + [
cls._column_date(tables, withs)]
class Context(ModelView):
__name__ = 'stock.reporting.margin.context'
company = fields.Many2One('company.company', "Company", required=True)
from_date = fields.Date("From Date",
domain=[
If(Eval('to_date') & Eval('from_date'),
('from_date', '<=', Eval('to_date')),
()),
])
to_date = fields.Date("To Date",
domain=[
If(Eval('from_date') & Eval('to_date'),
('to_date', '>=', Eval('from_date')),
()),
])
period = fields.Selection([
('year', "Year"),
('month', "Month"),
('day', "Day"),
], "Period", required=True)
include_lost = fields.Boolean(
"Include Lost",
help="If checked, the cost of product moved "
"to a lost and found location is included.")
@classmethod
def default_company(cls):
return Transaction().context.get('company')
@classmethod
def default_from_date(cls):
pool = Pool()
Date = pool.get('ir.date')
context = Transaction().context
if 'from_date' in context:
return context['from_date']
return Date.today() - relativedelta(years=1)
@classmethod
def default_to_date(cls):
pool = Pool()
Date = pool.get('ir.date')
context = Transaction().context
if 'to_date' in context:
return context['to_date']
return Date.today()
@classmethod
def default_period(cls):
return Transaction().context.get('period', 'month')
@classmethod
def default_include_lost(cls):
return Transaction().context.get('include_lost', False)
class Main(Abstract, ModelView):
__name__ = 'stock.reporting.margin.main'
time_series = fields.Function(fields.Many2Many(
'stock.reporting.margin.main.time_series', None, None,
"Time Series"),
'get_time_series')
def get_rec_name(self, name):
return ''
def get_time_series(self, name):
pool = Pool()
Timeseries = pool.get('stock.reporting.margin.main.time_series')
return [t.id for t in Timeseries.search([])]
class MainTimeseries(AbstractTimeseries, ModelView):
__name__ = 'stock.reporting.margin.main.time_series'
class ProductMixin:
__slots__ = ()
product = fields.Many2One(
'product.product', "Product",
context={
'company': Eval('company', -1),
},
depends={'company'})
internal_quantity = fields.Float("Internal Quantity")
quantity = fields.Function(fields.Float(
"Quantity", digits='unit'), 'get_quantity')
unit = fields.Many2One('product.uom', "Unit")
@classmethod
def _joins(cls):
pool = Pool()
Product = pool.get('product.product')
Template = pool.get('product.template')
from_item, tables, withs = super()._joins()
if 'move.product' not in tables:
product = Product.__table__()
tables['move.product'] = product
move = tables['move']
from_item = (from_item
.join(product, condition=move.product == product.id))
if 'move.product.template' not in tables:
template = Template.__table__()
tables['move.product.template'] = template
product = tables['move.product']
from_item = (from_item
.join(template, condition=product.template == template.id))
return from_item, tables, withs
@classmethod
def _columns(cls, tables, withs):
move = tables['move']
from_location = tables['move.from_location']
to_location = tables['move.to_location']
template = tables['move.product.template']
sign = Case(
(from_location.type.in_(cls._to_location_types())
& to_location.type.in_(cls._from_location_types()),
-1),
else_=1)
return super()._columns(tables, withs) + [
move.product.as_('product'),
Sum(sign * move.internal_quantity).as_('internal_quantity'),
template.default_uom.as_('unit'),
]
@classmethod
def _group_by(cls, tables, withs):
move = tables['move']
template = tables['move.product.template']
return super()._group_by(tables, withs) + [
move.product, template.default_uom]
def get_rec_name(self, name):
return self.product.rec_name
@classmethod
def search_rec_name(cls, name, clause):
return [('product.rec_name', *clause[1:])]
def get_quantity(self, name):
return self.unit.round(self.internal_quantity)
class Product(ProductMixin, Abstract, ModelView):
__name__ = 'stock.reporting.margin.product'
time_series = fields.One2Many(
'stock.reporting.margin.product.time_series', 'product', "Time Series")
@classmethod
def __setup__(cls):
super().__setup__()
cls._order.insert(0, ('product', 'ASC'))
@classmethod
def _column_id(cls, tables, withs):
move = tables['move']
return move.product
class ProductTimeseries(ProductMixin, AbstractTimeseries, ModelView):
__name__ = 'stock.reporting.margin.product.time_series'
class CategoryMixin:
__slots__ = ()
category = fields.Many2One('product.category', "Category")
@classmethod
def _joins(cls):
pool = Pool()
Product = pool.get('product.product')
TemplateCategory = pool.get('product.template-product.category.all')
from_item, tables, withs = super()._joins()
if 'move.product' not in tables:
product = Product.__table__()
tables['move.product'] = product
move = tables['move']
from_item = (from_item
.join(product, condition=move.product == product.id))
if 'move.product.template_category' not in tables:
template_category = TemplateCategory.__table__()
tables['move.product.template_category'] = template_category
product = tables['move.product']
from_item = (from_item
.join(template_category,
condition=product.template == template_category.template))
return from_item, tables, withs
@classmethod
def _columns(cls, tables, withs):
template_category = tables['move.product.template_category']
return super()._columns(tables, withs) + [
template_category.category.as_('category')]
@classmethod
def _column_id(cls, tables, withs):
pool = Pool()
Category = pool.get('product.category')
category = Category.__table__()
move = tables['move']
template_category = tables['move.product.template_category']
# Get a stable number of category over time
# by using number one order bigger.
nb_category = category.select(
Power(10, (Ceil(Log(Max(category.id))) + Literal(1))))
return Min(move.id * nb_category + template_category.id)
@classmethod
def _group_by(cls, tables, withs):
template_category = tables['move.product.template_category']
return super()._group_by(tables, withs) + [template_category.category]
@classmethod
def _where(cls, tables, withs):
template_category = tables['move.product.template_category']
where = super()._where(tables, withs)
where &= template_category.category != Null
return where
def get_rec_name(self, name):
return self.category.rec_name if self.category else None
@classmethod
def search_rec_name(cls, name, clause):
return [('category.rec_name', *clause[1:])]
class Category(CategoryMixin, Abstract, ModelView):
__name__ = 'stock.reporting.margin.category'
time_series = fields.One2Many(
'stock.reporting.margin.category.time_series', 'category',
"Time Series")
@classmethod
def __setup__(cls):
super().__setup__()
cls._order.insert(0, ('category', 'ASC'))
@classmethod
def _column_id(cls, tables, withs):
template_category = tables['move.product.template_category']
return template_category.category
class CategoryTimeseries(CategoryMixin, AbstractTimeseries, ModelView):
__name__ = 'stock.reporting.margin.category.time_series'
class CategoryTree(ModelSQL, ModelView):
__name__ = 'stock.reporting.margin.category.tree'
name = fields.Function(
fields.Char("Name"), 'get_name', searcher='search_name')
parent = fields.Many2One('stock.reporting.margin.category.tree', "Parent")
children = fields.One2Many(
'stock.reporting.margin.category.tree', 'parent', "Children")
cost = fields.Function(Monetary(
lazy_gettext('stock.msg_stock_reporting_cost'),
currency='currency', digits='currency'),
'get_total')
revenue = fields.Function(Monetary(
lazy_gettext('stock.msg_stock_reporting_revenue'),
currency='currency', digits='currency'),
'get_total')
profit = fields.Function(Monetary(
lazy_gettext('stock.msg_stock_reporting_profit'),
currency='currency', digits='currency'),
'get_total')
margin = fields.Function(Monetary(
lazy_gettext('stock.msg_stock_reporting_margin'),
digits=(None, 4)),
'get_margin')
currency = fields.Function(fields.Many2One(
'currency.currency',
lazy_gettext('stock.msg_stock_reporting_currency')),
'get_currency')
@classmethod
def __setup__(cls):
super().__setup__()
cls._order.insert(0, ('name', 'ASC'))
@classmethod
def table_query(cls):
pool = Pool()
Category = pool.get('product.category')
return Category.__table__()
@classmethod
def get_name(cls, categories, name):
pool = Pool()
Category = pool.get('product.category')
categories = Category.browse(categories)
return {c.id: c.name for c in categories}
@classmethod
def search_name(cls, name, clause):
pool = Pool()
Category = pool.get('product.category')
return [('id', 'in', Category.search([clause], query=True))]
@classmethod
def order_name(cls, tables):
pool = Pool()
Category = pool.get('product.category')
table, _ = tables[None]
if 'category' not in tables:
category = Category.__table__()
tables['category'] = {
None: (category, table.id == category.id),
}
return Category.name.convert_order(
'name', tables['category'], Category)
def time_series_all(self):
return []
@classmethod
def get_total(cls, categories, names):
pool = Pool()
ReportingCategory = pool.get('stock.reporting.margin.category')
reporting_category = ReportingCategory.__table__()
cursor = Transaction().connection.cursor()
categories = cls.search([
('parent', 'child_of', [c.id for c in categories]),
])
ids = [c.id for c in categories]
reporting_categories = []
for sub_ids in grouped_slice(ids):
sub_ids = list(sub_ids)
where = reduce_ids(reporting_category.id, sub_ids)
cursor.execute(
*reporting_category.select(reporting_category.id, where=where))
reporting_categories.extend(r for r, in cursor)
result = {}
reporting_categories = ReportingCategory.browse(reporting_categories)
for name in names:
values = defaultdict(
Decimal,
{c.id: getattr(c, name) for c in reporting_categories})
result[name] = sum_tree(categories, values)
return result
def get_margin(self, name):
digits = self.__class__.margin.digits
if self.profit is not None and self.revenue:
return (self.profit / self.revenue).quantize(
Decimal(1) / 10 ** digits[1])
def get_currency(self, name):
pool = Pool()
Company = pool.get('company.company')
company = Transaction().context.get('company')
if company is not None and company >= 0:
return Company(company).currency.id
@classmethod
def view_attributes(cls):
return super().view_attributes() + [
('/tree/field[@name="profit"]', 'visual',
If(Eval('profit', 0) < 0, 'danger', '')),
('/tree/field[@name="margin"]', 'visual',
If(Eval('margin', 0) < 0, 'danger', '')),
]

View File

@@ -0,0 +1,591 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<tryton>
<data>
<menuitem
name="Margins"
parent="product.menu_reporting"
sequence="50"
id="menu_reporting_margin"
icon="tryton-graph"/>
<record model="ir.ui.menu-res.group" id="menu_reporting_margin_group_product_admin">
<field name="menu" ref="menu_reporting_margin"/>
<field name="group" ref="product.group_product_admin"/>
</record>
<record model="ir.ui.view" id="reporting_margin_context_view_form">
<field name="model">stock.reporting.margin.context</field>
<field name="type">form</field>
<field name="name">reporting_margin_context_form</field>
</record>
<!-- Main -->
<record model="ir.ui.view" id="reporting_margin_main_view_list">
<field name="model">stock.reporting.margin.main</field>
<field name="type">tree</field>
<field name="name">reporting_margin_main_list</field>
</record>
<record model="ir.ui.view" id="reporting_margin_main_view_graph_margin">
<field name="model">stock.reporting.margin.main</field>
<field name="type">graph</field>
<field name="name">reporting_margin_main_graph_margin</field>
</record>
<record model="ir.ui.view" id="reporting_margin_main_view_graph_profit">
<field name="model">stock.reporting.margin.main</field>
<field name="type">graph</field>
<field name="name">reporting_margin_main_graph_profit</field>
</record>
<record model="ir.ui.view" id="reporting_margin_main_view_graph_amount">
<field name="model">stock.reporting.margin.main</field>
<field name="type">graph</field>
<field name="name">reporting_margin_main_graph_amount</field>
</record>
<record model="ir.action.act_window" id="act_reporting_margin_main">
<field name="name">Margins</field>
<field name="res_model">stock.reporting.margin.main</field>
<field name="context_model">stock.reporting.margin.context</field>
</record>
<record model="ir.action.act_window.view" id="act_reporting_margin_main_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="reporting_margin_main_view_list"/>
<field name="act_window" ref="act_reporting_margin_main"/>
</record>
<record model="ir.action.act_window.view" id="act_reporting_margin_main_view2">
<field name="sequence" eval="20"/>
<field name="view" ref="reporting_margin_main_view_graph_margin"/>
<field name="act_window" ref="act_reporting_margin_main"/>
</record>
<record model="ir.action.act_window.view" id="act_reporting_margin_main_view3">
<field name="sequence" eval="30"/>
<field name="view" ref="reporting_margin_main_view_graph_profit"/>
<field name="act_window" ref="act_reporting_margin_main"/>
</record>
<record model="ir.action.act_window.view" id="act_reporting_margin_main_view4">
<field name="sequence" eval="40"/>
<field name="view" ref="reporting_margin_main_view_graph_amount"/>
<field name="act_window" ref="act_reporting_margin_main"/>
</record>
<record model="ir.action.keyword" id="act_reporting_margin_main_keyword1">
<field name="keyword">tree_open</field>
<field name="model" ref="menu_reporting_margin"/>
<field name="action" ref="act_reporting_margin_main"/>
</record>
<record model="ir.rule.group" id="rule_group_reporting_margin_main_companies">
<field name="name">User in companies</field>
<field name="model">stock.reporting.margin.main</field>
<field name="global_p" eval="True"/>
</record>
<record model="ir.rule" id="rule_reporting_margin_main_companies">
<field name="domain"
eval="[('company', 'in', Eval('companies', []))]"
pyson="1"/>
<field name="rule_group" ref="rule_group_reporting_margin_main_companies"/>
</record>
<record model="ir.model.access" id="access_reporting_margin_main">
<field name="model">stock.reporting.margin.main</field>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_reporting_margin_main_product_admin">
<field name="model">stock.reporting.margin.main</field>
<field name="group" ref="product.group_product_admin"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.ui.view" id="reporting_margin_main_time_series_view_list">
<field name="model">stock.reporting.margin.main.time_series</field>
<field name="type">tree</field>
<field name="name">reporting_margin_main_time_series_list</field>
</record>
<record model="ir.ui.view" id="reporting_margin_main_time_series_view_graph_margin">
<field name="model">stock.reporting.margin.main.time_series</field>
<field name="type">graph</field>
<field name="name">reporting_margin_main_time_series_graph_margin</field>
</record>
<record model="ir.ui.view" id="reporting_margin_main_time_series_view_graph_profit">
<field name="model">stock.reporting.margin.main.time_series</field>
<field name="type">graph</field>
<field name="name">reporting_margin_main_time_series_graph_profit</field>
</record>
<record model="ir.ui.view" id="reporting_margin_main_time_series_view_graph_amount">
<field name="model">stock.reporting.margin.main.time_series</field>
<field name="type">graph</field>
<field name="name">reporting_margin_main_time_series_graph_amount</field>
</record>
<record model="ir.action.act_window" id="act_reporting_margin_main_time_series">
<field name="name">Margins</field>
<field name="res_model">stock.reporting.margin.main.time_series</field>
<field name="context_model">stock.reporting.margin.context</field>
<field name="order" eval="[('date', 'DESC')]" pyson="1"/>
</record>
<record model="ir.action.act_window.view" id="act_reporting_margin_main_time_series_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="reporting_margin_main_time_series_view_list"/>
<field name="act_window" ref="act_reporting_margin_main_time_series"/>
</record>
<record model="ir.action.act_window.view" id="act_reporting_margin_main_time_series_view2">
<field name="sequence" eval="20"/>
<field name="view" ref="reporting_margin_main_time_series_view_graph_margin"/>
<field name="act_window" ref="act_reporting_margin_main_time_series"/>
</record>
<record model="ir.action.act_window.view" id="act_reporting_margin_main_time_series_view3">
<field name="sequence" eval="30"/>
<field name="view" ref="reporting_margin_main_time_series_view_graph_profit"/>
<field name="act_window" ref="act_reporting_margin_main_time_series"/>
</record>
<record model="ir.action.act_window.view" id="act_reporting_margin_main_time_series_view4">
<field name="sequence" eval="40"/>
<field name="view" ref="reporting_margin_main_time_series_view_graph_amount"/>
<field name="act_window" ref="act_reporting_margin_main_time_series"/>
</record>
<record model="ir.action.keyword" id="act_reporting_margin_main_time_series_keyword1">
<field name="keyword">tree_open</field>
<field name="model">stock.reporting.margin.main,-1</field>
<field name="action" ref="act_reporting_margin_main_time_series"/>
</record>
<record model="ir.rule.group" id="rule_group_reporting_margin_main_time_series_companies">
<field name="name">User in companies</field>
<field name="model">stock.reporting.margin.main.time_series</field>
<field name="global_p" eval="True"/>
</record>
<record model="ir.rule" id="rule_reporting_margin_main_time_series_companies">
<field name="domain"
eval="[('company', 'in', Eval('companies', []))]"
pyson="1"/>
<field name="rule_group" ref="rule_group_reporting_margin_main_time_series_companies"/>
</record>
<record model="ir.model.access" id="access_reporting_margin_main_time_series">
<field name="model">stock.reporting.margin.main.time_series</field>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_reporting_margin_main_time_series_main_admin">
<field name="model">stock.reporting.margin.main.time_series</field>
<field name="group" ref="product.group_product_admin"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<!-- Product -->
<record model="ir.ui.view" id="reporting_margin_product_view_list">
<field name="model">stock.reporting.margin.product</field>
<field name="type" eval="None"/>
<field name="inherit" ref="reporting_margin_main_view_list"/>
<field name="name">reporting_margin_product_list</field>
</record>
<record model="ir.ui.view" id="reporting_margin_product_view_graph_margin">
<field name="model">stock.reporting.margin.product</field>
<field name="type" eval="None"/>
<field name="inherit" ref="reporting_margin_main_view_graph_margin"/>
<field name="name">reporting_margin_product_graph_margin</field>
</record>
<record model="ir.ui.view" id="reporting_margin_product_view_graph_profit">
<field name="model">stock.reporting.margin.product</field>
<field name="type" eval="None"/>
<field name="inherit" ref="reporting_margin_main_view_graph_profit"/>
<field name="name">reporting_margin_product_graph_profit</field>
</record>
<record model="ir.ui.view" id="reporting_margin_product_view_graph_amount">
<field name="model">stock.reporting.margin.product</field>
<field name="type" eval="None"/>
<field name="inherit" ref="reporting_margin_main_view_graph_amount"/>
<field name="name">reporting_margin_product_graph_amount</field>
</record>
<record model="ir.action.act_window" id="act_reporting_margin_product">
<field name="name">Margins per Product</field>
<field name="res_model">stock.reporting.margin.product</field>
<field name="context_model">stock.reporting.margin.context</field>
</record>
<record model="ir.action.act_window.view" id="act_reporting_margin_product_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="reporting_margin_product_view_list"/>
<field name="act_window" ref="act_reporting_margin_product"/>
</record>
<record model="ir.action.act_window.view" id="act_reporting_margin_product_view2">
<field name="sequence" eval="20"/>
<field name="view" ref="reporting_margin_product_view_graph_margin"/>
<field name="act_window" ref="act_reporting_margin_product"/>
</record>
<record model="ir.action.act_window.view" id="act_reporting_margin_product_view3">
<field name="sequence" eval="30"/>
<field name="view" ref="reporting_margin_product_view_graph_profit"/>
<field name="act_window" ref="act_reporting_margin_product"/>
</record>
<record model="ir.action.act_window.view" id="act_reporting_margin_product_view4">
<field name="sequence" eval="40"/>
<field name="view" ref="reporting_margin_product_view_graph_amount"/>
<field name="act_window" ref="act_reporting_margin_product"/>
</record>
<record model="ir.action.keyword" id="act_reporting_margin_product_keyword1">
<field name="keyword">tree_open</field>
<field name="model" ref="menu_reporting_margin"/>
<field name="action" ref="act_reporting_margin_product"/>
</record>
<record model="ir.rule.group" id="rule_group_reporting_margin_product_companies">
<field name="name">User in companies</field>
<field name="model">stock.reporting.margin.product</field>
<field name="global_p" eval="True"/>
</record>
<record model="ir.rule" id="rule_reporting_margin_product_companies">
<field name="domain"
eval="[('company', 'in', Eval('companies', []))]"
pyson="1"/>
<field name="rule_group" ref="rule_group_reporting_margin_product_companies"/>
</record>
<record model="ir.model.access" id="access_reporting_margin_product">
<field name="model">stock.reporting.margin.product</field>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_reporting_margin_product_product_admin">
<field name="model">stock.reporting.margin.product</field>
<field name="group" ref="product.group_product_admin"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.ui.view" id="reporting_margin_product_time_series_view_list">
<field name="model">stock.reporting.margin.product.time_series</field>
<field name="type" eval="None"/>
<field name="inherit" ref="reporting_margin_main_time_series_view_list"/>
<field name="name">reporting_margin_product_time_series_list</field>
</record>
<record model="ir.ui.view" id="reporting_margin_product_time_series_view_graph_margin">
<field name="model">stock.reporting.margin.product.time_series</field>
<field name="type">graph</field>
<field name="name">reporting_margin_main_time_series_graph_margin</field>
</record>
<record model="ir.ui.view" id="reporting_margin_product_time_series_view_graph_profit">
<field name="model">stock.reporting.margin.product.time_series</field>
<field name="type">graph</field>
<field name="name">reporting_margin_main_time_series_graph_profit</field>
</record>
<record model="ir.ui.view" id="reporting_margin_product_time_series_view_graph_amount">
<field name="model">stock.reporting.margin.product.time_series</field>
<field name="type">graph</field>
<field name="name">reporting_margin_main_time_series_graph_amount</field>
</record>
<record model="ir.action.act_window" id="act_reporting_margin_product_time_series">
<field name="name">Margins per Product</field>
<field name="res_model">stock.reporting.margin.product.time_series</field>
<field name="context_model">stock.reporting.margin.context</field>
<field
name="domain"
eval="[('product', '=', Eval('active_id', -1))]"
pyson="1"/>
<field name="order" eval="[('date', 'DESC')]" pyson="1"/>
</record>
<record model="ir.action.act_window.view" id="act_reporting_margin_product_time_series_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="reporting_margin_product_time_series_view_list"/>
<field name="act_window" ref="act_reporting_margin_product_time_series"/>
</record>
<record model="ir.action.act_window.view" id="act_reporting_margin_product_time_series_view2">
<field name="sequence" eval="20"/>
<field name="view" ref="reporting_margin_product_time_series_view_graph_margin"/>
<field name="act_window" ref="act_reporting_margin_product_time_series"/>
</record>
<record model="ir.action.act_window.view" id="act_reporting_margin_product_time_series_view3">
<field name="sequence" eval="30"/>
<field name="view" ref="reporting_margin_product_time_series_view_graph_profit"/>
<field name="act_window" ref="act_reporting_margin_product_time_series"/>
</record>
<record model="ir.action.act_window.view" id="act_reporting_margin_product_time_series_view4">
<field name="sequence" eval="40"/>
<field name="view" ref="reporting_margin_product_time_series_view_graph_amount"/>
<field name="act_window" ref="act_reporting_margin_product_time_series"/>
</record>
<record model="ir.action.keyword" id="act_reporting_margin_product_time_series_keyword1">
<field name="keyword">tree_open</field>
<field name="model">stock.reporting.margin.product,-1</field>
<field name="action" ref="act_reporting_margin_product_time_series"/>
</record>
<record model="ir.rule.group" id="rule_group_reporting_margin_product_time_series_companies">
<field name="name">User in companies</field>
<field name="model">stock.reporting.margin.product.time_series</field>
<field name="global_p" eval="True"/>
</record>
<record model="ir.rule" id="rule_reporting_margin_product_time_series_companies">
<field name="domain"
eval="[('company', 'in', Eval('companies', []))]"
pyson="1"/>
<field name="rule_group" ref="rule_group_reporting_margin_product_time_series_companies"/>
</record>
<record model="ir.model.access" id="access_reporting_margin_product_time_series">
<field name="model">stock.reporting.margin.product.time_series</field>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_reporting_margin_product_time_series_product_admin">
<field name="model">stock.reporting.margin.product.time_series</field>
<field name="group" ref="product.group_product_admin"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<!-- Category -->
<record model="ir.ui.view" id="reporting_margin_category_tree_view_tree">
<field name="model">stock.reporting.margin.category.tree</field>
<field name="type">tree</field>
<field name="field_childs">children</field>
<field name="name">reporting_margin_category_tree</field>
</record>
<record model="ir.action.act_window" id="act_reporting_margin_category_tree">
<field name="name">Margins per Category</field>
<field name="res_model">stock.reporting.margin.category.tree</field>
<field name="context_model">stock.reporting.margin.context</field>
<field name="domain" eval="[('parent', '=', None)]" pyson="1"/>
</record>
<record model="ir.action.act_window.view" id="act_reporting_margin_category_tree_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="reporting_margin_category_tree_view_tree"/>
<field name="act_window" ref="act_reporting_margin_category_tree"/>
</record>
<record model="ir.action.keyword" id="act_reporting_margin_category_tree_keyword1">
<field name="keyword">tree_open</field>
<field name="model" ref="menu_reporting_margin"/>
<field name="action" ref="act_reporting_margin_category_tree"/>
</record>
<record model="ir.model.access" id="access_reporting_margin_category_tree">
<field name="model">stock.reporting.margin.category.tree</field>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_reporting_margin_category_tree_product_admin">
<field name="model">stock.reporting.margin.category.tree</field>
<field name="group" ref="product.group_product_admin"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.ui.view" id="reporting_margin_category_view_list">
<field name="model">stock.reporting.margin.category</field>
<field name="type" eval="None"/>
<field name="inherit" ref="reporting_margin_main_view_list"/>
<field name="name">reporting_margin_category_list</field>
</record>
<record model="ir.ui.view" id="reporting_margin_category_view_graph_margin">
<field name="model">stock.reporting.margin.category</field>
<field name="type" eval="None"/>
<field name="inherit" ref="reporting_margin_main_view_graph_margin"/>
<field name="name">reporting_margin_category_graph_margin</field>
</record>
<record model="ir.ui.view" id="reporting_margin_category_view_graph_profit">
<field name="model">stock.reporting.margin.category</field>
<field name="type" eval="None"/>
<field name="inherit" ref="reporting_margin_main_view_graph_profit"/>
<field name="name">reporting_margin_category_graph_profit</field>
</record>
<record model="ir.ui.view" id="reporting_margin_category_view_graph_amount">
<field name="model">stock.reporting.margin.category</field>
<field name="type" eval="None"/>
<field name="inherit" ref="reporting_margin_main_view_graph_amount"/>
<field name="name">reporting_margin_category_graph_amount</field>
</record>
<record model="ir.action.act_window" id="act_reporting_margin_category">
<field name="name">Margins per Category</field>
<field name="res_model">stock.reporting.margin.category</field>
<field name="context_model">stock.reporting.margin.context</field>
<field
name="domain"
eval="[('category', 'child_of', Eval('active_ids', []), 'parent')]"
pyson="1"/>
</record>
<record model="ir.action.act_window.view" id="act_reporting_margin_category_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="reporting_margin_category_view_list"/>
<field name="act_window" ref="act_reporting_margin_category"/>
</record>
<record model="ir.action.act_window.view" id="act_reporting_margin_category_view2">
<field name="sequence" eval="20"/>
<field name="view" ref="reporting_margin_category_view_graph_margin"/>
<field name="act_window" ref="act_reporting_margin_category"/>
</record>
<record model="ir.action.act_window.view" id="act_reporting_margin_category_view3">
<field name="sequence" eval="30"/>
<field name="view" ref="reporting_margin_category_view_graph_profit"/>
<field name="act_window" ref="act_reporting_margin_category"/>
</record>
<record model="ir.action.act_window.view" id="act_reporting_margin_category_view4">
<field name="sequence" eval="40"/>
<field name="view" ref="reporting_margin_category_view_graph_amount"/>
<field name="act_window" ref="act_reporting_margin_category"/>
</record>
<record model="ir.action.keyword" id="act_reporting_margin_category_keyword1">
<field name="keyword">tree_open</field>
<field name="model">stock.reporting.margin.category.tree,-1</field>
<field name="action" ref="act_reporting_margin_category"/>
</record>
<record model="ir.rule.group" id="rule_group_reporting_margin_category_companies">
<field name="name">User in companies</field>
<field name="model">stock.reporting.margin.category</field>
<field name="global_p" eval="True"/>
</record>
<record model="ir.rule" id="rule_reporting_margin_category_companies">
<field name="domain"
eval="[('company', 'in', Eval('companies', []))]"
pyson="1"/>
<field name="rule_group" ref="rule_group_reporting_margin_category_companies"/>
</record>
<record model="ir.model.access" id="access_reporting_margin_category">
<field name="model">stock.reporting.margin.category</field>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_reporting_margin_category_product_admin">
<field name="model">stock.reporting.margin.category</field>
<field name="group" ref="product.group_product_admin"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.ui.view" id="reporting_margin_category_time_series_view_list">
<field name="model">stock.reporting.margin.category.time_series</field>
<field name="type">tree</field>
<field name="name">reporting_margin_main_time_series_list</field>
</record>
<record model="ir.ui.view" id="reporting_margin_category_time_series_view_graph_margin">
<field name="model">stock.reporting.margin.category.time_series</field>
<field name="type">graph</field>
<field name="name">reporting_margin_main_time_series_graph_margin</field>
</record>
<record model="ir.ui.view" id="reporting_margin_category_time_series_view_graph_profit">
<field name="model">stock.reporting.margin.category.time_series</field>
<field name="type">graph</field>
<field name="name">reporting_margin_main_time_series_graph_profit</field>
</record>
<record model="ir.ui.view" id="reporting_margin_category_time_series_view_graph_amount">
<field name="model">stock.reporting.margin.category.time_series</field>
<field name="type">graph</field>
<field name="name">reporting_margin_main_time_series_graph_amount</field>
</record>
<record model="ir.action.act_window" id="act_reporting_margin_category_time_series">
<field name="name">Margins per Category</field>
<field name="res_model">stock.reporting.margin.category.time_series</field>
<field name="context_model">stock.reporting.margin.context</field>
<field
name="domain"
eval="[('category', '=', Eval('active_id', -1))]"
pyson="1"/>
<field name="order" eval="[('date', 'DESC')]" pyson="1"/>
</record>
<record model="ir.action.act_window.view" id="act_reporting_margin_category_time_series_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="reporting_margin_category_time_series_view_list"/>
<field name="act_window" ref="act_reporting_margin_category_time_series"/>
</record>
<record model="ir.action.act_window.view" id="act_reporting_margin_category_time_series_view2">
<field name="sequence" eval="20"/>
<field name="view" ref="reporting_margin_category_time_series_view_graph_margin"/>
<field name="act_window" ref="act_reporting_margin_category_time_series"/>
</record>
<record model="ir.action.act_window.view" id="act_reporting_margin_category_time_series_view3">
<field name="sequence" eval="30"/>
<field name="view" ref="reporting_margin_category_time_series_view_graph_profit"/>
<field name="act_window" ref="act_reporting_margin_category_time_series"/>
</record>
<record model="ir.action.act_window.view" id="act_reporting_margin_category_time_series_view4">
<field name="sequence" eval="40"/>
<field name="view" ref="reporting_margin_category_time_series_view_graph_amount"/>
<field name="act_window" ref="act_reporting_margin_category_time_series"/>
</record>
<record model="ir.action.keyword" id="act_reporting_margin_category_time_series_keyword1">
<field name="keyword">tree_open</field>
<field name="model">stock.reporting.margin.category,-1</field>
<field name="action" ref="act_reporting_margin_category_time_series"/>
</record>
<record model="ir.rule.group" id="rule_group_reporting_margin_category_time_series_companies">
<field name="name">User in companies</field>
<field name="model">stock.reporting.margin.category.time_series</field>
<field name="global_p" eval="True"/>
</record>
<record model="ir.rule" id="rule_reporting_margin_category_time_series_companies">
<field name="domain"
eval="[('company', 'in', Eval('companies', []))]"
pyson="1"/>
<field name="rule_group" ref="rule_group_reporting_margin_category_time_series_companies"/>
</record>
<record model="ir.model.access" id="access_reporting_margin_category_time_series">
<field name="model">stock.reporting.margin.category.time_series</field>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_reporting_margin_category_time_series_product_admin">
<field name="model">stock.reporting.margin.category.time_series</field>
<field name="group" ref="product.group_product_admin"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
</data>
</tryton>

View File

@@ -0,0 +1,603 @@
<?xml version="1.0" encoding="UTF-8"?>
<office:document xmlns:css3t="http://www.w3.org/TR/css3-text/" xmlns:grddl="http://www.w3.org/2003/g/data-view#" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xforms="http://www.w3.org/2002/xforms" xmlns:dom="http://www.w3.org/2001/xml-events" xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" xmlns:ooow="http://openoffice.org/2004/writer" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:drawooo="http://openoffice.org/2010/draw" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" xmlns:tableooo="http://openoffice.org/2009/table" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:rpt="http://openoffice.org/2005/report" xmlns:formx="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:officeooo="http://openoffice.org/2009/office" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text">
<office:meta><meta:generator>LibreOffice/7.6.4.1$Linux_X86_64 LibreOffice_project/60$Build-1</meta:generator><meta:initial-creator>Bertrand Chenal</meta:initial-creator><meta:creation-date>2009-03-26T13:54:48</meta:creation-date><dc:date>2024-04-19T23:16:55.104218033</dc:date><meta:editing-cycles>55</meta:editing-cycles><meta:editing-duration>PT7H1M30S</meta:editing-duration><meta:document-statistic meta:table-count="1" meta:image-count="0" meta:object-count="0" meta:page-count="3" meta:paragraph-count="46" meta:word-count="103" meta:character-count="1103" meta:non-whitespace-character-count="1045"/><meta:user-defined meta:name="Info 1"/><meta:user-defined meta:name="Info 2"/><meta:user-defined meta:name="Info 3"/><meta:user-defined meta:name="Info 4"/></office:meta>
<office:settings>
<config:config-item-set config:name="ooo:view-settings">
<config:config-item config:name="ViewAreaTop" config:type="long">0</config:config-item>
<config:config-item config:name="ViewAreaLeft" config:type="long">0</config:config-item>
<config:config-item config:name="ViewAreaWidth" config:type="long">25269</config:config-item>
<config:config-item config:name="ViewAreaHeight" config:type="long">23973</config:config-item>
<config:config-item config:name="ShowRedlineChanges" config:type="boolean">true</config:config-item>
<config:config-item config:name="InBrowseMode" config:type="boolean">false</config:config-item>
<config:config-item-map-indexed config:name="Views">
<config:config-item-map-entry>
<config:config-item config:name="ViewId" config:type="string">view2</config:config-item>
<config:config-item config:name="ViewLeft" config:type="long">5955</config:config-item>
<config:config-item config:name="ViewTop" config:type="long">10248</config:config-item>
<config:config-item config:name="VisibleLeft" config:type="long">0</config:config-item>
<config:config-item config:name="VisibleTop" config:type="long">0</config:config-item>
<config:config-item config:name="VisibleRight" config:type="long">25268</config:config-item>
<config:config-item config:name="VisibleBottom" config:type="long">23971</config:config-item>
<config:config-item config:name="ZoomType" config:type="short">0</config:config-item>
<config:config-item config:name="ViewLayoutColumns" config:type="short">0</config:config-item>
<config:config-item config:name="ViewLayoutBookMode" config:type="boolean">false</config:config-item>
<config:config-item config:name="ZoomFactor" config:type="short">100</config:config-item>
<config:config-item config:name="IsSelectedFrame" config:type="boolean">false</config:config-item>
<config:config-item config:name="KeepRatio" config:type="boolean">false</config:config-item>
<config:config-item config:name="AnchoredTextOverflowLegacy" config:type="boolean">false</config:config-item>
<config:config-item config:name="LegacySingleLineFontwork" config:type="boolean">false</config:config-item>
<config:config-item config:name="ConnectorUseSnapRect" config:type="boolean">false</config:config-item>
<config:config-item config:name="IgnoreBreakAfterMultilineField" config:type="boolean">false</config:config-item>
</config:config-item-map-entry>
</config:config-item-map-indexed>
</config:config-item-set>
<config:config-item-set config:name="ooo:configuration-settings">
<config:config-item config:name="PrintRightPages" config:type="boolean">true</config:config-item>
<config:config-item config:name="PrintProspectRTL" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintLeftPages" config:type="boolean">true</config:config-item>
<config:config-item config:name="PrintPaperFromSetup" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintControls" config:type="boolean">true</config:config-item>
<config:config-item config:name="PrintProspect" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintBlackFonts" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintAnnotationMode" config:type="short">0</config:config-item>
<config:config-item config:name="PrintEmptyPages" config:type="boolean">true</config:config-item>
<config:config-item config:name="PrintSingleJobs" config:type="boolean">false</config:config-item>
<config:config-item config:name="AutoFirstLineIndentDisregardLineSpace" config:type="boolean">false</config:config-item>
<config:config-item config:name="HeaderSpacingBelowLastPara" config:type="boolean">false</config:config-item>
<config:config-item config:name="ProtectBookmarks" config:type="boolean">false</config:config-item>
<config:config-item config:name="ContinuousEndnotes" config:type="boolean">false</config:config-item>
<config:config-item config:name="DisableOffPagePositioning" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintTables" config:type="boolean">true</config:config-item>
<config:config-item config:name="SubtractFlysAnchoredAtFlys" config:type="boolean">true</config:config-item>
<config:config-item config:name="ApplyParagraphMarkFormatToNumbering" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintFaxName" config:type="string"/>
<config:config-item config:name="SurroundTextWrapSmall" config:type="boolean">false</config:config-item>
<config:config-item config:name="TreatSingleColumnBreakAsPageBreak" config:type="boolean">false</config:config-item>
<config:config-item config:name="PropLineSpacingShrinksFirstLine" config:type="boolean">false</config:config-item>
<config:config-item config:name="TabOverSpacing" config:type="boolean">false</config:config-item>
<config:config-item config:name="TabOverMargin" config:type="boolean">false</config:config-item>
<config:config-item config:name="EmbedComplexScriptFonts" config:type="boolean">true</config:config-item>
<config:config-item config:name="EmbedLatinScriptFonts" config:type="boolean">true</config:config-item>
<config:config-item config:name="EmbedOnlyUsedFonts" config:type="boolean">false</config:config-item>
<config:config-item config:name="EmbedFonts" config:type="boolean">false</config:config-item>
<config:config-item config:name="ClippedPictures" config:type="boolean">false</config:config-item>
<config:config-item config:name="FrameAutowidthWithMorePara" config:type="boolean">false</config:config-item>
<config:config-item config:name="FloattableNomargins" config:type="boolean">false</config:config-item>
<config:config-item config:name="UnbreakableNumberings" config:type="boolean">false</config:config-item>
<config:config-item config:name="AllowPrintJobCancel" config:type="boolean">true</config:config-item>
<config:config-item config:name="UseFormerObjectPositioning" config:type="boolean">false</config:config-item>
<config:config-item config:name="UseOldNumbering" config:type="boolean">false</config:config-item>
<config:config-item config:name="RsidRoot" config:type="int">1289861</config:config-item>
<config:config-item config:name="PrinterPaperFromSetup" config:type="boolean">false</config:config-item>
<config:config-item config:name="CurrentDatabaseDataSource" config:type="string"/>
<config:config-item config:name="UpdateFromTemplate" config:type="boolean">true</config:config-item>
<config:config-item config:name="AddFrameOffsets" config:type="boolean">false</config:config-item>
<config:config-item config:name="LoadReadonly" config:type="boolean">false</config:config-item>
<config:config-item config:name="Rsid" config:type="int">2119093</config:config-item>
<config:config-item config:name="FootnoteInColumnToPageEnd" config:type="boolean">true</config:config-item>
<config:config-item config:name="ProtectFields" config:type="boolean">false</config:config-item>
<config:config-item config:name="SaveGlobalDocumentLinks" config:type="boolean">false</config:config-item>
<config:config-item config:name="ClipAsCharacterAnchoredWriterFlyFrames" config:type="boolean">false</config:config-item>
<config:config-item config:name="LinkUpdateMode" config:type="short">1</config:config-item>
<config:config-item config:name="AddExternalLeading" config:type="boolean">true</config:config-item>
<config:config-item config:name="PrintGraphics" config:type="boolean">true</config:config-item>
<config:config-item config:name="EmbedSystemFonts" config:type="boolean">false</config:config-item>
<config:config-item config:name="IsLabelDocument" config:type="boolean">false</config:config-item>
<config:config-item config:name="AddParaLineSpacingToTableCells" config:type="boolean">false</config:config-item>
<config:config-item config:name="UseFormerTextWrapping" config:type="boolean">false</config:config-item>
<config:config-item config:name="HyphenateURLs" config:type="boolean">true</config:config-item>
<config:config-item config:name="AddParaTableSpacingAtStart" config:type="boolean">true</config:config-item>
<config:config-item config:name="TabsRelativeToIndent" config:type="boolean">true</config:config-item>
<config:config-item config:name="FieldAutoUpdate" config:type="boolean">true</config:config-item>
<config:config-item config:name="SaveVersionOnClose" config:type="boolean">false</config:config-item>
<config:config-item config:name="ChartAutoUpdate" config:type="boolean">true</config:config-item>
<config:config-item config:name="ImagePreferredDPI" config:type="int">0</config:config-item>
<config:config-item config:name="PrinterSetup" config:type="base64Binary"/>
<config:config-item config:name="SmallCapsPercentage66" config:type="boolean">true</config:config-item>
<config:config-item config:name="AlignTabStopPosition" config:type="boolean">true</config:config-item>
<config:config-item config:name="DropCapPunctuation" config:type="boolean">false</config:config-item>
<config:config-item config:name="MathBaselineAlignment" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrinterName" config:type="string"/>
<config:config-item config:name="CharacterCompressionType" config:type="short">0</config:config-item>
<config:config-item config:name="AddParaTableSpacing" config:type="boolean">true</config:config-item>
<config:config-item config:name="DoNotJustifyLinesWithManualBreak" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintHiddenText" config:type="boolean">false</config:config-item>
<config:config-item config:name="IsKernAsianPunctuation" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrinterIndependentLayout" config:type="string">high-resolution</config:config-item>
<config:config-item config:name="TabOverflow" config:type="boolean">false</config:config-item>
<config:config-item config:name="AddParaSpacingToTableCells" config:type="boolean">true</config:config-item>
<config:config-item config:name="AddVerticalFrameOffsets" config:type="boolean">false</config:config-item>
<config:config-item config:name="TabAtLeftIndentForParagraphsInList" config:type="boolean">false</config:config-item>
<config:config-item config:name="ApplyUserData" config:type="boolean">true</config:config-item>
<config:config-item config:name="MsWordCompMinLineHeightByFly" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintTextPlaceholder" config:type="boolean">false</config:config-item>
<config:config-item config:name="IgnoreFirstLineIndentInNumbering" config:type="boolean">false</config:config-item>
<config:config-item config:name="UseFormerLineSpacing" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintPageBackground" config:type="boolean">true</config:config-item>
<config:config-item config:name="RedlineProtectionKey" config:type="base64Binary"/>
<config:config-item config:name="EmbedAsianScriptFonts" config:type="boolean">true</config:config-item>
<config:config-item config:name="BackgroundParaOverDrawings" config:type="boolean">false</config:config-item>
<config:config-item config:name="SaveThumbnail" config:type="boolean">true</config:config-item>
<config:config-item config:name="ConsiderTextWrapOnObjPos" config:type="boolean">false</config:config-item>
<config:config-item config:name="EmbeddedDatabaseName" config:type="string"/>
<config:config-item config:name="ProtectForm" config:type="boolean">false</config:config-item>
<config:config-item config:name="DoNotResetParaAttrsForNumFont" config:type="boolean">false</config:config-item>
<config:config-item config:name="MsWordCompTrailingBlanks" config:type="boolean">false</config:config-item>
<config:config-item config:name="EmptyDbFieldHidesPara" config:type="boolean">false</config:config-item>
<config:config-item config:name="TableRowKeep" config:type="boolean">false</config:config-item>
<config:config-item config:name="NoNumberingShowFollowBy" config:type="boolean">false</config:config-item>
<config:config-item config:name="InvertBorderSpacing" config:type="boolean">false</config:config-item>
<config:config-item config:name="IgnoreTabsAndBlanksForLineCalculation" config:type="boolean">false</config:config-item>
<config:config-item config:name="DoNotCaptureDrawObjsOnPage" config:type="boolean">false</config:config-item>
<config:config-item config:name="GutterAtTop" config:type="boolean">false</config:config-item>
<config:config-item config:name="StylesNoDefault" config:type="boolean">false</config:config-item>
<config:config-item config:name="UnxForceZeroExtLeading" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintReversed" config:type="boolean">false</config:config-item>
<config:config-item config:name="UseOldPrinterMetrics" config:type="boolean">false</config:config-item>
<config:config-item config:name="CurrentDatabaseCommandType" config:type="int">0</config:config-item>
<config:config-item config:name="PrintDrawings" config:type="boolean">true</config:config-item>
<config:config-item config:name="OutlineLevelYieldsNumbering" config:type="boolean">false</config:config-item>
<config:config-item config:name="CurrentDatabaseCommand" config:type="string"/>
<config:config-item config:name="CollapseEmptyCellPara" config:type="boolean">true</config:config-item>
</config:config-item-set>
</office:settings>
<office:scripts>
<office:script script:language="ooo:Basic">
<ooo:libraries xmlns:ooo="http://openoffice.org/2004/office" xmlns:xlink="http://www.w3.org/1999/xlink"/>
</office:script>
</office:scripts>
<office:font-face-decls>
<style:font-face style:name="Andale Sans UI" svg:font-family="&apos;Andale Sans UI&apos;" style:font-family-generic="system" style:font-pitch="variable"/>
<style:font-face style:name="DejaVu Sans" svg:font-family="&apos;DejaVu Sans&apos;" style:font-family-generic="system" style:font-pitch="variable"/>
<style:font-face style:name="Liberation Sans" svg:font-family="&apos;Liberation Sans&apos;" style:font-adornments="Regular" style:font-family-generic="swiss" style:font-pitch="variable"/>
<style:font-face style:name="Liberation Serif" svg:font-family="&apos;Liberation Serif&apos;" style:font-family-generic="roman" style:font-pitch="variable"/>
<style:font-face style:name="Liberation Serif1" svg:font-family="&apos;Liberation Serif&apos;" style:font-adornments="Bold" style:font-family-generic="roman" style:font-pitch="variable"/>
<style:font-face style:name="Liberation Serif2" svg:font-family="&apos;Liberation Serif&apos;" style:font-adornments="Regular" style:font-family-generic="roman" style:font-pitch="variable"/>
<style:font-face style:name="StarSymbol" svg:font-family="StarSymbol"/>
<style:font-face style:name="Thorndale AMT" svg:font-family="&apos;Thorndale AMT&apos;" style:font-family-generic="roman" style:font-pitch="variable"/>
</office:font-face-decls>
<office:styles>
<style:default-style style:family="graphic">
<style:graphic-properties svg:stroke-color="#000000" draw:fill-color="#99ccff" fo:wrap-option="no-wrap" draw:shadow-offset-x="0.3cm" draw:shadow-offset-y="0.3cm" draw:start-line-spacing-horizontal="0.283cm" draw:start-line-spacing-vertical="0.283cm" draw:end-line-spacing-horizontal="0.283cm" draw:end-line-spacing-vertical="0.283cm" style:writing-mode="lr-tb" style:flow-with-text="false"/>
<style:paragraph-properties style:text-autospace="ideograph-alpha" style:line-break="strict" loext:tab-stop-distance="0cm" style:writing-mode="lr-tb" style:font-independent-line-spacing="false">
<style:tab-stops/>
</style:paragraph-properties>
<style:text-properties style:use-window-font-color="true" loext:opacity="0%" style:font-name="Thorndale AMT" fo:font-size="12pt" fo:language="en" fo:country="US" style:letter-kerning="true" style:font-name-asian="Andale Sans UI" style:font-size-asian="10.5pt" style:language-asian="zxx" style:country-asian="none" style:font-name-complex="Andale Sans UI" style:font-size-complex="12pt" style:language-complex="zxx" style:country-complex="none"/>
</style:default-style>
<style:default-style style:family="paragraph">
<style:paragraph-properties fo:hyphenation-ladder-count="no-limit" style:text-autospace="ideograph-alpha" style:punctuation-wrap="hanging" style:line-break="strict" style:tab-stop-distance="1.251cm" style:writing-mode="lr-tb"/>
<style:text-properties style:use-window-font-color="true" loext:opacity="0%" style:font-name="Thorndale AMT" fo:font-size="12pt" fo:language="en" fo:country="US" style:letter-kerning="true" style:font-name-asian="Andale Sans UI" style:font-size-asian="10.5pt" style:language-asian="zxx" style:country-asian="none" style:font-name-complex="Andale Sans UI" style:font-size-complex="12pt" style:language-complex="zxx" style:country-complex="none" fo:hyphenate="false" fo:hyphenation-remain-char-count="2" fo:hyphenation-push-char-count="2" loext:hyphenation-no-caps="false" loext:hyphenation-no-last-word="false" loext:hyphenation-word-char-count="5" loext:hyphenation-zone="no-limit"/>
</style:default-style>
<style:default-style style:family="table">
<style:table-properties table:border-model="collapsing"/>
</style:default-style>
<style:default-style style:family="table-row">
<style:table-row-properties fo:keep-together="auto"/>
</style:default-style>
<style:style style:name="Standard" style:family="paragraph" style:class="text">
<style:text-properties style:font-name="Liberation Sans" fo:font-family="&apos;Liberation Sans&apos;" style:font-style-name="Regular" style:font-family-generic="swiss" style:font-pitch="variable" style:font-size-asian="10.5pt"/>
</style:style>
<style:style style:name="Heading" style:family="paragraph" style:parent-style-name="Standard" style:next-style-name="Text_20_body" style:class="text">
<style:paragraph-properties fo:margin-top="0.423cm" fo:margin-bottom="0.212cm" style:contextual-spacing="false" fo:keep-with-next="always"/>
<style:text-properties style:font-name="Liberation Serif2" fo:font-family="&apos;Liberation Serif&apos;" style:font-style-name="Regular" style:font-family-generic="roman" style:font-pitch="variable" fo:font-size="16pt" style:font-name-asian="DejaVu Sans" style:font-family-asian="&apos;DejaVu Sans&apos;" style:font-family-generic-asian="system" style:font-pitch-asian="variable" style:font-size-asian="14pt" style:font-name-complex="DejaVu Sans" style:font-family-complex="&apos;DejaVu Sans&apos;" style:font-family-generic-complex="system" style:font-pitch-complex="variable" style:font-size-complex="14pt"/>
</style:style>
<style:style style:name="Text_20_body" style:display-name="Text body" style:family="paragraph" style:parent-style-name="Standard" style:class="text">
<style:paragraph-properties fo:margin-top="0cm" fo:margin-bottom="0.212cm" style:contextual-spacing="false"/>
<style:text-properties style:font-name="Liberation Sans" fo:font-family="&apos;Liberation Sans&apos;" style:font-style-name="Regular" style:font-family-generic="swiss" style:font-pitch="variable" style:font-size-asian="10.5pt"/>
</style:style>
<style:style style:name="List" style:family="paragraph" style:parent-style-name="Text_20_body" style:class="list">
<style:text-properties style:font-size-asian="12pt"/>
</style:style>
<style:style style:name="Caption" style:family="paragraph" style:parent-style-name="Standard" style:class="extra">
<style:paragraph-properties fo:margin-top="0.212cm" fo:margin-bottom="0.212cm" style:contextual-spacing="false" text:number-lines="false" text:line-number="0"/>
<style:text-properties fo:font-size="12pt" fo:font-style="italic" style:font-size-asian="12pt" style:font-style-asian="italic" style:font-size-complex="12pt" style:font-style-complex="italic"/>
</style:style>
<style:style style:name="Index" style:family="paragraph" style:parent-style-name="Standard" style:class="index">
<style:paragraph-properties text:number-lines="false" text:line-number="0"/>
<style:text-properties style:font-size-asian="12pt"/>
</style:style>
<style:style style:name="Heading_20_1" style:display-name="Heading 1" style:family="paragraph" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:class="text">
<style:text-properties fo:font-size="16pt" fo:font-weight="bold" style:font-size-asian="115%" style:font-weight-asian="bold" style:font-size-complex="115%" style:font-weight-complex="bold"/>
</style:style>
<style:style style:name="Table_20_Contents" style:display-name="Table Contents" style:family="paragraph" style:parent-style-name="Standard" style:class="extra">
<style:paragraph-properties text:number-lines="false" text:line-number="0"/>
<style:text-properties style:font-size-asian="10.5pt"/>
</style:style>
<style:style style:name="Table_20_Heading" style:display-name="Table Heading" style:family="paragraph" style:parent-style-name="Table_20_Contents" style:class="extra" style:master-page-name="">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false" style:page-number="auto" text:number-lines="false" text:line-number="0"/>
<style:text-properties style:font-name="Liberation Serif1" fo:font-family="&apos;Liberation Serif&apos;" style:font-style-name="Bold" style:font-family-generic="roman" style:font-pitch="variable" fo:font-weight="bold" style:font-size-asian="10.5pt" style:font-weight-asian="bold" style:font-weight-complex="bold"/>
</style:style>
<style:style style:name="Header_20_and_20_Footer" style:display-name="Header and Footer" style:family="paragraph" style:parent-style-name="Standard" style:class="extra">
<style:paragraph-properties text:number-lines="false" text:line-number="0">
<style:tab-stops>
<style:tab-stop style:position="8.795cm" style:type="center"/>
<style:tab-stop style:position="17.59cm" style:type="right"/>
</style:tab-stops>
</style:paragraph-properties>
</style:style>
<style:style style:name="Header" style:family="paragraph" style:parent-style-name="Standard" style:class="extra">
<style:paragraph-properties text:number-lines="false" text:line-number="0">
<style:tab-stops>
<style:tab-stop style:position="8.795cm" style:type="center"/>
<style:tab-stop style:position="17.59cm" style:type="right"/>
</style:tab-stops>
</style:paragraph-properties>
<style:text-properties fo:font-size="9pt" style:font-size-asian="10.5pt"/>
</style:style>
<style:style style:name="Heading_20_2" style:display-name="Heading 2" style:family="paragraph" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:class="text">
<style:text-properties fo:font-size="14pt" fo:font-style="italic" fo:font-weight="bold" style:font-size-asian="14pt" style:font-style-asian="italic" style:font-weight-asian="bold" style:font-size-complex="14pt" style:font-style-complex="italic" style:font-weight-complex="bold"/>
</style:style>
<style:style style:name="Footer" style:family="paragraph" style:parent-style-name="Standard" style:class="extra">
<style:paragraph-properties text:number-lines="false" text:line-number="0">
<style:tab-stops>
<style:tab-stop style:position="8.795cm" style:type="center"/>
<style:tab-stop style:position="17.59cm" style:type="right"/>
</style:tab-stops>
</style:paragraph-properties>
<style:text-properties fo:font-size="9pt" style:font-size-asian="10.5pt"/>
</style:style>
<style:style style:name="Heading_20_3" style:display-name="Heading 3" style:family="paragraph" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:class="text">
<style:text-properties fo:font-size="14pt" fo:font-weight="bold" style:font-size-asian="14pt" style:font-weight-asian="bold" style:font-size-complex="14pt" style:font-weight-complex="bold"/>
</style:style>
<style:style style:name="Text_20_body_20_indent" style:display-name="Text body indent" style:family="paragraph" style:parent-style-name="Text_20_body" style:class="text">
<style:paragraph-properties fo:margin-left="0.499cm" fo:margin-right="0cm" fo:margin-top="0cm" fo:margin-bottom="0cm" style:contextual-spacing="false" fo:text-indent="0cm" style:auto-text-indent="false"/>
</style:style>
<style:style style:name="Text" style:family="paragraph" style:parent-style-name="Caption" style:class="extra"/>
<style:style style:name="Quotations" style:family="paragraph" style:parent-style-name="Standard" style:class="html">
<style:paragraph-properties fo:margin-left="1cm" fo:margin-right="1cm" fo:margin-top="0cm" fo:margin-bottom="0.499cm" style:contextual-spacing="false" fo:text-indent="0cm" style:auto-text-indent="false"/>
</style:style>
<style:style style:name="Title" style:family="paragraph" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:class="chapter">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
<style:text-properties fo:font-size="28pt" fo:font-weight="bold" style:font-size-asian="28pt" style:font-weight-asian="bold" style:font-size-complex="28pt" style:font-weight-complex="bold"/>
</style:style>
<style:style style:name="Subtitle" style:family="paragraph" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:class="chapter">
<style:paragraph-properties fo:margin-top="0.106cm" fo:margin-bottom="0.212cm" style:contextual-spacing="false" fo:text-align="center" style:justify-single-word="false"/>
<style:text-properties fo:font-size="18pt" style:font-size-asian="18pt" style:font-size-complex="18pt"/>
</style:style>
<style:style style:name="Frame_20_contents" style:display-name="Frame contents" style:family="paragraph" style:parent-style-name="Standard" style:class="extra"/>
<style:style style:name="Placeholder" style:family="text">
<style:text-properties fo:font-variant="small-caps" fo:color="#008080" loext:opacity="100%" style:text-underline-style="dotted" style:text-underline-width="auto" style:text-underline-color="font-color"/>
</style:style>
<style:style style:name="Bullet_20_Symbols" style:display-name="Bullet Symbols" style:family="text">
<style:text-properties style:font-name="StarSymbol" fo:font-family="StarSymbol" fo:font-size="9pt" style:font-name-asian="StarSymbol" style:font-family-asian="StarSymbol" style:font-size-asian="9pt" style:font-name-complex="StarSymbol" style:font-family-complex="StarSymbol" style:font-size-complex="9pt"/>
</style:style>
<style:style style:name="Frame" style:family="graphic">
<style:graphic-properties text:anchor-type="paragraph" svg:x="0cm" svg:y="0cm" fo:margin-left="0.201cm" fo:margin-right="0.201cm" fo:margin-top="0.201cm" fo:margin-bottom="0.201cm" style:wrap="parallel" style:number-wrapped-paragraphs="no-limit" style:wrap-contour="false" style:vertical-pos="top" style:vertical-rel="paragraph-content" style:horizontal-pos="center" style:horizontal-rel="paragraph-content" fo:background-color="transparent" draw:fill="none" draw:fill-color="#99ccff" fo:padding="0.15cm" fo:border="0.06pt solid #000000"/>
</style:style>
<text:outline-style style:name="Outline">
<text:outline-level-style text:level="1" loext:num-list-format="%1%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
<text:outline-level-style text:level="2" loext:num-list-format="%2%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
<text:outline-level-style text:level="3" loext:num-list-format="%3%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
<text:outline-level-style text:level="4" loext:num-list-format="%4%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
<text:outline-level-style text:level="5" loext:num-list-format="%5%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
<text:outline-level-style text:level="6" loext:num-list-format="%6%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
<text:outline-level-style text:level="7" loext:num-list-format="%7%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
<text:outline-level-style text:level="8" loext:num-list-format="%8%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
<text:outline-level-style text:level="9" loext:num-list-format="%9%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
<text:outline-level-style text:level="10" loext:num-list-format="%10%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
</text:outline-style>
<text:notes-configuration text:note-class="footnote" style:num-format="1" text:start-value="0" text:footnotes-position="page" text:start-numbering-at="document"/>
<text:notes-configuration text:note-class="endnote" style:num-format="i" text:start-value="0"/>
<text:linenumbering-configuration text:number-lines="false" text:offset="0.499cm" style:num-format="1" text:number-position="left" text:increment="5"/>
<loext:theme loext:name="Office Theme">
<loext:theme-colors loext:name="LibreOffice">
<loext:color loext:name="dark1" loext:color="#000000"/>
<loext:color loext:name="light1" loext:color="#ffffff"/>
<loext:color loext:name="dark2" loext:color="#000000"/>
<loext:color loext:name="light2" loext:color="#ffffff"/>
<loext:color loext:name="accent1" loext:color="#18a303"/>
<loext:color loext:name="accent2" loext:color="#0369a3"/>
<loext:color loext:name="accent3" loext:color="#a33e03"/>
<loext:color loext:name="accent4" loext:color="#8e03a3"/>
<loext:color loext:name="accent5" loext:color="#c99c00"/>
<loext:color loext:name="accent6" loext:color="#c9211e"/>
<loext:color loext:name="hyperlink" loext:color="#0000ee"/>
<loext:color loext:name="followed-hyperlink" loext:color="#551a8b"/>
</loext:theme-colors>
</loext:theme>
</office:styles>
<office:automatic-styles>
<style:style style:name="Table1" style:family="table">
<style:table-properties style:width="16.999cm" fo:margin-left="0cm" fo:margin-right="0cm" fo:margin-top="0.381cm" fo:margin-bottom="0cm" table:align="margins"/>
</style:style>
<style:style style:name="Table1.A" style:family="table-column">
<style:table-column-properties style:column-width="4.683cm" style:rel-column-width="18053*"/>
</style:style>
<style:style style:name="Table1.C" style:family="table-column">
<style:table-column-properties style:column-width="4.685cm" style:rel-column-width="18060*"/>
</style:style>
<style:style style:name="Table1.D" style:family="table-column">
<style:table-column-properties style:column-width="2.949cm" style:rel-column-width="11369*"/>
</style:style>
<style:style style:name="Table1.A1" style:family="table-cell">
<style:table-cell-properties fo:background-color="#cccccc" fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="0.05pt solid #000000" fo:border-bottom="0.05pt solid #000000">
<style:background-image/>
</style:table-cell-properties>
</style:style>
<style:style style:name="Table1.D1" style:family="table-cell">
<style:table-cell-properties fo:background-color="#cccccc" fo:padding="0.097cm" fo:border="0.05pt solid #000000">
<style:background-image/>
</style:table-cell-properties>
</style:style>
<style:style style:name="Table1.A2" style:family="table-cell">
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
</style:style>
<style:style style:name="Table1.C2" style:family="table-cell">
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
</style:style>
<style:style style:name="Table1.D2" style:family="table-cell">
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="0.05pt solid #000000" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
</style:style>
<style:style style:name="Table1.A3" style:family="table-cell">
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
</style:style>
<style:style style:name="Table1.B3" style:family="table-cell">
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
</style:style>
<style:style style:name="Table1.C3" style:family="table-cell">
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
</style:style>
<style:style style:name="Table1.D3" style:family="table-cell">
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="0.05pt solid #000000" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
</style:style>
<style:style style:name="Table1.A4" style:family="table-cell">
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
</style:style>
<style:style style:name="Table1.C4" style:family="table-cell">
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
</style:style>
<style:style style:name="Table1.D4" style:family="table-cell">
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="0.05pt solid #000000" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
</style:style>
<style:style style:name="P1" style:family="paragraph" style:parent-style-name="Header">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
<style:text-properties officeooo:paragraph-rsid="0018924f"/>
</style:style>
<style:style style:name="P2" style:family="paragraph" style:parent-style-name="Footer">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
<style:text-properties officeooo:paragraph-rsid="0018924f"/>
</style:style>
<style:style style:name="P3" style:family="paragraph" style:parent-style-name="Header">
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false"/>
<style:text-properties fo:font-size="12pt" officeooo:paragraph-rsid="002055b5" style:font-size-asian="12pt" style:font-size-complex="12pt"/>
</style:style>
<style:style style:name="P4" style:family="paragraph" style:parent-style-name="Frame_20_contents">
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false"/>
</style:style>
<style:style style:name="P5" style:family="paragraph" style:parent-style-name="Footer">
<style:paragraph-properties fo:text-align="end" style:justify-single-word="false"/>
</style:style>
<style:style style:name="P6" style:family="paragraph" style:parent-style-name="Header">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
<style:text-properties officeooo:paragraph-rsid="0018924f"/>
</style:style>
<style:style style:name="P7" style:family="paragraph" style:parent-style-name="Footer">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
<style:text-properties officeooo:paragraph-rsid="0018924f"/>
</style:style>
<style:style style:name="P8" style:family="paragraph" style:parent-style-name="Footer">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
<style:text-properties officeooo:paragraph-rsid="001db22b"/>
</style:style>
<style:style style:name="P9" style:family="paragraph" style:parent-style-name="Header">
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false"/>
<style:text-properties fo:font-size="12pt" officeooo:paragraph-rsid="001db22b" style:font-size-asian="12pt" style:font-size-complex="12pt"/>
</style:style>
<style:style style:name="P10" style:family="paragraph" style:parent-style-name="Header">
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false"/>
<style:text-properties fo:font-size="12pt" officeooo:paragraph-rsid="0018924f" style:font-size-asian="12pt" style:font-size-complex="12pt"/>
</style:style>
<style:style style:name="P11" style:family="paragraph" style:parent-style-name="Frame_20_contents">
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false"/>
</style:style>
<style:style style:name="P12" style:family="paragraph" style:parent-style-name="Footer">
<style:paragraph-properties fo:text-align="end" style:justify-single-word="false"/>
</style:style>
<style:style style:name="P13" style:family="paragraph" style:parent-style-name="Table_20_Contents">
<style:paragraph-properties fo:text-align="end" style:justify-single-word="false"/>
</style:style>
<style:style style:name="P14" style:family="paragraph" style:parent-style-name="Table_20_Heading">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
<style:text-properties style:font-name="Liberation Serif"/>
</style:style>
<style:style style:name="P15" style:family="paragraph" style:parent-style-name="Standard">
<style:paragraph-properties fo:break-before="page"/>
</style:style>
<style:style style:name="P16" style:family="paragraph" style:parent-style-name="Standard">
<style:text-properties officeooo:paragraph-rsid="0014daa2"/>
</style:style>
<style:style style:name="P17" style:family="paragraph" style:parent-style-name="Heading_20_1" style:master-page-name="">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false" style:page-number="auto" fo:break-before="page"/>
<style:text-properties style:text-underline-style="solid" style:text-underline-width="auto" style:text-underline-color="font-color"/>
</style:style>
<style:style style:name="P18" style:family="paragraph" style:parent-style-name="Heading_20_1">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
<style:text-properties style:text-underline-style="solid" style:text-underline-width="auto" style:text-underline-color="font-color"/>
</style:style>
<style:style style:name="P19" style:family="paragraph" style:parent-style-name="Heading_20_1">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
<style:text-properties style:text-underline-style="solid" style:text-underline-width="auto" style:text-underline-color="font-color" officeooo:paragraph-rsid="001c0678"/>
</style:style>
<style:style style:name="P20" style:family="paragraph" style:parent-style-name="Standard">
<style:text-properties officeooo:paragraph-rsid="001c0678"/>
</style:style>
<style:style style:name="P21" style:family="paragraph" style:parent-style-name="Frame_20_contents">
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false"/>
</style:style>
<style:style style:name="P22" style:family="paragraph" style:parent-style-name="Header">
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false"/>
<style:text-properties fo:font-size="12pt" officeooo:paragraph-rsid="002055b5" style:font-size-asian="12pt" style:font-size-complex="12pt"/>
</style:style>
<style:style style:name="T1" style:family="text">
<style:text-properties fo:font-weight="bold" style:font-weight-asian="bold" style:font-weight-complex="bold"/>
</style:style>
<style:style style:name="T2" style:family="text">
<style:text-properties fo:font-weight="bold" officeooo:rsid="001a23ce" style:font-weight-asian="bold" style:font-weight-complex="bold"/>
</style:style>
<style:style style:name="T3" style:family="text">
<style:text-properties fo:font-weight="normal" style:font-weight-asian="normal" style:font-weight-complex="normal"/>
</style:style>
<style:style style:name="T4" style:family="text">
<style:text-properties officeooo:rsid="001c0678"/>
</style:style>
<style:style style:name="fr1" style:family="graphic" style:parent-style-name="Frame">
<style:graphic-properties fo:margin-left="0cm" fo:margin-right="0cm" fo:margin-top="0cm" fo:margin-bottom="0cm" style:wrap="parallel" style:number-wrapped-paragraphs="no-limit" style:vertical-pos="middle" style:vertical-rel="baseline" style:horizontal-pos="left" style:horizontal-rel="paragraph" draw:opacity="100%" fo:padding="0cm" fo:border="none" draw:wrap-influence-on-position="once-concurrent" loext:allow-overlap="true">
<style:columns fo:column-count="1" fo:column-gap="0cm"/>
</style:graphic-properties>
</style:style>
<style:page-layout style:name="pm1">
<style:page-layout-properties fo:page-width="20.999cm" fo:page-height="29.699cm" style:num-format="1" style:print-orientation="portrait" fo:margin-top="2cm" fo:margin-bottom="2cm" fo:margin-left="2cm" fo:margin-right="2cm" style:writing-mode="lr-tb" style:layout-grid-color="#c0c0c0" style:layout-grid-lines="20" style:layout-grid-base-height="0.706cm" style:layout-grid-ruby-height="0.353cm" style:layout-grid-mode="none" style:layout-grid-ruby-below="false" style:layout-grid-print="false" style:layout-grid-display="false" style:footnote-max-height="0cm" loext:margin-gutter="0cm">
<style:footnote-sep style:width="0.018cm" style:distance-before-sep="0.101cm" style:distance-after-sep="0.101cm" style:line-style="none" style:adjustment="left" style:rel-width="25%" style:color="#000000"/>
</style:page-layout-properties>
<style:header-style>
<style:header-footer-properties fo:min-height="0cm" fo:margin-left="0cm" fo:margin-right="0cm" fo:margin-bottom="0.499cm"/>
</style:header-style>
<style:footer-style>
<style:header-footer-properties fo:min-height="0cm" fo:margin-left="0cm" fo:margin-right="0cm" fo:margin-top="0.499cm"/>
</style:footer-style>
</style:page-layout>
<style:style style:name="dp1" style:family="drawing-page">
<style:drawing-page-properties draw:background-size="full"/>
</style:style>
</office:automatic-styles>
<office:master-styles>
<style:master-page style:name="Standard" style:page-layout-name="pm1" draw:style-name="dp1">
<style:header>
<text:p text:style-name="P1"><text:placeholder text:placeholder-type="text">&lt;if test=&quot;company and company.header&quot;&gt;</text:placeholder></text:p>
<text:p text:style-name="P1"><text:placeholder text:placeholder-type="text">&lt;for each=&quot;line in company.header_used.split(&apos;\n&apos;)&quot;&gt;</text:placeholder></text:p>
<text:p text:style-name="P1"><text:placeholder text:placeholder-type="text">&lt;line&gt;</text:placeholder></text:p>
<text:p text:style-name="P1"><text:placeholder text:placeholder-type="text">&lt;/for&gt;</text:placeholder></text:p>
<text:p text:style-name="P2"><text:placeholder text:placeholder-type="text">&lt;/if&gt;</text:placeholder></text:p>
<text:p text:style-name="P3"><text:placeholder text:placeholder-type="text">&lt;choose&gt;</text:placeholder></text:p>
<text:p text:style-name="P3"><text:placeholder text:placeholder-type="text">&lt;when test=&quot;company and company.logo&quot;&gt;</text:placeholder></text:p>
<text:p text:style-name="P3"><draw:frame draw:style-name="fr1" draw:name="image:company.get_logo_cm(7, 3.5)" text:anchor-type="as-char" svg:width="7.001cm" draw:z-index="2">
<draw:text-box fo:min-height="3cm">
<text:p text:style-name="P4"/>
</draw:text-box>
</draw:frame></text:p>
<text:p text:style-name="P3"><text:placeholder text:placeholder-type="text">&lt;/when&gt;</text:placeholder></text:p>
<text:p text:style-name="P3"><text:placeholder text:placeholder-type="text">&lt;when test=&quot;company&quot;&gt;</text:placeholder></text:p>
<text:p text:style-name="P3"><text:placeholder text:placeholder-type="text">&lt;company.rec_name&gt;</text:placeholder></text:p>
<text:p text:style-name="P3"><text:placeholder text:placeholder-type="text">&lt;/when&gt;</text:placeholder></text:p>
<text:p text:style-name="P3"><text:placeholder text:placeholder-type="text">&lt;/choose&gt;</text:placeholder></text:p>
</style:header>
<style:footer>
<text:p text:style-name="P2"><text:placeholder text:placeholder-type="text">&lt;if test=&quot;company and company.footer&quot;&gt;</text:placeholder></text:p>
<text:p text:style-name="P2"><text:placeholder text:placeholder-type="text">&lt;for each=&quot;line in company.footer_used.split(&apos;\n&apos;)&quot;&gt;</text:placeholder></text:p>
<text:p text:style-name="P2"><text:placeholder text:placeholder-type="text">&lt;line&gt;</text:placeholder></text:p>
<text:p text:style-name="P2"><text:placeholder text:placeholder-type="text">&lt;/for&gt;</text:placeholder></text:p>
<text:p text:style-name="P2"><text:placeholder text:placeholder-type="text">&lt;/if&gt;</text:placeholder></text:p>
<text:p text:style-name="P5"><text:page-number text:select-page="current">3</text:page-number>/<text:page-count>3</text:page-count></text:p>
</style:footer>
</style:master-page>
</office:master-styles>
<office:body>
<office:text text:use-soft-page-breaks="true">
<office:forms form:automatic-focus="false" form:apply-design-mode="false"/>
<text:sequence-decls>
<text:sequence-decl text:display-outline-level="0" text:name="Illustration"/>
<text:sequence-decl text:display-outline-level="0" text:name="Table"/>
<text:sequence-decl text:display-outline-level="0" text:name="Text"/>
<text:sequence-decl text:display-outline-level="0" text:name="Drawing"/>
<text:sequence-decl text:display-outline-level="0" text:name="Figure"/>
</text:sequence-decls>
<text:p text:style-name="P15"><text:placeholder text:placeholder-type="text">&lt;for each=&quot;shipment in records&quot;&gt;</text:placeholder></text:p>
<text:p text:style-name="P15"><text:placeholder text:placeholder-type="text">&lt;choose test=&quot;shipment.state&quot;&gt;</text:placeholder></text:p>
<text:p text:style-name="Standard"><text:placeholder text:placeholder-type="text">&lt;when test=&quot;&apos;draft&apos;&quot;&gt;</text:placeholder></text:p>
<text:p text:style-name="P19"><text:span text:style-name="T4">Draft </text:span>Restocking List</text:p>
<text:p text:style-name="Standard"><text:placeholder text:placeholder-type="text">&lt;/when&gt;</text:placeholder></text:p>
<text:p text:style-name="P20"><text:placeholder text:placeholder-type="text">&lt;when test=&quot;&apos;cancelled&apos;&quot;&gt;</text:placeholder></text:p>
<text:p text:style-name="P19"><text:span text:style-name="T4">Cancelled </text:span>Restocking List</text:p>
<text:p text:style-name="P20"><text:placeholder text:placeholder-type="text">&lt;/when&gt;</text:placeholder></text:p>
<text:p text:style-name="Standard"><text:placeholder text:placeholder-type="text">&lt;otherwise&gt;</text:placeholder></text:p>
<text:p text:style-name="P18">Restocking List No: <text:span text:style-name="T4"><text:placeholder text:placeholder-type="text">&lt;shipment.number&gt;</text:placeholder></text:span></text:p>
<text:p text:style-name="Standard"><text:placeholder text:placeholder-type="text">&lt;/otherwise&gt;</text:placeholder></text:p>
<text:p text:style-name="Standard"><text:placeholder text:placeholder-type="text">&lt;/choose&gt;</text:placeholder></text:p>
<text:p text:style-name="Standard"><text:span text:style-name="T1">Reference:</text:span><text:span text:style-name="T3"> </text:span><text:span text:style-name="T3"><text:placeholder text:placeholder-type="text">&lt;shipment.origins or &apos;&apos;&gt;</text:placeholder></text:span><text:span text:style-name="T3"><text:placeholder text:placeholder-type="text">&lt;&apos;, &apos; if (shipment.origins and shipment.reference) else &apos;&apos;&gt;</text:placeholder></text:span><text:span text:style-name="T3"><text:placeholder text:placeholder-type="text">&lt;shipment.reference or &apos;&apos;&gt;</text:placeholder></text:span></text:p>
<text:p text:style-name="Standard"><text:span text:style-name="T1">Supplier:</text:span><text:span text:style-name="T3"> </text:span><text:span text:style-name="T3"><text:placeholder text:placeholder-type="text">&lt;shipment.supplier.rec_name&gt;</text:placeholder></text:span></text:p>
<text:p text:style-name="Standard"><text:span text:style-name="T1">Planned Date:</text:span><text:span text:style-name="T3"> </text:span><text:span text:style-name="T3"><text:placeholder text:placeholder-type="text">&lt;shipment.planned_date and format_date(shipment.planned_date, user.language) or &apos;&apos;&gt;</text:placeholder></text:span></text:p>
<text:p text:style-name="Standard"><text:span text:style-name="T1">Warehouse:</text:span><text:span text:style-name="T3"> </text:span><text:span text:style-name="T3"><text:placeholder text:placeholder-type="text">&lt;shipment.warehouse.rec_name&gt;</text:placeholder></text:span></text:p>
<table:table table:name="Table1" table:style-name="Table1">
<table:table-column table:style-name="Table1.A" table:number-columns-repeated="2"/>
<table:table-column table:style-name="Table1.C"/>
<table:table-column table:style-name="Table1.D"/>
<table:table-header-rows>
<table:table-row>
<table:table-cell table:style-name="Table1.A1" office:value-type="string">
<text:p text:style-name="P14">From Location</text:p>
</table:table-cell>
<table:table-cell table:style-name="Table1.A1" office:value-type="string">
<text:p text:style-name="P14">To Location</text:p>
</table:table-cell>
<table:table-cell table:style-name="Table1.A1" office:value-type="string">
<text:p text:style-name="P14">Product</text:p>
</table:table-cell>
<table:table-cell table:style-name="Table1.D1" office:value-type="string">
<text:p text:style-name="P14">Quantity</text:p>
</table:table-cell>
</table:table-row>
</table:table-header-rows>
<table:table-row>
<table:table-cell table:style-name="Table1.A2" table:number-columns-spanned="2" office:value-type="string">
<text:p text:style-name="Table_20_Contents"><text:placeholder text:placeholder-type="text">&lt;for each=&quot;move in moves(shipment)&quot;&gt;</text:placeholder></text:p>
</table:table-cell>
<table:covered-table-cell/>
<table:table-cell table:style-name="Table1.C2" office:value-type="string">
<text:p text:style-name="Table_20_Contents"/>
</table:table-cell>
<table:table-cell table:style-name="Table1.D2" office:value-type="string">
<text:p text:style-name="Table_20_Contents"/>
</table:table-cell>
</table:table-row>
<table:table-row>
<table:table-cell table:style-name="Table1.A3" office:value-type="string">
<text:p text:style-name="Table_20_Contents"><text:placeholder text:placeholder-type="text">&lt;move.from_location_name&gt;</text:placeholder></text:p>
</table:table-cell>
<table:table-cell table:style-name="Table1.A3" office:value-type="string">
<text:p text:style-name="Table_20_Contents"><text:placeholder text:placeholder-type="text">&lt;move.to_location_name&gt;</text:placeholder></text:p>
</table:table-cell>
<table:table-cell table:style-name="Table1.C2" office:value-type="string">
<text:p text:style-name="Table_20_Contents"><text:placeholder text:placeholder-type="text">&lt;move.product.rec_name&gt;</text:placeholder></text:p>
</table:table-cell>
<table:table-cell table:style-name="Table1.D2" office:value-type="string">
<text:p text:style-name="P13"><text:placeholder text:placeholder-type="text">&lt;format_number_symbol(move.quantity, user.language, move.unit, digits=move.unit.digits)&gt;</text:placeholder><text:soft-page-break/></text:p>
</table:table-cell>
</table:table-row>
<table:table-row>
<table:table-cell table:style-name="Table1.A2" table:number-columns-spanned="2" office:value-type="string">
<text:p text:style-name="Table_20_Contents"><text:placeholder text:placeholder-type="text">&lt;/for&gt;</text:placeholder></text:p>
</table:table-cell>
<table:covered-table-cell/>
<table:table-cell table:style-name="Table1.C2" office:value-type="string">
<text:p text:style-name="Table_20_Contents"/>
</table:table-cell>
<table:table-cell table:style-name="Table1.D2" office:value-type="string">
<text:p text:style-name="Table_20_Contents"/>
</table:table-cell>
</table:table-row>
</table:table>
<text:p text:style-name="Text_20_body"><text:placeholder text:placeholder-type="text">&lt;/for&gt;</text:placeholder></text:p>
</office:text>
</office:body>
</office:document>

View File

@@ -0,0 +1,2 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.

View File

@@ -0,0 +1,246 @@
========================
Stock Average Cost Price
========================
Imports::
>>> import datetime as dt
>>> from decimal import Decimal
>>> from proteus import Model, Wizard
>>> from trytond.modules.company.tests.tools import create_company
>>> from trytond.modules.currency.tests.tools import get_currency
>>> from trytond.tests.tools import activate_modules
>>> today = dt.date.today()
>>> next_day = today + dt.timedelta(days=1)
Activate modules::
>>> config = activate_modules('stock', create_company)
Get currency::
>>> currency = get_currency()
Create product::
>>> ProductUom = Model.get('product.uom')
>>> ProductTemplate = Model.get('product.template')
>>> Product = Model.get('product.product')
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
>>> template = ProductTemplate()
>>> template.name = 'Product'
>>> template.default_uom = unit
>>> template.type = 'goods'
>>> template.list_price = Decimal('300')
>>> template.cost_price_method = 'average'
>>> product, = template.products
>>> product.cost_price = Decimal('80')
>>> template.save()
>>> product, = template.products
>>> template = ProductTemplate()
>>> template.name = 'Negative Product'
>>> template.default_uom = unit
>>> template.type = 'goods'
>>> template.list_price = Decimal('28')
>>> template.cost_price_method = 'average'
>>> negative_product, = template.products
>>> negative_product.cost_price = Decimal('5.0000')
>>> template.save()
>>> negative_product, = template.products
Get stock locations::
>>> Location = Model.get('stock.location')
>>> supplier_loc, = Location.find([('code', '=', 'SUP')])
>>> storage_loc, = Location.find([('code', '=', 'STO')])
>>> storage_sub_loc = Location(
... name="Storage Sub", type='storage', parent=storage_loc)
>>> storage_sub_loc.save()
>>> customer_loc, = Location.find([('code', '=', 'CUS')])
Make 1 unit of the product available @ 100 ::
>>> StockMove = Model.get('stock.move')
>>> incoming_move = StockMove()
>>> incoming_move.product = product
>>> incoming_move.unit = unit
>>> incoming_move.quantity = 1
>>> incoming_move.from_location = supplier_loc
>>> incoming_move.to_location = storage_sub_loc
>>> incoming_move.planned_date = today
>>> incoming_move.effective_date = today
>>> incoming_move.unit_price = Decimal('100')
>>> incoming_move.currency = currency
>>> incoming_move.click('do')
Check Cost Price is 100::
>>> product.reload()
>>> product.cost_price
Decimal('100.0000')
Add 1 more unit @ 200::
>>> incoming_move = StockMove()
>>> incoming_move.product = product
>>> incoming_move.unit = unit
>>> incoming_move.quantity = 1
>>> incoming_move.from_location = supplier_loc
>>> incoming_move.to_location = storage_loc
>>> incoming_move.planned_date = today
>>> incoming_move.effective_date = today
>>> incoming_move.unit_price = Decimal('200')
>>> incoming_move.currency = currency
>>> incoming_move.click('do')
Check Cost Price Average is 150::
>>> product.reload()
>>> product.cost_price
Decimal('150.0000')
Add twice 1 more unit @ 200::
>>> incoming_moves = []
>>> incoming_move = StockMove()
>>> incoming_move.product = product
>>> incoming_move.unit = unit
>>> incoming_move.quantity = 1
>>> incoming_move.from_location = supplier_loc
>>> incoming_move.to_location = storage_loc
>>> incoming_move.planned_date = today
>>> incoming_move.effective_date = today
>>> incoming_move.unit_price = Decimal('200')
>>> incoming_move.currency = currency
>>> incoming_move.save()
>>> incoming_moves.append(incoming_move)
>>> incoming_move = StockMove()
>>> incoming_move.product = product
>>> incoming_move.unit = unit
>>> incoming_move.quantity = 1
>>> incoming_move.from_location = supplier_loc
>>> incoming_move.to_location = storage_loc
>>> incoming_move.planned_date = today
>>> incoming_move.effective_date = today
>>> incoming_move.unit_price = Decimal('200')
>>> incoming_move.currency = currency
>>> incoming_move.save()
>>> incoming_moves.append(incoming_move)
>>> StockMove.click(incoming_moves, 'do')
Check Cost Price Average is 175::
>>> product.reload()
>>> product.cost_price
Decimal('175.0000')
Reduce Cost Price by 80%, to force to write recomputed price later::
>>> modify_cost_price = Wizard('product.modify_cost_price', [product])
>>> modify_cost_price.form.cost_price = 'cost_price * 0.8'
>>> modify_cost_price.form.date = next_day
>>> modify_cost_price.execute('modify')
>>> product.cost_price
Decimal('140.0000')
Increase Cost Price by 10% using Template wizard::
>>> modify_cost_price = Wizard(
... 'product.modify_cost_price', [product.template])
>>> modify_cost_price.form.cost_price = 'cost_price * 1.1'
>>> modify_cost_price.form.date = next_day
>>> modify_cost_price.execute('modify')
>>> product.reload()
>>> product.cost_price
Decimal('154.0000')
Send one product we don't have in stock::
>>> outgoing_move = StockMove()
>>> outgoing_move.product = negative_product
>>> outgoing_move.unit = unit
>>> outgoing_move.quantity = 1
>>> outgoing_move.unit_price = Decimal('28')
>>> outgoing_move.from_location = storage_loc
>>> outgoing_move.to_location = customer_loc
>>> outgoing_move.planned_date = today
>>> outgoing_move.effective_date = today
>>> outgoing_move.currency = currency
>>> outgoing_move.click('do')
Cost price should stay 5::
>>> negative_product.cost_price
Decimal('5.0000')
Return one product to the supplier::
>>> outgoing_move = StockMove()
>>> outgoing_move.product = negative_product
>>> outgoing_move.unit = unit
>>> outgoing_move.quantity = 1
>>> outgoing_move.unit_price = Decimal('28')
>>> outgoing_move.currency = currency
>>> outgoing_move.from_location = storage_loc
>>> outgoing_move.to_location = supplier_loc
>>> outgoing_move.planned_date = today
>>> outgoing_move.effective_date = today
>>> outgoing_move.click('do')
Cost price should stay 5::
>>> negative_product.cost_price
Decimal('5.0000')
Receive one unit of the product with negative stock so the stock stays negative::
>>> incoming_move = StockMove()
>>> incoming_move.product = negative_product
>>> incoming_move.unit = unit
>>> incoming_move.quantity = 1
>>> incoming_move.from_location = supplier_loc
>>> incoming_move.to_location = storage_loc
>>> incoming_move.planned_date = today
>>> incoming_move.effective_date = today
>>> incoming_move.unit_price = Decimal('3')
>>> incoming_move.currency = currency
>>> incoming_move.click('do')
Cost price should be set to last unit price::
>>> negative_product.reload()
>>> negative_product.cost_price
Decimal('3.0000')
Receive two units of the product so the stock becomes positive::
>>> incoming_move = StockMove()
>>> incoming_move.product = negative_product
>>> incoming_move.unit = unit
>>> incoming_move.quantity = 2
>>> incoming_move.from_location = supplier_loc
>>> incoming_move.to_location = storage_loc
>>> incoming_move.planned_date = today
>>> incoming_move.effective_date = today
>>> incoming_move.unit_price = Decimal('2')
>>> incoming_move.currency = currency
>>> incoming_move.click('do')
Cost price should be set to last unit price::
>>> negative_product.reload()
>>> negative_product.cost_price
Decimal('2.0000')
Recompute Cost Price::
>>> recompute = Wizard('product.recompute_cost_price', [negative_product])
>>> recompute.execute('recompute')
>>> negative_product.cost_price
Decimal('2.0000')

View File

@@ -0,0 +1,250 @@
========================
Stock Inventory Scenario
========================
Imports::
>>> import datetime as dt
>>> from decimal import Decimal
>>> from proteus import Model
>>> from trytond.modules.company.tests.tools import create_company
>>> from trytond.modules.currency.tests.tools import get_currency
>>> from trytond.tests.tools import activate_modules, assertEqual
>>> today = dt.date.today()
Activate modules::
>>> config = activate_modules('stock', create_company)
Get currency::
>>> currency = get_currency()
Get stock locations::
>>> Location = Model.get('stock.location')
>>> supplier_loc, = Location.find([('code', '=', 'SUP')])
>>> storage_loc, = Location.find([('code', '=', 'STO')])
>>> customer_loc, = Location.find([('code', '=', 'CUS')])
>>> lost_found_loc, = Location.find([('type', '=', 'lost_found')])
Create products::
>>> ProductUom = Model.get('product.uom')
>>> ProductTemplate = Model.get('product.template')
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
>>> template = ProductTemplate()
>>> template.name = 'Product'
>>> template.default_uom = unit
>>> template.type = 'goods'
>>> template.list_price = Decimal('300')
>>> template.cost_price_method = 'average'
>>> product, = template.products
>>> product.cost_price = Decimal('80')
>>> template.save()
>>> product, = template.products
>>> kg, = ProductUom.find([('name', '=', 'Kilogram')])
>>> template2 = ProductTemplate()
>>> template2.name = 'Product'
>>> template2.default_uom = kg
>>> template2.type = 'goods'
>>> template2.list_price = Decimal('140')
>>> template2.cost_price_method = 'average'
>>> product2, = template2.products
>>> product2.cost_price = Decimal('60')
>>> template2.save()
>>> product2, = template2.products
>>> template = ProductTemplate()
>>> template.name = 'Product'
>>> template.default_uom = unit
>>> template.type = 'goods'
>>> template.consumable = True
>>> template.list_price = Decimal('300')
>>> template.cost_price_method = 'average'
>>> consumable, = template.products
>>> consumable.cost_price = Decimal('80')
>>> template.save()
>>> consumable, = template.products
Fill storage::
>>> StockMove = Model.get('stock.move')
>>> incoming_move = StockMove()
>>> incoming_move.product = product
>>> incoming_move.unit = unit
>>> incoming_move.quantity = 1
>>> incoming_move.from_location = supplier_loc
>>> incoming_move.to_location = storage_loc
>>> incoming_move.planned_date = today
>>> incoming_move.effective_date = today
>>> incoming_move.unit_price = Decimal('100')
>>> incoming_move.currency = currency
>>> incoming_moves = [incoming_move]
>>> incoming_move = StockMove()
>>> incoming_move.product = product2
>>> incoming_move.unit = kg
>>> incoming_move.quantity = 2.5
>>> incoming_move.from_location = supplier_loc
>>> incoming_move.to_location = storage_loc
>>> incoming_move.planned_date = today
>>> incoming_move.effective_date = today
>>> incoming_move.unit_price = Decimal('70')
>>> incoming_move.currency = currency
>>> incoming_moves.append(incoming_move)
>>> StockMove.click(incoming_moves, 'do')
Create an inventory::
>>> Inventory = Model.get('stock.inventory')
>>> inventory = Inventory()
>>> inventory.location = storage_loc
>>> inventory.empty_quantity = 'keep'
>>> inventory.save()
>>> inventory.click('complete_lines')
>>> line_by_product = {l.product.id: l for l in inventory.lines}
>>> line_p1 = line_by_product[product.id]
>>> line_p1.expected_quantity
1.0
>>> line_p1.quantity = 3
>>> line_p2 = line_by_product[product2.id]
>>> line_p2.expected_quantity
2.5
>>> inventory.save()
Fill storage with more quantities::
>>> incoming_move = StockMove()
>>> incoming_move.product = product
>>> incoming_move.unit = unit
>>> incoming_move.quantity = 1
>>> incoming_move.from_location = supplier_loc
>>> incoming_move.to_location = storage_loc
>>> incoming_move.planned_date = today
>>> incoming_move.effective_date = today
>>> incoming_move.unit_price = Decimal('100')
>>> incoming_move.currency = currency
>>> incoming_moves = [incoming_move]
>>> incoming_move = StockMove()
>>> incoming_move.product = product2
>>> incoming_move.unit = kg
>>> incoming_move.quantity = 1.3
>>> incoming_move.from_location = supplier_loc
>>> incoming_move.to_location = storage_loc
>>> incoming_move.planned_date = today
>>> incoming_move.effective_date = today
>>> incoming_move.unit_price = Decimal('70')
>>> incoming_move.currency = currency
>>> incoming_moves.append(incoming_move)
>>> StockMove.click(incoming_moves, 'do')
Update the inventory::
>>> inventory.click('complete_lines')
>>> line_p1.reload()
>>> line_p1.expected_quantity
2.0
>>> line_p1.quantity
3.0
>>> line_p2.reload()
>>> line_p2.expected_quantity
3.8
>>> line_p2.quantity = 3.8
>>> line_p2.save()
Confirm the inventory::
>>> inventory.click('confirm')
>>> line_p1.reload()
>>> line_p1.expected_quantity
2.0
>>> move, = line_p1.moves
>>> move.quantity
1.0
>>> assertEqual(move.from_location, lost_found_loc)
>>> assertEqual(move.to_location, inventory.location)
>>> line_p2.reload()
>>> len(line_p2.moves)
0
Creating an inventory with empty quantities::
>>> inventory = Inventory()
>>> inventory.location = storage_loc
>>> inventory.empty_quantity = 'keep'
>>> line = inventory.lines.new()
>>> line.product = product
>>> inventory.click('confirm')
>>> line, = inventory.lines
>>> len(line.moves)
0
Empty storage::
>>> Inventory = Model.get('stock.inventory')
>>> inventory = Inventory()
>>> inventory.location = storage_loc
>>> inventory.empty_quantity = 'keep'
>>> line = inventory.lines.new()
>>> line.product = product
>>> line.quantity = 0
>>> line = inventory.lines.new()
>>> line.product = product2
>>> line.quantity = 0
>>> inventory.save()
>>> line_p1, line_p2 = inventory.lines
>>> line_p1.quantity
0.0
>>> line_p1.expected_quantity
3.0
>>> line_p2.quantity
0.0
>>> line_p2.expected_quantity
3.8
>>> inventory.click('confirm')
Add quantity of consumable product::
>>> inventory = Inventory()
>>> inventory.location = storage_loc
>>> inventory.empty_quantity = 'keep'
>>> line = inventory.lines.new()
>>> line.product = consumable
>>> line.quantity = 5.0
>>> inventory.click('complete_lines')
>>> len(inventory.lines)
1
>>> inventory.click('confirm')
>>> line, = inventory.lines
>>> move, = line.moves
>>> move.quantity
5.0
>>> assertEqual(move.from_location, lost_found_loc)
>>> assertEqual(move.to_location, inventory.location)
Create an inventory that should be empty after completion::
>>> Inventory = Model.get('stock.inventory')
>>> inventory = Inventory()
>>> inventory.location = storage_loc
>>> inventory.empty_quantity = 'keep'
>>> inventory.click('complete_lines')
>>> len(inventory.lines)
0
Create an inventory and check rec_name::
>>> Inventory = Model.get('stock.inventory')
>>> inventory = Inventory()
>>> inventory.date = dt.date(2023, 1, 31)
>>> inventory.location = storage_loc
>>> inventory.empty_quantity = 'keep'
>>> inventory.save()
>>> inventory.rec_name
'[6] [STO] Storage Zone @ 01/31/2023'

View File

@@ -0,0 +1,111 @@
==============================
Stock Inventory Count Scenario
==============================
Imports::
>>> from decimal import Decimal
>>> from proteus import Model
>>> from trytond.modules.company.tests.tools import create_company
>>> from trytond.tests.tools import activate_modules
Activate modules::
>>> config = activate_modules('stock', create_company)
Get stock locations::
>>> Location = Model.get('stock.location')
>>> supplier_loc, = Location.find([('code', '=', 'SUP')])
>>> storage_loc, = Location.find([('code', '=', 'STO')])
>>> customer_loc, = Location.find([('code', '=', 'CUS')])
Create products::
>>> ProductUom = Model.get('product.uom')
>>> ProductTemplate = Model.get('product.template')
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
>>> template = ProductTemplate()
>>> template.name = 'Product'
>>> template.default_uom = unit
>>> template.type = 'goods'
>>> template.list_price = Decimal('300')
>>> template.cost_price_method = 'average'
>>> product, = template.products
>>> product.cost_price = Decimal('80')
>>> template.save()
>>> product, = template.products
>>> kg, = ProductUom.find([('name', '=', 'Kilogram')])
>>> template2 = ProductTemplate()
>>> template2.name = 'Product'
>>> template2.default_uom = kg
>>> template2.type = 'goods'
>>> template2.list_price = Decimal('140')
>>> template2.cost_price_method = 'average'
>>> product2, = template2.products
>>> product2.cost_price = Decimal('60')
>>> template2.save()
>>> product2, = template2.products
Create an inventory::
>>> Inventory = Model.get('stock.inventory')
>>> inventory = Inventory()
>>> inventory.location = storage_loc
>>> inventory.empty_quantity = 'keep'
>>> inventory.save()
Count inventory::
>>> count = inventory.click('do_count')
>>> count.form.search = product
>>> count.execute('quantity')
Traceback (most recent call last):
...
InventoryCountWarning: ...
>>> Model.get('res.user.warning')(user=config.user,
... name='stock.inventory,%s.product.product,%s.count_create' % (
... inventory.id, product.id)).save()
>>> count.execute('quantity')
>>> count.form.quantity
1.0
>>> count.form.total_quantity
1.0
>>> count.execute('add')
>>> count.form.search = product
>>> count.execute('quantity')
>>> count.form.total_quantity
2.0
>>> count.execute('add')
>>> count.form.search = product2
>>> Model.get('res.user.warning')(user=config.user,
... name='stock.inventory,%s.product.product,%s.count_create' % (
... inventory.id, product2.id)).save()
>>> count.execute('quantity')
>>> count.form.quantity
>>> count.form.total_quantity
>>> count.form.quantity = 10
>>> count.form.total_quantity
10.0
>>> count.execute('add')
>>> count.execute('end')
Check inventory::
>>> len(inventory.lines)
2
>>> line1, = [l for l in inventory.lines if l.product == product]
>>> line1.quantity
2.0
>>> line2, = [l for l in inventory.lines if l.product == product2]
>>> line2.quantity
10.0

View File

@@ -0,0 +1,81 @@
=======================================
Stock Inventory Scenario Empty Quantity
=======================================
Imports::
>>> import datetime as dt
>>> from decimal import Decimal
>>> from proteus import Model
>>> from trytond.modules.company.tests.tools import create_company
>>> from trytond.modules.currency.tests.tools import get_currency
>>> from trytond.tests.tools import activate_modules, assertEqual
>>> today = dt.date.today()
Activate modules::
>>> config = activate_modules('stock', create_company)
Get stock locations::
>>> Location = Model.get('stock.location')
>>> supplier_loc, = Location.find([('code', '=', 'SUP')])
>>> storage_loc, = Location.find([('code', '=', 'STO')])
>>> customer_loc, = Location.find([('code', '=', 'CUS')])
>>> lost_found_loc, = Location.find([('type', '=', 'lost_found')])
Create product::
>>> ProductUom = Model.get('product.uom')
>>> ProductTemplate = Model.get('product.template')
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
>>> template = ProductTemplate()
>>> template.name = 'Product'
>>> template.default_uom = unit
>>> template.type = 'goods'
>>> template.list_price = Decimal('300')
>>> template.cost_price_method = 'average'
>>> product, = template.products
>>> product.cost_price = Decimal('80')
>>> template.save()
>>> product, = template.products
Fill storage::
>>> StockMove = Model.get('stock.move')
>>> incoming_move = StockMove()
>>> incoming_move.product = product
>>> incoming_move.unit = unit
>>> incoming_move.quantity = 1
>>> incoming_move.from_location = supplier_loc
>>> incoming_move.to_location = storage_loc
>>> incoming_move.planned_date = today
>>> incoming_move.effective_date = today
>>> incoming_move.unit_price = Decimal('100')
>>> incoming_move.currency = get_currency()
>>> incoming_move.click('do')
Creating an inventory with empty quantities creates and empty move::
>>> Inventory = Model.get('stock.inventory')
>>> inventory = Inventory()
>>> inventory.location = storage_loc
>>> inventory.empty_quantity = 'empty'
>>> inventory.save()
>>> inventory.click('complete_lines')
>>> line, = inventory.lines
>>> line.expected_quantity
1.0
>>> line.quantity
>>> inventory.click('confirm')
>>> line.reload()
>>> line.expected_quantity
1.0
>>> move, = line.moves
>>> move.quantity
1.0
>>> assertEqual(move.from_location, inventory.location)
>>> assertEqual(move.to_location, lost_found_loc)

View File

@@ -0,0 +1,67 @@
=============================
Stock Move In Future Scenario
=============================
Imports::
>>> import datetime as dt
>>> from decimal import Decimal
>>> from proteus import Model
>>> from trytond.modules.company.tests.tools import create_company
>>> from trytond.modules.currency.tests.tools import get_currency
>>> from trytond.tests.tools import activate_modules
>>> today = dt.date.today()
>>> tomorrow = today + dt.timedelta(days=1)
Activate modules::
>>> config = activate_modules('stock', create_company)
Create product::
>>> ProductUom = Model.get('product.uom')
>>> ProductTemplate = Model.get('product.template')
>>> Product = Model.get('product.product')
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
>>> template = ProductTemplate()
>>> template.name = 'Product'
>>> template.default_uom = unit
>>> template.type = 'goods'
>>> template.list_price = Decimal('20')
>>> template.save()
>>> product, = template.products
>>> product.cost_price = Decimal('1')
>>> product.save()
Get stock locations::
>>> Location = Model.get('stock.location')
>>> supplier_loc, = Location.find([('code', '=', 'SUP')])
>>> storage_loc, = Location.find([('code', '=', 'STO')])
A warning is raised when doing a move in the future::
>>> Move = Model.get('stock.move')
>>> move = Move()
>>> move.product = product
>>> move.quantity = 1
>>> move.from_location = supplier_loc
>>> move.to_location = storage_loc
>>> move.currency = get_currency()
>>> move.effective_date = tomorrow
>>> move.quantity = 2
>>> move.unit_price = Decimal('1')
>>> move.save()
>>> move.click('do')
Traceback (most recent call last):
...
MoveFutureWarning: ...
But it can be done for today::
>>> move.effective_date = today
>>> move.click('do')
>>> move.state
'done'

View File

@@ -0,0 +1,110 @@
=====================
Stock Period Scenario
=====================
Imports::
>>> import datetime as dt
>>> from decimal import Decimal
>>> from proteus import Model
>>> from trytond.modules.company.tests.tools import create_company
>>> from trytond.modules.currency.tests.tools import get_currency
>>> from trytond.tests.tools import activate_modules
>>> today = dt.date.today()
>>> yesterday = today - dt.timedelta(days=1)
Activate modules::
>>> config = activate_modules('stock', create_company)
>>> Location = Model.get('stock.location')
>>> Move = Model.get('stock.move')
>>> Period = Model.get('stock.period')
>>> Product = Model.get('product.product')
>>> ProductTemplate = Model.get('product.template')
>>> ProductUom = Model.get('product.uom')
>>> currency = get_currency()
Create product::
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
>>> template = ProductTemplate()
>>> template.name = 'Product'
>>> template.default_uom = unit
>>> template.type = 'goods'
>>> template.save()
>>> product, = template.products
Get stock locations::
>>> storage_loc, = Location.find([('code', '=', 'STO')])
>>> customer_loc, = Location.find([('code', '=', 'CUS')])
Create a period::
>>> period = Period(date=yesterday)
>>> period.save()
Close the period::
>>> period.click('close')
>>> period.state
'closed'
Try to create a move::
>>> move = Move()
>>> move.product = product
>>> move.quantity = 1
>>> move.from_location = storage_loc
>>> move.to_location = customer_loc
>>> move.planned_date = yesterday
>>> move.unit_price = Decimal('42.0000')
>>> move.currency = currency
>>> move.save()
Traceback (most recent call last):
...
AccessError: ...
Reopen the period::
>>> period.click('draft')
>>> period.state
'draft'
Close the period with draft move::
>>> move.save()
>>> period.click('close')
>>> period.state
'closed'
Reopen the period::
>>> period.click('draft')
>>> period.state
'draft'
Create an assigned move::
>>> Move.write([move], {'state': 'assigned'}, config._context)
>>> move.state
'assigned'
Close the period with assigned move::
>>> period.click('close')
Traceback (most recent call last):
...
PeriodCloseError: ...
Try to close a period on today::
>>> period = Period(date=today)
>>> period.click('close')
Traceback (most recent call last):
...
PeriodCloseError: ...

View File

@@ -0,0 +1,126 @@
=====================================
Stock Product Quantities by Warehouse
=====================================
Imports::
>>> import datetime as dt
>>> from decimal import Decimal
>>> from proteus import Model
>>> from trytond.modules.company.tests.tools import create_company
>>> from trytond.modules.currency.tests.tools import get_currency
>>> from trytond.tests.tools import activate_modules, assertEqual
>>> today = dt.date.today()
>>> yesterday = today - dt.timedelta(days=1)
>>> tomorrow = today + dt.timedelta(days=1)
Activate modules::
>>> config = activate_modules('stock', create_company)
Get currency::
>>> currency = get_currency()
Create product::
>>> ProductUom = Model.get('product.uom')
>>> ProductTemplate = Model.get('product.template')
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
>>> template = ProductTemplate()
>>> template.name = 'Product'
>>> template.default_uom = unit
>>> template.type = 'goods'
>>> template.list_price = Decimal('20')
>>> template.save()
>>> product, = template.products
Get stock locations::
>>> Location = Model.get('stock.location')
>>> warehouse_loc, = Location.find([('code', '=', 'WH')])
>>> supplier_loc, = Location.find([('code', '=', 'SUP')])
>>> customer_loc, = Location.find([('code', '=', 'CUS')])
>>> storage_loc, = Location.find([('code', '=', 'STO')])
Fill warehouse::
>>> Move = Model.get('stock.move')
>>> move = Move()
>>> move.product = product
>>> move.from_location = supplier_loc
>>> move.to_location = storage_loc
>>> move.unit = unit
>>> move.quantity = 10
>>> move.effective_date = yesterday
>>> move.unit_price = Decimal('10')
>>> move.currency = currency
>>> move.click('do')
Forecast some moves::
>>> move = Move()
>>> move.product = product
>>> move.from_location = storage_loc
>>> move.to_location = customer_loc
>>> move.unit = unit
>>> move.quantity = 6
>>> move.planned_date = tomorrow
>>> move.unit_price = Decimal('20')
>>> move.currency = currency
>>> move.save()
>>> move = Move()
>>> move.product = product
>>> move.from_location = supplier_loc
>>> move.to_location = storage_loc
>>> move.unit = unit
>>> move.quantity = 5
>>> move.planned_date = tomorrow
>>> move.unit_price = Decimal('10')
>>> move.currency = currency
>>> move.save()
>>> move = Move()
>>> move.product = product
>>> move.from_location = storage_loc
>>> move.to_location = customer_loc
>>> move.unit = unit
>>> move.quantity = 3
>>> move.planned_date = tomorrow
>>> move.unit_price = Decimal('20')
>>> move.currency = currency
>>> move.save()
Check Product Quantities by Warehouse::
>>> ProductQuantitiesByWarehouse = Model.get('stock.product_quantities_warehouse')
>>> with config.set_context(
... product_template=template.id, warehouse=warehouse_loc.id):
... records = ProductQuantitiesByWarehouse.find([])
>>> len(records)
3
>>> assertEqual([(r.date, r.quantity) for r in records],
... [(yesterday, 10), (today, 10), (tomorrow, 6)])
Check Product Quantities by Warehouse Moves::
>>> ProductQuantitiesByWarehouseMove = Model.get(
... 'stock.product_quantities_warehouse.move')
>>> with config.set_context(
... product_template=template.id, warehouse=warehouse_loc.id):
... records = ProductQuantitiesByWarehouseMove.find([])
>>> len(records)
4
>>> assertEqual([
... (r.date, r.cumulative_quantity_start, r.quantity,
... r.cumulative_quantity_end)
... for r in records],
... [
... (yesterday, 0, 10, 10),
... (tomorrow, 10, -6, 4),
... (tomorrow, 4, 5, 9),
... (tomorrow, 9, -3, 6)])

View File

@@ -0,0 +1,115 @@
==============================
Stock Product Replace Scenario
==============================
Imports::
>>> from decimal import Decimal
>>> from proteus import Model, Wizard
>>> from trytond.modules.company.tests.tools import create_company, get_company
>>> from trytond.tests.tools import (
... activate_modules, assertEqual, assertFalse, assertTrue)
Activate modules::
>>> config = activate_modules('stock')
>>> Cron = Model.get('ir.cron')
>>> Location = Model.get('stock.location')
>>> Move = Model.get('stock.move')
>>> ProductTemplate = Model.get('product.template')
>>> UoM = Model.get('product.uom')
Create company::
>>> _ = create_company()
>>> company = get_company()
Get stock locations::
>>> supplier_loc, = Location.find([('code', '=', "SUP")])
>>> storage_loc, = Location.find([('code', '=', "STO")])
>>> customer_loc, = Location.find([('code', '=', "CUS")])
Create a product::
>>> unit, = UoM.find([('name', '=', "Unit")])
>>> template = ProductTemplate()
>>> template.name = "Product"
>>> template.type = 'goods'
>>> template.default_uom = unit
>>> template.save()
>>> product1, = template.products
Create a second product::
>>> template = ProductTemplate()
>>> template.name = "Product"
>>> template.type = 'goods'
>>> template.default_uom = unit
>>> template.save()
>>> product2, = template.products
Fill storage location::
>>> move = Move(product=product1)
>>> move.quantity = 1
>>> move.from_location = supplier_loc
>>> move.to_location = storage_loc
>>> move.unit_price = Decimal('10.0000')
>>> move.currency = company.currency
>>> move.click('do')
>>> move.state
'done'
Replace the product::
>>> replace = Wizard('product.product.replace', models=[product1])
>>> replace.form.destination = product2
>>> replace.execute('replace')
>>> assertEqual(product1.replaced_by, product2)
>>> assertTrue(product1.active)
Create a draft move::
>>> move = Move(product=product1)
>>> move.quantity = 1
>>> move.from_location = storage_loc
>>> move.to_location = customer_loc
>>> move.unit_price = Decimal('20.0000')
>>> move.currency = company.currency
>>> move.save()
>>> move.state
'draft'
Empty storage location::
>>> move = Move(product=product1)
>>> move.quantity = 1
>>> move.from_location = storage_loc
>>> move.to_location = customer_loc
>>> move.unit_price = Decimal('20.0000')
>>> move.currency = company.currency
>>> move.click('do')
>>> move.state
'done'
Check replaced product is deactivated and draft move is deleted::
>>> product1.reload()
>>> assertFalse(product1.active)
>>> Move.find([('state', '=', 'draft')])
[]
Create a move for replaced product change the product::
>>> move = Move(product=product1)
>>> move.quantity = 1
>>> move.from_location = storage_loc
>>> move.to_location = customer_loc
>>> move.unit_price = Decimal('20.0000')
>>> move.currency = company.currency
>>> move.save()
>>> assertEqual(move.product, product2)

View File

@@ -0,0 +1,161 @@
==================================
Stock Recompute Average Cost Price
==================================
Imports::
>>> import datetime as dt
>>> from decimal import Decimal
>>> from proteus import Model, Wizard
>>> from trytond.modules.company.tests.tools import create_company, get_company
>>> from trytond.tests.tools import activate_modules, assertEqual, assertFalse
>>> today = dt.date.today()
Activate modules::
>>> config = activate_modules('stock', create_company)
Get company::
>>> company = get_company()
Create product::
>>> ProductUom = Model.get('product.uom')
>>> ProductTemplate = Model.get('product.template')
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
>>> template = ProductTemplate()
>>> template.name = 'Product'
>>> template.default_uom = unit
>>> template.type = 'goods'
>>> template.list_price = Decimal('300')
>>> template.cost_price_method = 'average'
>>> product, = template.products
>>> product.cost_price = Decimal('80')
>>> template.save()
>>> product, = template.products
Get stock locations::
>>> Location = Model.get('stock.location')
>>> supplier_loc, = Location.find([('code', '=', 'SUP')])
>>> storage_loc, = Location.find([('code', '=', 'STO')])
>>> customer_loc, = Location.find([('code', '=', 'CUS')])
Create some moves::
>>> StockMove = Model.get('stock.move')
>>> StockMove(
... product=product,
... quantity=1,
... from_location=supplier_loc,
... to_location=storage_loc,
... unit_price=Decimal('100'),
... currency=company.currency,
... effective_date=today - dt.timedelta(days=2)).click('do')
>>> StockMove(
... product=product,
... quantity=2,
... from_location=storage_loc,
... to_location=customer_loc,
... unit_price=Decimal('300'),
... currency=company.currency,
... effective_date=today - dt.timedelta(days=1)).click('do')
>>> StockMove(
... product=product,
... quantity=2,
... from_location=supplier_loc,
... to_location=storage_loc,
... unit_price=Decimal('120'),
... currency=company.currency,
... effective_date=today - dt.timedelta(days=1)).click('do')
>>> StockMove(
... product=product,
... quantity=3,
... from_location=supplier_loc,
... to_location=storage_loc,
... unit_price=Decimal('100'),
... currency=company.currency,
... effective_date=today).click('do')
>>> [m.cost_price for m in StockMove.find([])]
[Decimal('105.0000'), Decimal('120.0000'), Decimal('100.0000'), Decimal('100.0000')]
>>> product.reload()
>>> product.cost_price
Decimal('105.0000')
Recompute cost price::
>>> recompute = Wizard('product.recompute_cost_price', [product])
>>> recompute.execute('recompute')
>>> [m.cost_price for m in StockMove.find([])]
[Decimal('105.0000'), Decimal('120.0000'), Decimal('120.0000'), Decimal('100.0000')]
>>> product.reload()
>>> product.cost_price
Decimal('105.0000')
Recompute cost price from a date::
>>> recompute = Wizard('product.recompute_cost_price', [product])
>>> recompute.form.from_ = today - dt.timedelta(days=1)
>>> recompute.execute('recompute')
>>> [m.cost_price for m in StockMove.find([])]
[Decimal('105.0000'), Decimal('120.0000'), Decimal('120.0000'), Decimal('100.0000')]
>>> product.reload()
>>> product.cost_price
Decimal('105.0000')
Update unit price of a move::
>>> move, = StockMove.find([
... ('from_location', '=', supplier_loc.id),
... ('effective_date', '=', today - dt.timedelta(days=1)),
... ])
>>> bool(move.unit_price_updated)
False
>>> move.unit_price = Decimal('130')
>>> move.save()
>>> bool(move.unit_price_updated)
True
>>> recompute = Wizard('product.recompute_cost_price', [product])
>>> recompute.form.from_ = move.effective_date + dt.timedelta(days=1)
>>> recompute.execute('recompute')
>>> move.reload()
>>> bool(move.unit_price_updated)
True
>>> recompute = Wizard('product.recompute_cost_price', [product])
>>> assertEqual(recompute.form.from_, move.effective_date)
>>> recompute.execute('recompute')
>>> move.reload()
>>> assertFalse(move.unit_price_updated)
>>> [m.cost_price for m in StockMove.find([])]
[Decimal('107.5000'), Decimal('130.0000'), Decimal('130.0000'), Decimal('100.0000')]
Launch cron task::
>>> move.unit_price = Decimal('120')
>>> move.save()
>>> Cron = Model.get('ir.cron')
>>> Company = Model.get('company.company')
>>> cron_recompute, = Cron.find([
... ('method', '=', 'product.product|recompute_cost_price_from_moves'),
... ])
>>> cron_recompute.companies.append(Company(company.id))
>>> cron_recompute.click('run_once')
>>> move.reload()
>>> bool(move.unit_price_updated)
False
>>> [m.cost_price for m in StockMove.find([])]
[Decimal('105.0000'), Decimal('120.0000'), Decimal('120.0000'), Decimal('100.0000')]

View File

@@ -0,0 +1,106 @@
=============================================
Stock Recompute Average Cost Price Production
=============================================
Imports::
>>> import datetime as dt
>>> from decimal import Decimal
>>> from proteus import Model, Wizard
>>> from trytond.modules.company.tests.tools import create_company
>>> from trytond.modules.currency.tests.tools import get_currency
>>> from trytond.tests.tools import activate_modules
>>> today = dt.date.today()
>>> yesterday = today - dt.timedelta(days=1)
Activate modules::
>>> config = activate_modules('stock', create_company)
>>> Location = Model.get('stock.location')
>>> ProductTemplate = Model.get('product.template')
>>> ProductUom = Model.get('product.uom')
>>> StockMove = Model.get('stock.move')
Get currency::
>>> currency = get_currency()
Create product::
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
>>> template = ProductTemplate()
>>> template.name = 'Product'
>>> template.default_uom = unit
>>> template.type = 'goods'
>>> template.list_price = Decimal('50.0000')
>>> template.cost_price_method = 'average'
>>> product, = template.products
>>> product.cost_price = Decimal('40.0000')
>>> template.save()
>>> product, = template.products
Get stock locations::
>>> supplier_loc, = Location.find([('code', '=', 'SUP')])
>>> storage_loc, = Location.find([('code', '=', 'STO')])
>>> production_loc = Location(name="Production", type='production')
>>> production_loc.save()
Consume product for production and reverse some::
>>> StockMove(
... product=product,
... quantity=10,
... from_location=storage_loc,
... to_location=production_loc,
... effective_date=today).click('do')
>>> StockMove(
... product=product,
... quantity=2,
... from_location=production_loc,
... to_location=storage_loc,
... unit_price=Decimal('40.0000'),
... currency=currency,
... effective_date=today).click('do')
>>> [m.cost_price for m in StockMove.find([])]
[Decimal('40.0000'), Decimal('40.0000')]
Recompute cost price::
>>> recompute = Wizard('product.recompute_cost_price', [product])
>>> recompute.execute('recompute')
>>> [m.cost_price for m in StockMove.find([])]
[Decimal('0.0000'), Decimal('0.0000')]
>>> product.reload()
>>> product.cost_price
Decimal('0.0000')
Receive product yesterday at new cost::
>>> StockMove(
... product=product,
... quantity=16,
... from_location=supplier_loc,
... to_location=storage_loc,
... unit_price=Decimal('20.0000'),
... currency=currency,
... effective_date=yesterday).click('do')
Recompute cost price::
>>> recompute = Wizard('product.recompute_cost_price', [product])
>>> recompute.execute('recompute')
>>> [m.cost_price for m in StockMove.find([])]
[Decimal('20.0000'), Decimal('20.0000'), Decimal('20.0000')]
>>> product.reload()
>>> product.cost_price
Decimal('20.0000')

View File

@@ -0,0 +1,157 @@
=================================
Stock Shipment Reporting Scenario
=================================
Imports::
>>> import datetime as dt
>>> from decimal import Decimal
>>> from proteus import Model, Report
>>> from trytond.modules.company.tests.tools import create_company
>>> from trytond.modules.currency.tests.tools import get_currency
>>> from trytond.tests.tools import activate_modules
>>> today = dt.date.today()
>>> yesterday = today - dt.timedelta(days=1)
Activate modules::
>>> config = activate_modules('stock', create_company)
Get currency::
>>> currency = get_currency()
Create customer & supplier::
>>> Party = Model.get('party.party')
>>> customer = Party(name='Customer')
>>> customer.save()
>>> supplier = Party(name='Supplier')
>>> supplier.save()
Create product::
>>> ProductUom = Model.get('product.uom')
>>> ProductTemplate = Model.get('product.template')
>>> Product = Model.get('product.product')
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
>>> template = ProductTemplate()
>>> template.name = 'Product'
>>> template.default_uom = unit
>>> template.type = 'goods'
>>> template.list_price = Decimal('20')
>>> template.save()
>>> product, = template.products
Get stock locations::
>>> Location = Model.get('stock.location')
>>> warehouse_loc, = Location.find([('code', '=', 'WH')])
>>> supplier_loc, = Location.find([('code', '=', 'SUP')])
>>> customer_loc, = Location.find([('code', '=', 'CUS')])
>>> input_loc, = Location.find([('code', '=', 'IN')])
>>> output_loc, = Location.find([('code', '=', 'OUT')])
>>> storage_loc, = Location.find([('code', '=', 'STO')])
>>> lost_loc, = Location.find([('type', '=', 'lost_found')])
Create Shipment In::
>>> ShipmentIn = Model.get('stock.shipment.in')
>>> shipment_in = ShipmentIn()
>>> shipment_in.planned_date = today
>>> shipment_in.supplier = supplier
>>> shipment_in.warehouse = warehouse_loc
Receive a bunch of products::
>>> move = shipment_in.incoming_moves.new()
>>> move.product = product
>>> move.unit = unit
>>> move.quantity = 100
>>> move.from_location = supplier_loc
>>> move.to_location = input_loc
>>> move.unit_price = Decimal('1')
>>> move.currency = currency
>>> shipment_in.save()
>>> shipment_in.click('receive')
>>> shipment_in.click('do')
Testing the report::
>>> supplier_restocking_list = Report('stock.shipment.in.restocking_list')
>>> ext, _, _, name = supplier_restocking_list.execute([shipment_in], {})
>>> ext
'odt'
>>> name
'Restocking List-1'
Create Shipment Out::
>>> ShipmentOut = Model.get('stock.shipment.out')
>>> shipment_out = ShipmentOut()
>>> shipment_out.planned_date = today
>>> shipment_out.customer = customer
>>> shipment_out.warehouse = warehouse_loc
Add two shipment lines of same product and go through the workflow::
>>> move = shipment_out.outgoing_moves.new()
>>> move.product = product
>>> move.unit = unit
>>> move.quantity = 1
>>> move.from_location = output_loc
>>> move.to_location = customer_loc
>>> move.unit_price = Decimal('1')
>>> move.currency = currency
>>> shipment_out.save()
>>> shipment_out.click('wait')
>>> shipment_out.click('assign_try')
>>> shipment_out.click('pick')
>>> shipment_out.click('pack')
>>> shipment_out.click('do')
Testing the reports::
>>> delivery_note = Report('stock.shipment.out.delivery_note')
>>> ext, _, _, name = delivery_note.execute([shipment_out], {})
>>> ext
'odt'
>>> name
'Delivery Note-1'
>>> picking_list = Report('stock.shipment.out.picking_list')
>>> ext, _, _, name = picking_list.execute([shipment_out], {})
>>> ext
'odt'
>>> name
'Picking List-1'
Create an internal shipment::
>>> ShipmentInternal = Model.get('stock.shipment.internal')
>>> shipment_internal = ShipmentInternal()
>>> shipment_internal.planned_date = today
>>> shipment_internal.from_location = storage_loc
>>> shipment_internal.to_location = lost_loc
>>> move = shipment_internal.moves.new()
>>> move.product = product
>>> move.unit = unit
>>> move.quantity = 1
>>> move.from_location = storage_loc
>>> move.to_location = lost_loc
>>> shipment_internal.save()
>>> shipment_internal.click('wait')
>>> shipment_internal.click('assign_try')
>>> shipment_internal.click('do')
Testing the report::
>>> internal_report = Report('stock.shipment.internal.report')
>>> ext, _, _, name = internal_report.execute([shipment_internal], {})
>>> ext
'odt'
>>> name
'Internal Shipment-1'

View File

@@ -0,0 +1,239 @@
==================================
Stock Reporting Inventory Scenario
==================================
Imports::
>>> import datetime as dt
>>> from decimal import Decimal
>>> from unittest.mock import patch
>>> from proteus import Model
>>> from trytond.ir.date import Date
>>> from trytond.modules.company.tests.tools import create_company
>>> from trytond.modules.currency.tests.tools import get_currency
>>> from trytond.tests.tools import activate_modules, assertEqual
>>> period_close = globals().get('period_close', False)
>>> product_type = globals().get('product_type', 'product.template')
Patch today::
>>> mock = patch.object(Date, 'today', return_value=dt.date(2025, 1, 15))
>>> _ = mock.start()
Activate modules::
>>> config = activate_modules('stock', create_company)
>>> Inventory = Model.get('stock.reporting.inventory')
>>> InventoryMove = Model.get('stock.reporting.inventory.move')
>>> InventoryDaily = Model.get('stock.reporting.inventory.daily')
>>> InventoryTurnover = Model.get('stock.reporting.inventory.turnover')
>>> Location = Model.get('stock.location')
>>> Move = Model.get('stock.move')
>>> Period = Model.get('stock.period')
>>> ProductTemplate = Model.get('product.template')
>>> ProductUom = Model.get('product.uom')
Get currency::
>>> currency = get_currency()
Create product::
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
>>> template = ProductTemplate()
>>> template.name = 'Product'
>>> template.default_uom = unit
>>> template.type = 'goods'
>>> template.save()
>>> product, = template.products
Get stock locations::
>>> warehouse_loc, = Location.find([('code', '=', 'WH')])
>>> supplier_loc, = Location.find([('code', '=', 'SUP')])
>>> customer_loc, = Location.find([('code', '=', 'CUS')])
>>> storage_loc, = Location.find([('code', '=', 'STO')])
Fill warehouse::
>>> move = Move()
>>> move.product = product
>>> move.from_location = supplier_loc
>>> move.to_location = storage_loc
>>> move.quantity = 10
>>> move.effective_date = dt.date(2025, 1, 1)
>>> move.unit_price = Decimal('10.0000')
>>> move.currency = currency
>>> move.click('do')
>>> move.state
'done'
>>> move = Move()
>>> move.product = product
>>> move.from_location = storage_loc
>>> move.to_location = customer_loc
>>> move.unit = unit
>>> move.quantity = 1
>>> move.effective_date = dt.date(2025, 1, 1)
>>> move.unit_price = Decimal('20.0000')
>>> move.currency = currency
>>> move.click('do')
>>> move.state
'done'
>>> move = Move()
>>> move.product = product
>>> move.from_location = storage_loc
>>> move.to_location = customer_loc
>>> move.unit = unit
>>> move.quantity = 2
>>> move.effective_date = dt.date(2025, 1, 10)
>>> move.unit_price = Decimal('20.0000')
>>> move.currency = currency
>>> move.click('do')
>>> move.state
'done'
Forecast some moves::
>>> move = Move()
>>> move.product = product
>>> move.from_location = supplier_loc
>>> move.to_location = storage_loc
>>> move.quantity = 10
>>> move.effective_date = dt.date(2025, 1, 5)
>>> move.unit_price = Decimal('10.0000')
>>> move.currency = currency
>>> move.save()
>>> move.state
'draft'
>>> move = Move()
>>> move.product = product
>>> move.from_location = storage_loc
>>> move.to_location = customer_loc
>>> move.unit = unit
>>> move.quantity = 3
>>> move.planned_date = dt.date(2025, 1, 20)
>>> move.unit_price = Decimal('20.0000')
>>> move.currency = currency
>>> move.save()
>>> move.state
'draft'
Close period::
>>> period = Period(date=dt.date(2025, 1, 5))
>>> if period_close:
... period.click('close')
Check inventory::
>>> with config.set_context(
... location=warehouse_loc.id,
... product_type=product_type,
... date=dt.date(2024, 1, 1)):
... Inventory.find([])
[]
>>> with config.set_context(
... location=warehouse_loc.id,
... product_type=product_type,
... date=dt.date(2025, 1, 1)):
... inventory, = Inventory.find([])
>>> inventory.quantity
9.0
>>> assertEqual(inventory.product.__class__.__name__, product_type)
>>> with config.set_context(
... location=warehouse_loc.id,
... product_type=product_type,
... date=dt.date(2025, 1, 15)):
... inventory, = Inventory.find([])
>>> inventory.quantity
7.0
>>> with config.set_context(
... location=warehouse_loc.id,
... product_type=product_type,
... date=dt.date(2025, 1, 20)):
... inventory, = Inventory.find([])
>>> inventory.quantity
4.0
>>> with config.set_context(
... location=warehouse_loc.id,
... product_type=product_type):
... inventory_moves = InventoryMove.find([])
... inventories_daily = InventoryDaily.find([])
>>> [i.quantity for i in inventory_moves]
[4.0, 7.0, 9.0, 10.0]
>>> [(i.input_quantity, i.output_quantity) for i in inventory_moves]
[(None, 3.0), (None, 2.0), (None, 1.0), (10.0, None)]
>>> [i.quantity for i in inventories_daily]
[4.0, 7.0, 9.0]
>>> [(i.input_quantity, i.output_quantity) for i in inventories_daily]
[(None, 3.0), (None, 2.0), (10.0, 1.0)]
>>> with config.set_context(
... location=warehouse_loc.id,
... from_date=dt.date(2025, 1, 15),
... to_date=dt.date(2025, 1, 29),
... product_type=product_type):
... inventory_moves = InventoryMove.find([])
... inventories_daily = InventoryDaily.find([])
>>> [i.quantity for i in inventory_moves]
[4.0, 7.0]
>>> [(i.input_quantity, i.output_quantity) for i in inventory_moves]
[(None, 3.0), (None, None)]
>>> [i.quantity for i in inventories_daily]
[4.0, 7.0]
>>> [(i.input_quantity, i.output_quantity) for i in inventories_daily]
[(None, 3.0), (None, None)]
>>> with config.set_context(
... location=warehouse_loc.id,
... from_date=dt.date(2025, 1, 10),
... to_date=dt.date(2025, 1, 29),
... product_type=product_type):
... inventory_moves = InventoryMove.find([])
... inventories_daily = InventoryDaily.find([])
>>> assertEqual(
... [i.quantity for i in inventory_moves],
... [4.0, 7.0] if period_close else [4.0, 7.0, 9.0])
>>> assertEqual(
... [(i.input_quantity, i.output_quantity) for i in inventory_moves],
... [(None, 3.0), (None, 2.0)] if period_close
... else [(None, 3.0), (None, 2.0), (None, None)])
>>> [i.quantity for i in inventories_daily]
[4.0, 7.0]
>>> [(i.input_quantity, i.output_quantity) for i in inventories_daily]
[(None, 3.0), (None, 2.0)]
Check Inventory turnover::
>>> with config.set_context(
... location=warehouse_loc.id,
... from_date=dt.date(2025, 1, 10),
... to_date=dt.date(2025, 1, 29),
... product_type=product_type):
... turnover, = InventoryTurnover.find([])
>>> turnover.output_quantity
0.25
>>> turnover.average_quantity
5.5
>>> turnover.turnover
0.045
>>> assertEqual(turnover.product.__class__.__name__, product_type)

View File

@@ -0,0 +1,217 @@
===============================
Stock Reporting Margin Scenario
===============================
Imports::
>>> import datetime as dt
>>> from decimal import Decimal
>>> from proteus import Model
>>> from trytond.modules.company.tests.tools import create_company
>>> from trytond.modules.currency.tests.tools import get_currency
>>> from trytond.tests.tools import activate_modules, assertEqual
>>> today = dt.date.today()
>>> yesterday = today - dt.timedelta(days=1)
Activate modules::
>>> config = activate_modules('stock', create_company)
Get currency::
>>> currency = get_currency()
Create product::
>>> ProductUom = Model.get('product.uom')
>>> ProductTemplate = Model.get('product.template')
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
>>> template = ProductTemplate()
>>> template.name = 'Product'
>>> template.default_uom = unit
>>> template.type = 'goods'
>>> template.list_price = Decimal('40')
>>> template.save()
>>> product, = template.products
>>> product.cost_price = Decimal('20')
>>> product.save()
>>> template2, = template.duplicate()
>>> product2, = template2.products
>>> Category = Model.get('product.category')
>>> category_root = Category(name="Root")
>>> category_root.save()
>>> category1 = Category(name="Child1", parent=category_root)
>>> category1.save()
>>> category2 = Category(name="Child2", parent=category_root)
>>> category2.save()
>>> template.categories.append(Category(category1.id))
>>> template.save()
>>> template2.categories.append(Category(category2.id))
>>> template2.save()
Get stock locations::
>>> Location = Model.get('stock.location')
>>> supplier_loc, = Location.find([('code', '=', 'SUP')])
>>> customer_loc, = Location.find([('code', '=', 'CUS')])
>>> storage_loc, = Location.find([('code', '=', 'STO')])
>>> lost_loc, = Location.find([('type', '=', 'lost_found')])
Create some moves::
>>> Move = Model.get('stock.move')
>>> move = Move()
>>> move.product = product
>>> move.from_location = supplier_loc
>>> move.to_location = storage_loc
>>> move.quantity = 8
>>> move.unit_price = Decimal('20')
>>> move.currency = currency
>>> move.effective_date = yesterday
>>> move.click('do')
>>> move = Move()
>>> move.product = product
>>> move.from_location = storage_loc
>>> move.to_location = customer_loc
>>> move.quantity = 2
>>> move.unit_price = Decimal('40')
>>> move.currency = currency
>>> move.effective_date = yesterday
>>> move.click('do')
>>> move = Move()
>>> move.product = product
>>> move.from_location = storage_loc
>>> move.to_location = customer_loc
>>> move.quantity = 4
>>> move.unit_price = Decimal('30')
>>> move.currency = currency
>>> move.effective_date = today
>>> move.click('do')
>>> move = Move()
>>> move.product = product
>>> move.from_location = customer_loc
>>> move.to_location = storage_loc
>>> move.quantity = 1
>>> move.unit_price = Decimal('30')
>>> move.currency = currency
>>> move.effective_date = today
>>> move.click('do')
>>> move = Move()
>>> move.product = product2
>>> move.from_location = storage_loc
>>> move.to_location = customer_loc
>>> move.quantity = 2
>>> move.unit_price = Decimal('50')
>>> move.currency = currency
>>> move.effective_date = today
>>> move.click('do')
>>> move = Move()
>>> move.product = product
>>> move.from_location = storage_loc
>>> move.to_location = lost_loc
>>> move.quantity = 1
>>> move.effective_date = today
>>> move.click('do')
Check reporting margin per product::
>>> MarginProduct = Model.get('stock.reporting.margin.product')
>>> MarginProductTimeseries = Model.get(
... 'stock.reporting.margin.product.time_series')
>>> context = {
... 'from_date': yesterday,
... 'to_date': today,
... 'period': 'day',
... }
>>> with config.set_context(context=context):
... reports = MarginProduct.find([])
... time_series = MarginProductTimeseries.find([])
>>> len(reports)
2
>>> report, = [r for r in reports if r.product == product]
>>> (report.quantity, report.cost, report.revenue, report.profit, report.margin)
(5.0, Decimal('100.00'), Decimal('170.00'), Decimal('70.00'), Decimal('0.4118'))
>>> len(time_series)
3
>>> with config.set_context(context=context):
... assertEqual({(
... r.product.id, str(r.date), r.quantity, r.cost, r.revenue,
... r.profit, r.margin)
... for r in time_series},
... {
... (product.id, str(yesterday), 2, Decimal('40.00'),
... Decimal('80.00'), Decimal('40.00'), Decimal('0.5000')),
... (product.id, str(today), 3, Decimal('60.00'),
... Decimal('90.00'), Decimal('30.00'), Decimal('0.3333')),
... (product2.id, str(today), 2, Decimal('40.00'),
... Decimal('100.00'), Decimal('60.00'), Decimal('0.6000'))})
Check reporting margin per categories::
>>> MarginCategory = Model.get('stock.reporting.margin.category')
>>> MarginCategoryTimeseries = Model.get(
... 'stock.reporting.margin.category.time_series')
>>> MarginCategoryTree = Model.get(
... 'stock.reporting.margin.category.tree')
>>> with config.set_context(context=context):
... reports = MarginCategory.find([])
... time_series = MarginCategoryTimeseries.find([])
... tree = MarginCategoryTree.find([])
>>> len(reports)
2
>>> with config.set_context(context=context):
... assertEqual({(r.category.id, r.cost, r.revenue, r.profit, r.margin)
... for r in reports},
... {(category1.id, Decimal('100.00'), Decimal('170.00'),
... Decimal('70.00'), Decimal('0.4118')),
... (category2.id, Decimal('40.00'), Decimal('100.00'),
... Decimal('60.00'), Decimal('0.6000'))})
>>> len(time_series)
3
>>> with config.set_context(context=context):
... assertEqual({
... (r.category.id, str(r.date), r.cost, r.revenue, r.profit, r.margin)
... for r in time_series},
... {
... (category1.id, str(yesterday), Decimal('40.00'), Decimal('80.00'),
... Decimal('40.00'), Decimal('0.5000')),
... (category1.id, str(today), Decimal('60.00'), Decimal('90.00'),
... Decimal('30.00'), Decimal('0.3333')),
... (category2.id, str(today), Decimal('40.00'), Decimal('100.00'),
... Decimal('60.00'), Decimal('0.6000'))})
>>> len(tree)
3
>>> with config.set_context(context=context):
... assertEqual({(r.name, r.cost, r.revenue, r.profit, r.margin)
... for r in tree},
... {("Root", Decimal('140.00'), Decimal('270.00'),
... Decimal('130.00'), Decimal('0.4815')),
... ("Child1", Decimal('100.00'), Decimal('170.00'),
... Decimal('70.00'), Decimal('0.4118')),
... ('Child2', Decimal('40.00'), Decimal('100.00'),
... Decimal('60.00'), Decimal('0.6000'))})
>>> child1, = MarginCategoryTree.find([('rec_name', '=', 'Child1')])
>>> child1.rec_name
'Child1'
Check reporting margin including lost::
>>> context['include_lost'] = True
>>> with config.set_context(context=context):
... reports = MarginProduct.find([])
>>> len(reports)
2
>>> report, = [r for r in reports if r.product == product]
>>> (report.quantity, report.cost, report.revenue, report.profit, report.margin)
(6.0, Decimal('120.00'), Decimal('170.00'), Decimal('50.00'), Decimal('0.2941'))

View File

@@ -0,0 +1,137 @@
==========================
Stock Shipment Cron Assign
==========================
Imports::
>>> import datetime as dt
>>> from decimal import Decimal
>>> from proteus import Model
>>> from trytond.modules.company.tests.tools import create_company
>>> from trytond.modules.currency.tests.tools import get_currency
>>> from trytond.tests.tools import activate_modules
>>> today = dt.date.today()
>>> yesterday = today - dt.timedelta(days=1)
Activate modules::
>>> config = activate_modules('stock', create_company)
>>> Cron = Model.get('ir.cron')
>>> Location = Model.get('stock.location')
>>> Move = Model.get('stock.move')
>>> Party = Model.get('party.party')
>>> ProductTemplate = Model.get('product.template')
>>> ShipmentInternal = Model.get('stock.shipment.internal')
>>> ShipmentOut = Model.get('stock.shipment.out')
>>> UoM = Model.get('product.uom')
Get currency::
>>> currency = get_currency()
Create a product::
>>> unit, = UoM.find([('name', '=', "Unit")])
>>> template = ProductTemplate()
>>> template.name = "Product"
>>> template.type = 'goods'
>>> template.default_uom = unit
>>> template.save()
>>> product, = template.products
Create customer::
>>> customer = Party(name="Customer")
>>> customer.save()
Get locations::
>>> storage_loc, = Location.find([('code', '=', "STO")])
>>> supplier_loc, = Location.find([('code', '=', "SUP")])
>>> lost_loc, = Location.find([('type', '=', 'lost_found')], limit=1)
Fill storage::
>>> move = Move()
>>> move.product = product
>>> move.quantity = 5
>>> move.from_location = supplier_loc
>>> move.to_location = storage_loc
>>> move.unit_price = Decimal('10.0000')
>>> move.currency = currency
>>> move.effective_date = yesterday
>>> move.click('do')
>>> move.state
'done'
Create different shipments with different planned dates::
>>> shipment_int_1 = ShipmentInternal()
>>> shipment_int_1.planned_date = today
>>> shipment_int_1.from_location = storage_loc
>>> shipment_int_1.to_location = lost_loc
>>> move = shipment_int_1.moves.new()
>>> move.from_location = shipment_int_1.from_location
>>> move.to_location = shipment_int_1.to_location
>>> move.product = product
>>> move.quantity = 2
>>> shipment_int_1.click('wait')
>>> shipment_int_1.state
'waiting'
>>> shipment_out_1 = ShipmentOut()
>>> shipment_out_1.planned_date = today
>>> shipment_out_1.customer = customer
>>> move = shipment_out_1.outgoing_moves.new()
>>> move.from_location = shipment_out_1.warehouse_output
>>> move.to_location = shipment_out_1.customer_location
>>> move.product = product
>>> move.quantity = 2
>>> move.unit_price = Decimal('10.0000')
>>> move.currency = currency
>>> shipment_out_1.click('wait')
>>> shipment_out_1.state
'waiting'
>>> shipment_int_2, = shipment_int_1.duplicate()
>>> shipment_int_2.click('wait')
>>> shipment_int_2.state
'waiting'
>>> shipment_out_2, = shipment_out_1.duplicate()
>>> shipment_out_2.click('wait')
>>> shipment_out_2.state
'waiting'
Run assignation cron::
>>> cron = Cron(method='ir.cron|stock_shipment_assign_try')
>>> cron.interval_number = 1
>>> cron.interval_type = 'days'
>>> cron.click('run_once')
Check assignations::
>>> shipment_int_1.reload()
>>> shipment_int_1.state
'assigned'
>>> shipment_out_1.reload()
>>> shipment_out_1.state
'assigned'
>>> shipment_int_2.reload()
>>> shipment_int_2.state
'waiting'
>>> shipment_int_2.partially_assigned
True
>>> shipment_out_2.reload()
>>> shipment_out_2.state
'waiting'
>>> shipment_out_2.partially_assigned
False

View File

@@ -0,0 +1,115 @@
==========================
Stock Shipment In Scenario
==========================
Imports::
>>> from decimal import Decimal
>>> from proteus import Model, Report
>>> from trytond.modules.company.tests.tools import create_company
>>> from trytond.modules.currency.tests.tools import get_currency
>>> from trytond.tests.tools import activate_modules, assertNotEqual
Activate modules::
>>> config = activate_modules('stock', create_company)
>>> Location = Model.get('stock.location')
>>> Party = Model.get('party.party')
>>> ProductTemplate = Model.get('product.template')
>>> ProductUom = Model.get('product.uom')
>>> ShipmentIn = Model.get('stock.shipment.in')
Create supplier::
>>> supplier = Party(name='Supplier')
>>> supplier.save()
Create product::
>>> unit, = ProductUom.find([('name', '=', "Unit")])
>>> template = ProductTemplate()
>>> template.name = "Product"
>>> template.default_uom = unit
>>> template.type = 'goods'
>>> template.list_price = Decimal('20')
>>> template.save()
>>> product, = template.products
Get stock locations::
>>> supplier_loc, = Location.find([('code', '=', 'SUP')])
>>> input_loc, = Location.find([('code', '=', 'IN')])
>>> storage_loc, = Location.find([('code', '=', 'STO')])
Create a shipment::
>>> shipment = ShipmentIn()
>>> shipment.supplier = supplier
>>> move = shipment.incoming_moves.new()
>>> move.product = product
>>> move.quantity = 10
>>> move.from_location = supplier_loc
>>> move.to_location = input_loc
>>> move.unit_price = Decimal('5')
>>> move.currency = get_currency()
>>> shipment.save()
>>> assertNotEqual(shipment.number, None)
>>> len(shipment.incoming_moves)
1
>>> len(shipment.inventory_moves)
0
Receive the shipment::
>>> shipment.click('receive')
>>> shipment.state
'received'
>>> [m.state for m in shipment.incoming_moves]
['done']
>>> [m.state for m in shipment.inventory_moves]
['draft']
>>> restocking_list = Report('stock.shipment.in.restocking_list')
>>> _ = restocking_list.execute([shipment])
Change inventory quantity and try to finish::
>>> inventory_move, = shipment.inventory_moves
>>> inventory_move.quantity = 11
>>> shipment.click('do')
Traceback (most recent call last):
...
ShipmentCheckQuantityWarning: ...
>>> inventory_move.reload()
>>> inventory_move.quantity = 10
>>> inventory_move.save()
Add extra product to inventory and try to finish::
>>> product2, = product.duplicate()
>>> move = shipment.inventory_moves.new()
>>> move.product = product2
>>> move.quantity = 1
>>> move.from_location = input_loc
>>> move.to_location = storage_loc
>>> shipment.click('do')
Traceback (most recent call last):
...
ShipmentCheckQuantityWarning: ...
>>> move = shipment.inventory_moves[-1]
>>> shipment.inventory_moves.remove(move)
>>> shipment.save()
Finish the shipment::
>>> shipment.click('do')
>>> shipment.state
'done'
>>> len(shipment.incoming_moves)
1
>>> [m.state for m in shipment.inventory_moves]
['done']

View File

@@ -0,0 +1,78 @@
=================================
Stock Shipment In Return Scenario
=================================
Imports::
>>> import datetime as dt
>>> from decimal import Decimal
>>> from proteus import Model
>>> from trytond.modules.company.tests.tools import create_company
>>> from trytond.modules.currency.tests.tools import get_currency
>>> from trytond.tests.tools import activate_modules, assertEqual, assertNotEqual
>>> today = dt.date.today()
>>> yesterday = today - dt.timedelta(days=1)
Activate modules::
>>> config = activate_modules('stock', create_company)
Create supplier::
>>> Party = Model.get('party.party')
>>> supplier = Party(name='Supplier')
>>> supplier.save()
Create product::
>>> ProductUom = Model.get('product.uom')
>>> ProductTemplate = Model.get('product.template')
>>> Product = Model.get('product.product')
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
>>> template = ProductTemplate()
>>> template.name = 'Product'
>>> template.default_uom = unit
>>> template.type = 'goods'
>>> template.list_price = Decimal('20')
>>> template.save()
>>> product, = template.products
Get stock locations::
>>> Location = Model.get('stock.location')
>>> storage_loc, = Location.find([('code', '=', 'STO')])
>>> supplier_loc, = Location.find([('code', '=', 'SUP')])
Create Shipment In::
>>> ShipmentInReturn = Model.get('stock.shipment.in.return')
>>> shipment_return = ShipmentInReturn()
>>> shipment_return.planned_date = yesterday
>>> shipment_return.supplier = supplier
>>> shipment_return.from_location = storage_loc
>>> move = shipment_return.moves.new()
>>> move.product = product
>>> move.unit = unit
>>> move.quantity = 1
>>> move.from_location = storage_loc
>>> move.to_location = supplier_loc
>>> move.unit_price = Decimal('1')
>>> move.currency = get_currency()
>>> shipment_return.save()
>>> shipment_return.number
>>> shipment_return.click('wait')
>>> shipment_return.state
'waiting'
>>> assertNotEqual(shipment_return.number, None)
Reschedule shipment::
>>> Cron = Model.get('ir.cron')
>>> cron = Cron(method='stock.shipment.in.return|reschedule')
>>> cron.interval_number = 1
>>> cron.interval_type = 'months'
>>> cron.click('run_once')
>>> shipment_return.reload()
>>> assertEqual(shipment_return.planned_date, today)

View File

@@ -0,0 +1,81 @@
=================================================
Stock Shipment In Same Storage and Input Scenario
=================================================
Imports::
>>> from decimal import Decimal
>>> from proteus import Model
>>> from trytond.modules.company.tests.tools import create_company
>>> from trytond.modules.currency.tests.tools import get_currency
>>> from trytond.tests.tools import activate_modules
Activate modules::
>>> config = activate_modules('stock', create_company)
Create supplier::
>>> Party = Model.get('party.party')
>>> supplier = Party(name='Supplier')
>>> supplier.save()
Create product::
>>> ProductUom = Model.get('product.uom')
>>> ProductTemplate = Model.get('product.template')
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
>>> template = ProductTemplate()
>>> template.name = 'Product'
>>> template.default_uom = unit
>>> template.type = 'goods'
>>> template.list_price = Decimal('20')
>>> template.save()
>>> product, = template.products
Get stock locations::
>>> Location = Model.get('stock.location')
>>> warehouse_loc, = Location.find([('code', '=', 'WH')])
>>> supplier_loc, = Location.find([('code', '=', 'SUP')])
>>> storage_loc, = Location.find([('code', '=', 'STO')])
>>> storage1 = Location(name="Storage 1", parent=storage_loc)
>>> storage1.save()
Use storage location as input location::
>>> warehouse_loc.input_location = storage_loc
>>> warehouse_loc.save()
Create Shipment In::
>>> ShipmentIn = Model.get('stock.shipment.in')
>>> shipment_in = ShipmentIn()
>>> shipment_in.supplier = supplier
>>> shipment_in.warehouse = warehouse_loc
>>> move = shipment_in.incoming_moves.new()
>>> move.product = product
>>> move.unit = unit
>>> move.quantity = 1
>>> move.unit_price = Decimal('5')
>>> move.currency = get_currency()
>>> move.from_location = supplier_loc
>>> move.to_location = storage1
>>> shipment_in.save()
>>> len(shipment_in.incoming_moves)
1
>>> len(shipment_in.inventory_moves)
0
Incoming moves are done when receiving the shipment::
>>> shipment_in.click('receive')
>>> shipment_in.state
'done'
>>> move, = shipment_in.incoming_moves
>>> move.state
'done'
>>> len(shipment_in.inventory_moves)
0

View File

@@ -0,0 +1,141 @@
================================
Stock Shipment Internal Scenario
================================
Imports::
>>> import datetime as dt
>>> from decimal import Decimal
>>> from proteus import Model, Report
>>> from trytond.modules.company.tests.tools import create_company
>>> from trytond.tests.tools import (
... activate_modules, assertEqual, assertNotEqual, set_user)
>>> today = dt.date.today()
>>> yesterday = today - dt.timedelta(days=1)
>>> tomorrow = today + dt.timedelta(days=1)
Activate modules::
>>> config = activate_modules('stock', create_company)
>>> Employee = Model.get('company.employee')
>>> Party = Model.get('party.party')
>>> User = Model.get('res.user')
Set employee::
>>> employee_party = Party(name="Employee")
>>> employee_party.save()
>>> employee = Employee(party=employee_party)
>>> employee.save()
>>> user = User(config.user)
>>> user.employees.append(employee)
>>> user.employee = employee
>>> user.save()
>>> set_user(user.id)
Create product::
>>> ProductUom = Model.get('product.uom')
>>> ProductTemplate = Model.get('product.template')
>>> Product = Model.get('product.product')
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
>>> template = ProductTemplate()
>>> template.name = 'Product'
>>> template.default_uom = unit
>>> template.type = 'goods'
>>> template.list_price = Decimal('20')
>>> template.save()
>>> product, = template.products
Get stock locations::
>>> Location = Model.get('stock.location')
>>> lost_found_loc, = Location.find([('type', '=', 'lost_found')])
>>> storage_loc, = Location.find([('code', '=', 'STO')])
>>> internal_loc = Location(
... name="Internal", type='storage', parent=storage_loc.parent)
>>> internal_loc.save()
Create Internal Shipment::
>>> Shipment = Model.get('stock.shipment.internal')
>>> StockMove = Model.get('stock.move')
>>> shipment = Shipment()
>>> shipment.planned_date = today
>>> shipment.from_location = internal_loc
>>> shipment.to_location = storage_loc
>>> move = shipment.moves.new()
>>> move.product = product
>>> move.quantity = 1
>>> move.from_location = internal_loc
>>> move.to_location = storage_loc
>>> shipment.save()
>>> shipment.number
>>> shipment.assigned_by
>>> shipment.done_by
>>> shipment.click('wait')
>>> shipment.state
'waiting'
>>> assertNotEqual(shipment.number, None)
>>> shipment.click('assign_try')
>>> shipment.state
'waiting'
>>> shipment.assigned_by
>>> shipment.done_by
>>> report = Report('stock.shipment.internal.report')
>>> _ = report.execute([shipment])
Create Internal Shipment from lost_found location::
>>> lost_found_shipment = Shipment()
>>> lost_found_shipment.planned_date = today
>>> lost_found_shipment.from_location = lost_found_loc
>>> lost_found_shipment.to_location = internal_loc
>>> move = StockMove()
>>> move = lost_found_shipment.moves.new()
>>> move.product = product
>>> move.unit = unit
>>> move.quantity = 2
>>> move.from_location = lost_found_loc
>>> move.to_location = internal_loc
>>> lost_found_shipment.click('wait')
>>> lost_found_shipment.click('assign_try')
>>> lost_found_shipment.state
'assigned'
>>> lost_found_shipment.click('do')
>>> lost_found_shipment.state
'done'
Check that now we can finish the older shipment::
>>> shipment.click('assign_try')
>>> assertEqual(shipment.assigned_by, employee)
>>> shipment.done_by
>>> shipment.click('do')
>>> shipment.state
'done'
>>> assertEqual(shipment.done_by, employee)
Duplicate Internal Shipment::
>>> shipment_copy, = shipment.duplicate()
>>> len(shipment_copy.moves)
1
Reschedule shipment::
>>> shipment_copy.planned_date = yesterday
>>> shipment_copy.click('wait')
>>> Cron = Model.get('ir.cron')
>>> cron = Cron(method='stock.shipment.internal|reschedule')
>>> cron.interval_number = 1
>>> cron.interval_type = 'months'
>>> cron.click('run_once')
>>> shipment_copy.reload()
>>> assertEqual(shipment_copy.planned_date, today)

View File

@@ -0,0 +1,98 @@
=============================================
Stock Shipment Internal with Transit Scenario
=============================================
Imports::
>>> import datetime as dt
>>> from decimal import Decimal
>>> from proteus import Model
>>> from trytond.modules.company.tests.tools import create_company
>>> from trytond.tests.tools import activate_modules, assertEqual
>>> today = dt.date.today()
>>> yesterday = today - dt.timedelta(days=1)
>>> tomorrow = today + dt.timedelta(days=1)
Activate modules::
>>> config = activate_modules('stock', create_company)
>>> ProductTemplate = Model.get('product.template')
>>> ProductUom = Model.get('product.uom')
>>> Shipment = Model.get('stock.shipment.internal')
Create product::
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
>>> template = ProductTemplate()
>>> template.name = "Product"
>>> template.default_uom = unit
>>> template.type = 'goods'
>>> template.list_price = Decimal('20')
>>> template.save()
>>> product, = template.products
Get stock locations::
>>> Location = Model.get('stock.location')
>>> warehouse1, = Location.find([('type', '=', 'warehouse')])
>>> warehouse2, = warehouse1.duplicate()
Add lead time between warehouses::
>>> LeadTime = Model.get('stock.location.lead_time')
>>> lead_time = LeadTime()
>>> lead_time.warehouse_from = warehouse1
>>> lead_time.warehouse_to = warehouse2
>>> lead_time.lead_time = dt.timedelta(1)
>>> lead_time.save()
Create Internal Shipment with lead time::
>>> shipment = Shipment()
>>> shipment.planned_date = tomorrow
>>> shipment.from_location = warehouse1.storage_location
>>> shipment.to_location = warehouse2.storage_location
>>> bool(shipment.transit_location)
True
>>> assertEqual(shipment.planned_start_date, today)
>>> move = shipment.moves.new()
>>> move.product = product
>>> move.quantity = 2
>>> move.from_location = shipment.from_location
>>> move.to_location = shipment.to_location
>>> shipment.click('wait')
>>> len(shipment.moves)
2
>>> outgoing_move, = shipment.outgoing_moves
>>> outgoing_move.quantity
2.0
>>> assertEqual(outgoing_move.from_location, shipment.from_location)
>>> assertEqual(outgoing_move.to_location, shipment.transit_location)
>>> assertEqual(outgoing_move.planned_date, today)
>>> incoming_move, = shipment.incoming_moves
>>> incoming_move.quantity
2.0
>>> assertEqual(incoming_move.from_location, shipment.transit_location)
>>> assertEqual(incoming_move.to_location, shipment.to_location)
>>> assertEqual(incoming_move.planned_date, tomorrow)
>>> outgoing_move.quantity = 1
>>> outgoing_move.save()
>>> shipment.click('assign_force')
>>> shipment.effective_start_date = yesterday
>>> shipment.click('pack')
>>> shipment.click('ship')
>>> incoming_move, = shipment.incoming_moves
>>> incoming_move.quantity
1.0
>>> shipment.outgoing_moves[0].state
'done'
>>> assertEqual(shipment.outgoing_moves[0].effective_date, yesterday)
>>> shipment.click('do')
>>> shipment.incoming_moves[0].state
'done'
>>> assertEqual(shipment.incoming_moves[0].effective_date, today)

View File

@@ -0,0 +1,284 @@
===========================
Stock Shipment Out Scenario
===========================
Imports::
>>> import datetime as dt
>>> from decimal import Decimal
>>> from proteus import Model, Report
>>> from trytond.modules.company.tests.tools import create_company
>>> from trytond.modules.currency.tests.tools import get_currency
>>> from trytond.tests.tools import (
... activate_modules, assertEqual, assertNotEqual, set_user)
>>> today = dt.date.today()
>>> yesterday = today - dt.timedelta(days=1)
Activate modules::
>>> config = activate_modules('stock', create_company)
>>> Employee = Model.get('company.employee')
>>> Party = Model.get('party.party')
>>> User = Model.get('res.user')
Set employee::
>>> employee_party = Party(name="Employee")
>>> employee_party.save()
>>> employee = Employee(party=employee_party)
>>> employee.save()
>>> user = User(config.user)
>>> user.employees.append(employee)
>>> user.employee = employee
>>> user.save()
>>> set_user(user.id)
Get currency::
>>> currency = get_currency()
Create customer::
>>> Party = Model.get('party.party')
>>> customer = Party(name='Customer')
>>> customer.save()
Create product::
>>> ProductUom = Model.get('product.uom')
>>> ProductTemplate = Model.get('product.template')
>>> Product = Model.get('product.product')
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
>>> template = ProductTemplate()
>>> template.name = 'Product'
>>> template.default_uom = unit
>>> template.type = 'goods'
>>> template.list_price = Decimal('20')
>>> template.save()
>>> product, = template.products
Get stock locations::
>>> Location = Model.get('stock.location')
>>> warehouse_loc, = Location.find([('code', '=', 'WH')])
>>> supplier_loc, = Location.find([('code', '=', 'SUP')])
>>> customer_loc, = Location.find([('code', '=', 'CUS')])
>>> output_loc, = Location.find([('code', '=', 'OUT')])
>>> storage_loc, = Location.find([('code', '=', 'STO')])
Create Shipment Out::
>>> ShipmentOut = Model.get('stock.shipment.out')
>>> shipment_out = ShipmentOut()
>>> shipment_out.planned_date = today
>>> shipment_out.customer = customer
>>> shipment_out.warehouse = warehouse_loc
Add two shipment lines of same product::
>>> StockMove = Model.get('stock.move')
>>> shipment_out.outgoing_moves.extend([StockMove(), StockMove()])
>>> for move in shipment_out.outgoing_moves:
... move.product = product
... move.unit = unit
... move.quantity = 1
... move.from_location = output_loc
... move.to_location = customer_loc
... move.unit_price = Decimal('1')
... move.currency = currency
>>> shipment_out.save()
>>> shipment_out.number
>>> shipment_out.picked_by
>>> shipment_out.packed_by
>>> shipment_out.shipped_by
>>> shipment_out.done_by
Set the shipment state to waiting::
>>> shipment_out.click('wait')
>>> shipment_out.state
'waiting'
>>> assertNotEqual(shipment_out.number, None)
>>> len(shipment_out.outgoing_moves)
2
>>> len(shipment_out.inventory_moves)
2
>>> assertEqual(
... {m.origin for m in shipment_out.inventory_moves},
... {m for m in shipment_out.outgoing_moves})
Make 1 unit of the product available::
>>> incoming_move = StockMove()
>>> incoming_move.product = product
>>> incoming_move.unit = unit
>>> incoming_move.quantity = 1
>>> incoming_move.from_location = supplier_loc
>>> incoming_move.to_location = storage_loc
>>> incoming_move.planned_date = today
>>> incoming_move.effective_date = today
>>> incoming_move.unit_price = Decimal('1')
>>> incoming_move.currency = currency
>>> incoming_move.click('do')
Assign the shipment now::
>>> shipment_assign = shipment_out.click('assign_wizard')
>>> len(shipment_assign.form.moves)
1
>>> shipment_assign.execute('end')
>>> shipment_out.reload()
>>> len(shipment_out.outgoing_moves)
2
>>> len(shipment_out.inventory_moves)
2
>>> states = [m.state for m in shipment_out.inventory_moves]
>>> states.sort()
>>> states
['assigned', 'draft']
>>> effective_dates = [m.effective_date for m in
... shipment_out.inventory_moves]
>>> len(set(effective_dates))
1
>>> planned_dates = [m.planned_date for m in
... shipment_out.outgoing_moves]
>>> len(set(planned_dates))
1
Ignore non assigned moves and pack shipment::
>>> shipment_assign = shipment_out.click('assign_wizard')
>>> shipment_assign.execute('ignore')
>>> sorted([m.quantity for m in shipment_out.inventory_moves])
[0.0, 1.0]
>>> shipment_out.picked_by
>>> shipment_out.packed_by
>>> shipment_out.shipped_by
>>> shipment_out.done_by
>>> picking_list = Report('stock.shipment.out.picking_list')
>>> _ = picking_list.execute([shipment_out])
>>> shipment_out.click('pick')
>>> assertEqual(shipment_out.picked_by, employee)
>>> shipment_out.packed_by
>>> shipment_out.shipped_by
>>> shipment_out.done_by
>>> shipment_out.click('pack')
>>> assertEqual(shipment_out.packed_by, employee)
>>> shipment_out.shipped_by
>>> shipment_out.done_by
>>> [m.state for m in shipment_out.outgoing_moves]
['assigned']
>>> len(shipment_out.inventory_moves)
1
>>> shipment_out.inventory_moves[0].state
'done'
>>> assertEqual(sum([m.quantity for m in shipment_out.inventory_moves]),
... sum([m.quantity for m in shipment_out.outgoing_moves]))
>>> delivery_note = Report('stock.shipment.out.delivery_note')
>>> _ = delivery_note.execute([shipment_out])
Set the state as Shipped::
>>> shipment_out.click('ship')
>>> assertEqual(shipment_out.shipped_by, employee)
>>> shipment_out.done_by
Set the state as Done::
>>> shipment_out.click('do')
>>> assertEqual(shipment_out.done_by, employee)
>>> [m.state for m in shipment_out.outgoing_moves]
['done']
>>> planned_dates = [m.planned_date for m in
... shipment_out.outgoing_moves]
>>> assertEqual(planned_dates, [today])
>>> effective_dates = [m.effective_date for m in
... shipment_out.outgoing_moves]
>>> len(set(effective_dates))
1
>>> len(shipment_out.outgoing_moves)
1
>>> len(shipment_out.inventory_moves)
1
>>> shipment_out.inventory_moves[0].state
'done'
>>> assertEqual(sum([m.quantity for m in shipment_out.inventory_moves]),
... sum([m.quantity for m in shipment_out.outgoing_moves]))
Create Shipment Out with effective date::
>>> ShipmentOut = Model.get('stock.shipment.out')
>>> shipment_out = ShipmentOut()
>>> shipment_out.planned_date = yesterday
>>> shipment_out.effective_date = yesterday
>>> shipment_out.customer = customer
>>> shipment_out.warehouse = warehouse_loc
>>> move = shipment_out.outgoing_moves.new()
>>> move.product = product
>>> move.unit = unit
>>> move.quantity = 1
>>> move.from_location = output_loc
>>> move.to_location = customer_loc
>>> move.unit_price = Decimal('1')
>>> move.currency = currency
>>> shipment_out.click('wait')
Make 1 unit of the product available::
>>> incoming_move = StockMove()
>>> incoming_move.product = product
>>> incoming_move.unit = unit
>>> incoming_move.quantity = 1
>>> incoming_move.from_location = supplier_loc
>>> incoming_move.to_location = storage_loc
>>> incoming_move.planned_date = yesterday
>>> incoming_move.effective_date = yesterday
>>> incoming_move.unit_price = Decimal('1')
>>> incoming_move.currency = currency
>>> incoming_move.click('do')
Finish the shipment::
>>> shipment_out.click('assign_try')
>>> shipment_out.click('pick')
>>> len(shipment_out.inventory_moves)
1
>>> len(shipment_out.outgoing_moves)
1
>>> shipment_out.click('pack')
>>> shipment_out.click('pick')
>>> len(shipment_out.inventory_moves)
1
>>> len(shipment_out.outgoing_moves)
1
>>> shipment_out.click('pack')
Finish the shipment::
>>> shipment_out.click('do')
>>> shipment_out.state
'done'
>>> outgoing_move, = shipment_out.outgoing_moves
>>> assertEqual(outgoing_move.effective_date, yesterday)
>>> inventory_move, = shipment_out.inventory_moves
>>> assertEqual(inventory_move.effective_date, yesterday)
Reschedule shipment::
>>> shipment_copy, = shipment_out.duplicate()
>>> shipment_copy.planned_date = yesterday
>>> shipment_copy.click('wait')
>>> Cron = Model.get('ir.cron')
>>> cron = Cron(method='stock.shipment.out|reschedule')
>>> cron.interval_number = 1
>>> cron.interval_type = 'months'
>>> cron.click('run_once')
>>> shipment_copy.reload()
>>> assertEqual(shipment_copy.planned_date, today)

View File

@@ -0,0 +1,89 @@
==================================
Stock Shipment Out Return Scenario
==================================
Imports::
>>> from decimal import Decimal
>>> from proteus import Model, Report
>>> from trytond.modules.company.tests.tools import create_company
>>> from trytond.modules.currency.tests.tools import get_currency
>>> from trytond.tests.tools import activate_modules, assertNotEqual
Activate modules::
>>> config = activate_modules('stock', create_company)
>>> Party = Model.get('party.party')
>>> ProductUom = Model.get('product.uom')
>>> ProductTemplate = Model.get('product.template')
Create customer::
>>> customer = Party(name='Customer')
>>> customer.save()
Create product::
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
>>> template = ProductTemplate()
>>> template.name = "Product"
>>> template.default_uom = unit
>>> template.type = 'goods'
>>> template.list_price = Decimal('20')
>>> template.save()
>>> product, = template.products
Get stock locations::
>>> Location = Model.get('stock.location')
>>> warehouse_loc, = Location.find([('code', '=', 'WH')])
>>> customer_loc, = Location.find([('code', '=', 'CUS')])
>>> storage_loc, = Location.find([('code', '=', 'STO')])
Create Shipment Out Return::
>>> ShipmentOutReturn = Model.get('stock.shipment.out.return')
>>> shipment = ShipmentOutReturn()
>>> shipment.customer = customer
>>> shipment.warehouse = warehouse_loc
>>> move = shipment.incoming_moves.new()
>>> move.product = product
>>> move.unit = unit
>>> move.quantity = 1
>>> move.from_location = customer_loc
>>> move.to_location = warehouse_loc.input_location
>>> move.unit_price = Decimal('20')
>>> move.currency = get_currency()
>>> shipment.save()
>>> assertNotEqual(shipment.number, None)
>>> len(shipment.incoming_moves)
1
>>> len(shipment.inventory_moves)
0
Receive shipment::
>>> shipment.click('receive')
>>> shipment.state
'received'
>>> [m.state for m in shipment.incoming_moves]
['done']
>>> [m.state for m in shipment.inventory_moves]
['draft']
>>> restocking_list = Report('stock.shipment.out.return.restocking_list')
>>> _ = restocking_list.execute([shipment])
Finish the shipment::
>>> shipment.click('do')
>>> shipment.state
'done'
>>> len(shipment.incoming_moves)
1
>>> [m.state for m in shipment.inventory_moves]
['done']

View File

@@ -0,0 +1,82 @@
=========================================================
Stock Shipment Out Return Same Storage and Input Scenario
=========================================================
Imports::
>>> from decimal import Decimal
>>> from proteus import Model
>>> from trytond.modules.company.tests.tools import create_company
>>> from trytond.modules.currency.tests.tools import get_currency
>>> from trytond.tests.tools import activate_modules
Activate modules::
>>> config = activate_modules('stock', create_company)
Create customer::
>>> Party = Model.get('party.party')
>>> customer = Party(name='Customer')
>>> customer.save()
Create product::
>>> ProductUom = Model.get('product.uom')
>>> ProductTemplate = Model.get('product.template')
>>> Product = Model.get('product.product')
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
>>> template = ProductTemplate()
>>> template.name = 'Product'
>>> template.default_uom = unit
>>> template.type = 'goods'
>>> template.list_price = Decimal('20')
>>> template.save()
>>> product, = template.products
Get stock locations::
>>> Location = Model.get('stock.location')
>>> warehouse_loc, = Location.find([('code', '=', 'WH')])
>>> customer_loc, = Location.find([('code', '=', 'CUS')])
>>> storage_loc, = Location.find([('code', '=', 'STO')])
>>> storage1 = Location(name="Storage 1", parent=storage_loc)
>>> storage1.save()
Use storage location as input location::
>>> warehouse_loc.input_location = storage_loc
>>> warehouse_loc.save()
Create Shipment Out Return::
>>> ShipmentOutReturn = Model.get('stock.shipment.out.return')
>>> shipment = ShipmentOutReturn()
>>> shipment.customer = customer
>>> shipment.warehouse = warehouse_loc
>>> move = shipment.incoming_moves.new()
>>> move.product = product
>>> move.unit = unit
>>> move.quantity = 1
>>> move.from_location = customer_loc
>>> move.to_location = storage1
>>> move.unit_price = Decimal('20')
>>> move.currency = get_currency()
>>> shipment.save()
>>> len(shipment.incoming_moves)
1
>>> len(shipment.inventory_moves)
0
Shipment is done when receiving::
>>> shipment.click('receive')
>>> shipment.state
'done'
>>> move, = shipment.incoming_moves
>>> move.state
'done'
>>> len(shipment.inventory_moves)
0

View File

@@ -0,0 +1,117 @@
===================================================
Stock Shipment Out Same Storage and Output Scenario
===================================================
Imports::
>>> from decimal import Decimal
>>> from proteus import Model
>>> from trytond.modules.company.tests.tools import create_company
>>> from trytond.modules.currency.tests.tools import get_currency
>>> from trytond.tests.tools import activate_modules
Activate modules::
>>> config = activate_modules('stock', create_company)
>>> Move = Model.get('stock.move')
Create customer::
>>> Party = Model.get('party.party')
>>> customer = Party(name='Customer')
>>> customer.save()
Create product::
>>> ProductUom = Model.get('product.uom')
>>> ProductTemplate = Model.get('product.template')
>>> Product = Model.get('product.product')
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
>>> template = ProductTemplate()
>>> template.name = 'Product'
>>> template.default_uom = unit
>>> template.type = 'goods'
>>> template.list_price = Decimal('20')
>>> template.save()
>>> product, = template.products
Get stock locations::
>>> Location = Model.get('stock.location')
>>> warehouse_loc, = Location.find([('code', '=', 'WH')])
>>> customer_loc, = Location.find([('code', '=', 'CUS')])
>>> storage_loc, = Location.find([('code', '=', 'STO')])
>>> warehouse_loc.output_location = storage_loc
>>> warehouse_loc.save()
Create Shipment Out::
>>> ShipmentOut = Model.get('stock.shipment.out')
>>> shipment_out = ShipmentOut()
>>> shipment_out.customer = customer
>>> shipment_out.warehouse = warehouse_loc
>>> move = shipment_out.outgoing_moves.new()
>>> move.product = product
>>> move.unit = unit
>>> move.quantity = 1
>>> move.unit_price = Decimal('5')
>>> move.currency = get_currency()
>>> move.from_location = storage_loc
>>> move.to_location = customer_loc
>>> shipment_out.save()
>>> len(shipment_out.outgoing_moves)
1
>>> len(shipment_out.inventory_moves)
0
Set the shipment state to waiting::
>>> shipment_out.click('wait')
>>> shipment_out.state
'waiting'
>>> len(shipment_out.outgoing_moves)
1
>>> len(shipment_out.inventory_moves)
0
Try to assign::
>>> shipment_out.click('assign_try')
>>> shipment_out.state
'waiting'
>>> move, = shipment_out.outgoing_moves
>>> move.state
'draft'
Fill storage location::
>>> move = Move()
>>> move.from_location = warehouse_loc.lost_found_location
>>> move.to_location = storage_loc
>>> move.product = product
>>> move.quantity = 1
>>> move.click('do')
>>> move.state
'done'
Try to assign again::
>>> shipment_out.click('assign_try')
>>> shipment_out.state
'assigned'
>>> move, = shipment_out.outgoing_moves
>>> move.state
'assigned'
Pack the shipment::
>>> shipment_out.click('pack')
>>> shipment_out.state
'packed'
>>> len(shipment_out.outgoing_moves)
1
>>> len(shipment_out.inventory_moves)
0

Some files were not shown because too many files have changed in this diff Show More