first commit
This commit is contained in:
2
modules/purchase/__init__.py
Normal file
2
modules/purchase/__init__.py
Normal 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.
|
||||
BIN
modules/purchase/__pycache__/__init__.cpython-311.pyc
Normal file
BIN
modules/purchase/__pycache__/__init__.cpython-311.pyc
Normal file
Binary file not shown.
BIN
modules/purchase/__pycache__/configuration.cpython-311.pyc
Normal file
BIN
modules/purchase/__pycache__/configuration.cpython-311.pyc
Normal file
Binary file not shown.
BIN
modules/purchase/__pycache__/exceptions.cpython-311.pyc
Normal file
BIN
modules/purchase/__pycache__/exceptions.cpython-311.pyc
Normal file
Binary file not shown.
BIN
modules/purchase/__pycache__/invoice.cpython-311.pyc
Normal file
BIN
modules/purchase/__pycache__/invoice.cpython-311.pyc
Normal file
Binary file not shown.
BIN
modules/purchase/__pycache__/party.cpython-311.pyc
Normal file
BIN
modules/purchase/__pycache__/party.cpython-311.pyc
Normal file
Binary file not shown.
BIN
modules/purchase/__pycache__/product.cpython-311.pyc
Normal file
BIN
modules/purchase/__pycache__/product.cpython-311.pyc
Normal file
Binary file not shown.
BIN
modules/purchase/__pycache__/purchase.cpython-311.pyc
Normal file
BIN
modules/purchase/__pycache__/purchase.cpython-311.pyc
Normal file
Binary file not shown.
BIN
modules/purchase/__pycache__/purchase_reporting.cpython-311.pyc
Normal file
BIN
modules/purchase/__pycache__/purchase_reporting.cpython-311.pyc
Normal file
Binary file not shown.
BIN
modules/purchase/__pycache__/stock.cpython-311.pyc
Normal file
BIN
modules/purchase/__pycache__/stock.cpython-311.pyc
Normal file
Binary file not shown.
93
modules/purchase/configuration.py
Normal file
93
modules/purchase/configuration.py
Normal file
@@ -0,0 +1,93 @@
|
||||
# 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, TimeDelta
|
||||
|
||||
purchase_invoice_method = fields.Selection(
|
||||
'get_purchase_invoice_method', "Invoice Method")
|
||||
|
||||
|
||||
def get_purchase_methods(field_name):
|
||||
@classmethod
|
||||
def func(cls):
|
||||
pool = Pool()
|
||||
Purchase = pool.get('purchase.purchase')
|
||||
return Purchase.fields_get([field_name])[field_name]['selection']
|
||||
return func
|
||||
|
||||
|
||||
class Configuration(
|
||||
ModelSingleton, ModelSQL, ModelView, CompanyMultiValueMixin):
|
||||
__name__ = 'purchase.configuration'
|
||||
purchase_sequence = fields.MultiValue(fields.Many2One(
|
||||
'ir.sequence', "Purchase Sequence", required=True,
|
||||
domain=[
|
||||
('company', 'in',
|
||||
[Eval('context', {}).get('company', -1), None]),
|
||||
('sequence_type', '=',
|
||||
Id('purchase', 'sequence_type_purchase')),
|
||||
]))
|
||||
purchase_invoice_method = fields.MultiValue(purchase_invoice_method)
|
||||
get_purchase_invoice_method = get_purchase_methods('invoice_method')
|
||||
purchase_process_after = fields.TimeDelta(
|
||||
"Process Purchase after",
|
||||
domain=['OR',
|
||||
('purchase_process_after', '=', None),
|
||||
('purchase_process_after', '>=', TimeDelta()),
|
||||
],
|
||||
help="The grace period during which confirmed purchase "
|
||||
"can still be reset to draft.\n"
|
||||
"Applied only if a worker queue is activated.")
|
||||
|
||||
@classmethod
|
||||
def multivalue_model(cls, field):
|
||||
pool = Pool()
|
||||
if field == 'purchase_invoice_method':
|
||||
return pool.get('purchase.configuration.purchase_method')
|
||||
if field == 'purchase_sequence':
|
||||
return pool.get('purchase.configuration.sequence')
|
||||
return super().multivalue_model(field)
|
||||
|
||||
@classmethod
|
||||
def default_purchase_sequence(cls, **pattern):
|
||||
return cls.multivalue_model(
|
||||
'purchase_sequence').default_purchase_sequence()
|
||||
|
||||
@classmethod
|
||||
def default_purchase_invoice_method(cls, **pattern):
|
||||
return cls.multivalue_model(
|
||||
'purchase_invoice_method').default_purchase_invoice_method()
|
||||
|
||||
|
||||
class ConfigurationSequence(ModelSQL, CompanyValueMixin):
|
||||
__name__ = 'purchase.configuration.sequence'
|
||||
purchase_sequence = fields.Many2One(
|
||||
'ir.sequence', "Purchase Sequence", required=True,
|
||||
domain=[
|
||||
('company', 'in', [Eval('company', -1), None]),
|
||||
('sequence_type', '=',
|
||||
Id('purchase', 'sequence_type_purchase')),
|
||||
])
|
||||
|
||||
@classmethod
|
||||
def default_purchase_sequence(cls):
|
||||
pool = Pool()
|
||||
ModelData = pool.get('ir.model.data')
|
||||
try:
|
||||
return ModelData.get_id('purchase', 'sequence_purchase')
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
|
||||
class ConfigurationPurchaseMethod(ModelSQL, ValueMixin):
|
||||
__name__ = 'purchase.configuration.purchase_method'
|
||||
purchase_invoice_method = purchase_invoice_method
|
||||
get_purchase_invoice_method = get_purchase_methods('invoice_method')
|
||||
|
||||
@classmethod
|
||||
def default_purchase_invoice_method(cls):
|
||||
return 'order'
|
||||
57
modules/purchase/configuration.xml
Normal file
57
modules/purchase/configuration.xml
Normal file
@@ -0,0 +1,57 @@
|
||||
<?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>
|
||||
<!-- set active for 1.4 migration -->
|
||||
<menuitem
|
||||
name="Configuration"
|
||||
parent="menu_purchase"
|
||||
sequence="0"
|
||||
id="menu_configuration"
|
||||
icon="tryton-settings"
|
||||
active="1"/>
|
||||
<record model="ir.ui.menu-res.group" id="menu_configuration_group_purchase_admin">
|
||||
<field name="menu" ref="menu_configuration"/>
|
||||
<field name="group" ref="group_purchase_admin"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="purchase_configuration_view_form">
|
||||
<field name="model">purchase.configuration</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">configuration_form</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window" id="act_purchase_configuration_form">
|
||||
<field name="name">Configuration</field>
|
||||
<field name="res_model">purchase.configuration</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view"
|
||||
id="act_purchase_configuration_view1">
|
||||
<field name="sequence" eval="1"/>
|
||||
<field name="view" ref="purchase_configuration_view_form"/>
|
||||
<field name="act_window" ref="act_purchase_configuration_form"/>
|
||||
</record>
|
||||
<menuitem
|
||||
parent="menu_configuration"
|
||||
action="act_purchase_configuration_form"
|
||||
sequence="10"
|
||||
id="menu_purchase_configuration"
|
||||
icon="tryton-list"/>
|
||||
|
||||
<record model="ir.model.access" id="access_purchase_configuration">
|
||||
<field name="model">purchase.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_purchase_configuration_purchase_admin">
|
||||
<field name="model">purchase.configuration</field>
|
||||
<field name="group" ref="group_purchase_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>
|
||||
21
modules/purchase/exceptions.py
Normal file
21
modules/purchase/exceptions.py
Normal file
@@ -0,0 +1,21 @@
|
||||
# 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 PurchaseUOMWarning(UserWarning):
|
||||
pass
|
||||
|
||||
|
||||
class PurchaseQuotationError(ValidationError):
|
||||
pass
|
||||
|
||||
|
||||
class PurchaseMoveQuantity(UserWarning):
|
||||
pass
|
||||
|
||||
|
||||
class PartyLocationError(UserError):
|
||||
pass
|
||||
4
modules/purchase/icons/tryton-purchase.svg
Normal file
4
modules/purchase/icons/tryton-purchase.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
||||
<path d="M7 18c-1.1 0-1.99.9-1.99 2S5.9 22 7 22s2-.9 2-2-.9-2-2-2zM1 2v2h2l3.6 7.59-1.35 2.45c-.16.28-.25.61-.25.96 0 1.1.9 2 2 2h12v-2H7.42c-.14 0-.25-.11-.25-.25l.03-.12.9-1.63h7.45c.75 0 1.41-.41 1.75-1.03l3.58-6.49c.08-.14.12-.31.12-.48 0-.55-.45-1-1-1H5.21l-.94-2H1zm16 16c-1.1 0-1.99.9-1.99 2s.89 2 1.99 2 2-.9 2-2-.9-2-2-2z"/>
|
||||
<path d="M0 0h24v24H0z" fill="none"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 471 B |
177
modules/purchase/invoice.py
Normal file
177
modules/purchase/invoice.py
Normal file
@@ -0,0 +1,177 @@
|
||||
# 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 functools import wraps
|
||||
|
||||
from trytond.i18n import gettext
|
||||
from trytond.model import ModelView, Workflow, fields
|
||||
from trytond.model.exceptions import AccessError
|
||||
from trytond.pool import Pool, PoolMeta
|
||||
from trytond.pyson import Eval
|
||||
from trytond.tools import cached_property
|
||||
from trytond.transaction import Transaction, without_check_access
|
||||
|
||||
|
||||
def process_purchase(func):
|
||||
@wraps(func)
|
||||
def wrapper(cls, invoices):
|
||||
pool = Pool()
|
||||
Purchase = pool.get('purchase.purchase')
|
||||
transaction = Transaction()
|
||||
context = transaction.context
|
||||
with without_check_access():
|
||||
purchases = set(
|
||||
p for i in cls.browse(invoices) for p in i.purchases)
|
||||
result = func(cls, invoices)
|
||||
if purchases:
|
||||
with transaction.set_context(
|
||||
queue_batch=context.get('queue_batch', True)):
|
||||
Purchase.__queue__.process(purchases)
|
||||
return result
|
||||
return wrapper
|
||||
|
||||
|
||||
class Invoice(metaclass=PoolMeta):
|
||||
__name__ = 'account.invoice'
|
||||
purchase_exception_state = fields.Function(fields.Selection([
|
||||
('', ''),
|
||||
('ignored', 'Ignored'),
|
||||
('recreated', 'Recreated'),
|
||||
], 'Exception State'), 'get_purchase_exception_state')
|
||||
purchases = fields.Function(fields.Many2Many(
|
||||
'purchase.purchase', None, None, "Purchases"),
|
||||
'get_purchases', searcher='search_purchases')
|
||||
|
||||
def get_purchase_exception_state(self, name):
|
||||
purchases = self.purchases
|
||||
|
||||
recreated = tuple(i for p in purchases for i in p.invoices_recreated)
|
||||
ignored = tuple(i for p in purchases for i in p.invoices_ignored)
|
||||
|
||||
if self in recreated:
|
||||
return 'recreated'
|
||||
elif self in ignored:
|
||||
return 'ignored'
|
||||
return ''
|
||||
|
||||
def get_purchases(self, name):
|
||||
pool = Pool()
|
||||
PurchaseLine = pool.get('purchase.line')
|
||||
purchases = set()
|
||||
for line in self.lines:
|
||||
if isinstance(line.origin, PurchaseLine):
|
||||
purchases.add(line.origin.purchase.id)
|
||||
return list(purchases)
|
||||
|
||||
@classmethod
|
||||
def search_purchases(cls, name, clause):
|
||||
return [('lines.origin.purchase' + clause[0][len(name):],
|
||||
*clause[1:3], 'purchase.line', *clause[3:])]
|
||||
|
||||
@classmethod
|
||||
@process_purchase
|
||||
def on_delete(cls, invoices):
|
||||
return super().on_delete(invoices)
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
@Workflow.transition('draft')
|
||||
def draft(cls, invoices):
|
||||
for invoice in invoices:
|
||||
if invoice.purchases and invoice.state == 'cancelled':
|
||||
raise AccessError(
|
||||
gettext('purchase.msg_purchase_invoice_reset_draft',
|
||||
invoice=invoice.rec_name))
|
||||
|
||||
return super().draft(invoices)
|
||||
|
||||
@classmethod
|
||||
@process_purchase
|
||||
def _post(cls, invoices):
|
||||
super()._post(invoices)
|
||||
|
||||
@classmethod
|
||||
@process_purchase
|
||||
def paid(cls, invoices):
|
||||
super().paid(invoices)
|
||||
|
||||
@classmethod
|
||||
@process_purchase
|
||||
def cancel(cls, invoices):
|
||||
super().cancel(invoices)
|
||||
|
||||
|
||||
class InvoiceLine(metaclass=PoolMeta):
|
||||
__name__ = 'account.invoice.line'
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super().__setup__()
|
||||
if not cls.origin.domain:
|
||||
cls.origin.domain = {}
|
||||
cls.origin.domain['purchase.line'] = [
|
||||
('type', '=', Eval('type')),
|
||||
]
|
||||
|
||||
@cached_property
|
||||
def product_name(self):
|
||||
pool = Pool()
|
||||
PurchaseLine = pool.get('purchase.line')
|
||||
name = super().product_name
|
||||
if (isinstance(self.origin, PurchaseLine)
|
||||
and self.origin.product_supplier):
|
||||
name = self.origin.product_supplier.rec_name
|
||||
return name
|
||||
|
||||
@fields.depends('origin')
|
||||
def on_change_with_product_uom_category(self, name=None):
|
||||
pool = Pool()
|
||||
PurchaseLine = pool.get('purchase.line')
|
||||
category = super().on_change_with_product_uom_category(name=name)
|
||||
# Enforce the same unit category as they are used to compute the
|
||||
# remaining quantity to invoice and the quantity to receive.
|
||||
# Use getattr as reference field can have negative id
|
||||
if (isinstance(self.origin, PurchaseLine)
|
||||
and getattr(self.origin, 'unit', None)):
|
||||
category = self.origin.unit.category
|
||||
return category
|
||||
|
||||
def get_warehouse(self, name):
|
||||
pool = Pool()
|
||||
PurchaseLine = pool.get('purchase.line')
|
||||
warehouse = super().get_warehouse(name)
|
||||
if (not warehouse
|
||||
and isinstance(self.origin, PurchaseLine)
|
||||
and self.origin.purchase.warehouse):
|
||||
warehouse = self.origin.purchase.warehouse.id
|
||||
return warehouse
|
||||
|
||||
@property
|
||||
def origin_name(self):
|
||||
pool = Pool()
|
||||
PurchaseLine = pool.get('purchase.line')
|
||||
name = super().origin_name
|
||||
if isinstance(self.origin, PurchaseLine) and self.origin.id >= 0:
|
||||
name = self.origin.purchase.rec_name
|
||||
return name
|
||||
|
||||
@classmethod
|
||||
def _get_origin(cls):
|
||||
models = super()._get_origin()
|
||||
models.append('purchase.line')
|
||||
return models
|
||||
|
||||
@classmethod
|
||||
def on_delete(cls, lines):
|
||||
pool = Pool()
|
||||
Purchase = pool.get('purchase.purchase')
|
||||
transaction = Transaction()
|
||||
context = transaction.context
|
||||
with without_check_access():
|
||||
invoices = (l.invoice for l in cls.browse(lines)
|
||||
if l.type == 'line' and l.invoice)
|
||||
purchases = set(p for i in invoices for p in i.purchases)
|
||||
if purchases:
|
||||
with transaction.set_context(
|
||||
queue_batch=context.get('queue_batch', True)):
|
||||
Purchase.__queue__.process(purchases)
|
||||
return super().on_delete(lines)
|
||||
54
modules/purchase/invoice.xml
Normal file
54
modules/purchase/invoice.xml
Normal file
@@ -0,0 +1,54 @@
|
||||
<?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.action.act_window" id="act_invoice_form">
|
||||
<field name="name">Invoices</field>
|
||||
<field name="res_model">account.invoice</field>
|
||||
<field name="context"></field>
|
||||
<field
|
||||
name="domain"
|
||||
eval="[('purchases.id', 'in', Eval('active_ids', []))]"
|
||||
pyson="1"/>
|
||||
</record>
|
||||
<record model="ir.action.keyword"
|
||||
id="act_open_invoice_keyword1">
|
||||
<field name="keyword">form_relate</field>
|
||||
<field name="model">purchase.purchase,-1</field>
|
||||
<field name="action" ref="act_invoice_form"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.access" id="access_invoice_purchase">
|
||||
<field name="model">account.invoice</field>
|
||||
<field name="group" ref="group_purchase"/>
|
||||
<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.action.act_window" id="act_invoice_line_form">
|
||||
<field name="name">Invoice Lines</field>
|
||||
<field name="res_model">account.invoice.line</field>
|
||||
<field
|
||||
name="domain"
|
||||
eval="[('origin.id', 'in', Eval('active_ids', []), 'purchase.line')]"
|
||||
pyson="1"/>
|
||||
</record>
|
||||
<record model="ir.action.keyword" id="act_invoice_line_form_keyword1">
|
||||
<field name="keyword">form_relate</field>
|
||||
<field name="model">purchase.line,-1</field>
|
||||
<field name="action" ref="act_invoice_line_form"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.access" id="access_invoice_line_purchase">
|
||||
<field name="model">account.invoice.line</field>
|
||||
<field name="group" ref="group_purchase"/>
|
||||
<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>
|
||||
1608
modules/purchase/locale/bg.po
Normal file
1608
modules/purchase/locale/bg.po
Normal file
File diff suppressed because it is too large
Load Diff
1491
modules/purchase/locale/ca.po
Normal file
1491
modules/purchase/locale/ca.po
Normal file
File diff suppressed because it is too large
Load Diff
1548
modules/purchase/locale/cs.po
Normal file
1548
modules/purchase/locale/cs.po
Normal file
File diff suppressed because it is too large
Load Diff
1494
modules/purchase/locale/de.po
Normal file
1494
modules/purchase/locale/de.po
Normal file
File diff suppressed because it is too large
Load Diff
1493
modules/purchase/locale/es.po
Normal file
1493
modules/purchase/locale/es.po
Normal file
File diff suppressed because it is too large
Load Diff
1508
modules/purchase/locale/es_419.po
Normal file
1508
modules/purchase/locale/es_419.po
Normal file
File diff suppressed because it is too large
Load Diff
1577
modules/purchase/locale/et.po
Normal file
1577
modules/purchase/locale/et.po
Normal file
File diff suppressed because it is too large
Load Diff
1601
modules/purchase/locale/fa.po
Normal file
1601
modules/purchase/locale/fa.po
Normal file
File diff suppressed because it is too large
Load Diff
1547
modules/purchase/locale/fi.po
Normal file
1547
modules/purchase/locale/fi.po
Normal file
File diff suppressed because it is too large
Load Diff
1489
modules/purchase/locale/fr.po
Normal file
1489
modules/purchase/locale/fr.po
Normal file
File diff suppressed because it is too large
Load Diff
1567
modules/purchase/locale/hu.po
Normal file
1567
modules/purchase/locale/hu.po
Normal file
File diff suppressed because it is too large
Load Diff
1545
modules/purchase/locale/id.po
Normal file
1545
modules/purchase/locale/id.po
Normal file
File diff suppressed because it is too large
Load Diff
1587
modules/purchase/locale/it.po
Normal file
1587
modules/purchase/locale/it.po
Normal file
File diff suppressed because it is too large
Load Diff
1640
modules/purchase/locale/lo.po
Normal file
1640
modules/purchase/locale/lo.po
Normal file
File diff suppressed because it is too large
Load Diff
1578
modules/purchase/locale/lt.po
Normal file
1578
modules/purchase/locale/lt.po
Normal file
File diff suppressed because it is too large
Load Diff
1494
modules/purchase/locale/nl.po
Normal file
1494
modules/purchase/locale/nl.po
Normal file
File diff suppressed because it is too large
Load Diff
1525
modules/purchase/locale/pl.po
Normal file
1525
modules/purchase/locale/pl.po
Normal file
File diff suppressed because it is too large
Load Diff
1492
modules/purchase/locale/pt.po
Normal file
1492
modules/purchase/locale/pt.po
Normal file
File diff suppressed because it is too large
Load Diff
1564
modules/purchase/locale/ro.po
Normal file
1564
modules/purchase/locale/ro.po
Normal file
File diff suppressed because it is too large
Load Diff
1612
modules/purchase/locale/ru.po
Normal file
1612
modules/purchase/locale/ru.po
Normal file
File diff suppressed because it is too large
Load Diff
1551
modules/purchase/locale/sl.po
Normal file
1551
modules/purchase/locale/sl.po
Normal file
File diff suppressed because it is too large
Load Diff
1546
modules/purchase/locale/tr.po
Normal file
1546
modules/purchase/locale/tr.po
Normal file
File diff suppressed because it is too large
Load Diff
1470
modules/purchase/locale/uk.po
Normal file
1470
modules/purchase/locale/uk.po
Normal file
File diff suppressed because it is too large
Load Diff
1514
modules/purchase/locale/zh_CN.po
Normal file
1514
modules/purchase/locale/zh_CN.po
Normal file
File diff suppressed because it is too large
Load Diff
73
modules/purchase/message.xml
Normal file
73
modules/purchase/message.xml
Normal file
@@ -0,0 +1,73 @@
|
||||
<?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_change_purchase_uom">
|
||||
<field name="text">You are trying to change the purchase unit of measure on which the purchase prices are based.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_erase_party_pending_purchase">
|
||||
<field name="text">You cannot erase party "%(party)s" while they have pending purchases with company "%(company)s".</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_warehouse_required_for_quotation">
|
||||
<field name="text">To get a quote for the purchase "%(purchase)s" you must enter a warehouse for the line "%(line)s".</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_purchase_delete_cancel">
|
||||
<field name="text">To delete purchase "%(purchase)s" you must cancel it.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_purchase_supplier_location_required">
|
||||
<field name="text">To process purchase "%(purchase)s" you must set a supplier location on party "%(party)s".</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_purchase_product_missing_account_expense">
|
||||
<field name="text">To invoice purchase "%(purchase)s" you must define an account expense for product "%(product)s".</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_purchase_missing_account_expense">
|
||||
<field name="text">To invoice purchase "%(purchase)s" you must configure a default account expense.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_purchase_line_delete_cancel_draft">
|
||||
<field name="text">To delete line "%(line)s" you must cancel or reset to draft purchase "%(purchase)s".</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_purchase_line_move_quantity">
|
||||
<field name="text">The purchase line "%(line)s" is moving %(extra)s in addition to the %(quantity)s ordered.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_purchase_modify_header_draft">
|
||||
<field name="text">To modify the header of purchase "%(purchase)s", it must be in draft state.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_purchase_invoice_reset_draft">
|
||||
<field name="text">You cannot reset invoice "%(invoice)s" to draft because it was generated by a purchase.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_purchase_move_reset_draft">
|
||||
<field name="text">You cannot reset move "%(move)s" to draft because it was generated by a purchase.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_purchase_line_create_draft">
|
||||
<field name="text">You cannot add lines to purchase "%(purchase)s" because it is no longer in a draft state.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_purchase_line_tax_unique">
|
||||
<field name="text">A tax can be added only once to a purchase line.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_purchase_reporting_company">
|
||||
<field name="text">Company</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_purchase_reporting_number">
|
||||
<field name="text">#</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_purchase_reporting_number_help">
|
||||
<field name="text">Number of purchases.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_purchase_reporting_expense">
|
||||
<field name="text">Expense</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_purchase_reporting_expense_trend">
|
||||
<field name="text">Expense Trend</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_purchase_reporting_currency">
|
||||
<field name="text">Currency</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_purchase_reporting_date">
|
||||
<field name="text">Date</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_purchase_reporting_time_series">
|
||||
<field name="text">Time Series</field>
|
||||
</record>
|
||||
</data>
|
||||
</tryton>
|
||||
98
modules/purchase/party.py
Normal file
98
modules/purchase/party.py
Normal file
@@ -0,0 +1,98 @@
|
||||
# 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.company.model import (
|
||||
CompanyMultiValueMixin, CompanyValueMixin)
|
||||
from trytond.modules.party.exceptions import EraseError
|
||||
from trytond.pool import Pool, PoolMeta
|
||||
from trytond.pyson import Eval, TimeDelta
|
||||
|
||||
supplier_currency = fields.Many2One(
|
||||
'currency.currency', "Supplier Currency", ondelete='RESTRICT',
|
||||
help="Default currency for purchases from this party.")
|
||||
supplier_lead_time = fields.TimeDelta(
|
||||
"Lead Time",
|
||||
domain=['OR',
|
||||
('supplier_lead_time', '=', None),
|
||||
('supplier_lead_time', '>=', TimeDelta()),
|
||||
],
|
||||
help="The time from confirming the purchase order to receiving "
|
||||
"the goods from the party when used as a supplier.\n"
|
||||
"Used if no lead time is set on the product supplier.")
|
||||
|
||||
|
||||
class Party(CompanyMultiValueMixin, metaclass=PoolMeta):
|
||||
__name__ = 'party.party'
|
||||
customer_code = fields.MultiValue(fields.Char('Customer Code',
|
||||
help="The code the party as supplier has assigned to the company"
|
||||
" as customer."))
|
||||
customer_codes = fields.One2Many(
|
||||
'party.party.customer_code', 'party', "Customer Codes")
|
||||
supplier_lead_time = fields.MultiValue(supplier_lead_time)
|
||||
supplier_lead_times = fields.One2Many(
|
||||
'party.party.supplier_lead_time', 'party', "Lead Times")
|
||||
supplier_currency = fields.MultiValue(supplier_currency)
|
||||
supplier_currencies = fields.One2Many(
|
||||
'party.party.supplier_currency', 'party', "Supplier Currencies")
|
||||
|
||||
|
||||
class CustomerCode(ModelSQL, CompanyValueMixin):
|
||||
__name__ = 'party.party.customer_code'
|
||||
party = fields.Many2One(
|
||||
'party.party', "Party", ondelete='CASCADE',
|
||||
context={
|
||||
'company': Eval('company', -1),
|
||||
},
|
||||
depends={'company'})
|
||||
customer_code = fields.Char('Customer Code')
|
||||
|
||||
|
||||
class SupplierLeadTime(ModelSQL, CompanyValueMixin):
|
||||
__name__ = 'party.party.supplier_lead_time'
|
||||
party = fields.Many2One(
|
||||
'party.party', "Party", ondelete='CASCADE',
|
||||
context={
|
||||
'company': Eval('company', -1),
|
||||
},
|
||||
depends={'company'})
|
||||
supplier_lead_time = supplier_lead_time
|
||||
|
||||
|
||||
class PartySupplierCurrency(ModelSQL, ValueMixin):
|
||||
__name__ = 'party.party.supplier_currency'
|
||||
party = fields.Many2One(
|
||||
'party.party', "Party", ondelete='CASCADE')
|
||||
supplier_currency = supplier_currency
|
||||
|
||||
|
||||
class PartyReplace(metaclass=PoolMeta):
|
||||
__name__ = 'party.replace'
|
||||
|
||||
@classmethod
|
||||
def fields_to_replace(cls):
|
||||
return super().fields_to_replace() + [
|
||||
('purchase.product_supplier', 'party'),
|
||||
('purchase.purchase', 'party'),
|
||||
('purchase.purchase', 'invoice_party'),
|
||||
]
|
||||
|
||||
|
||||
class PartyErase(metaclass=PoolMeta):
|
||||
__name__ = 'party.erase'
|
||||
|
||||
def check_erase_company(self, party, company):
|
||||
pool = Pool()
|
||||
Purchase = pool.get('purchase.purchase')
|
||||
super().check_erase_company(party, company)
|
||||
|
||||
purchases = Purchase.search([
|
||||
('party', '=', party.id),
|
||||
('company', '=', company.id),
|
||||
('state', 'not in', ['done', 'cancelled']),
|
||||
])
|
||||
if purchases:
|
||||
raise EraseError(
|
||||
gettext('purchase.msg_erase_party_pending_purchase',
|
||||
party=party.rec_name,
|
||||
company=company.rec_name))
|
||||
26
modules/purchase/party.xml
Normal file
26
modules/purchase/party.xml
Normal file
@@ -0,0 +1,26 @@
|
||||
<?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_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.model.field.access" id="access_party_supplier_currency">
|
||||
<field name="model">party.party</field>
|
||||
<field name="field">supplier_currency</field>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_write" eval="False"/>
|
||||
</record>
|
||||
<record model="ir.model.field.access" id="access_party_supplier_currency_group_sale">
|
||||
<field name="model">party.party</field>
|
||||
<field name="field">supplier_currency</field>
|
||||
<field name="group" ref="group_purchase"/>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_write" eval="True"/>
|
||||
</record>
|
||||
</data>
|
||||
</tryton>
|
||||
554
modules/purchase/product.py
Normal file
554
modules/purchase/product.py
Normal file
@@ -0,0 +1,554 @@
|
||||
# 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
|
||||
from collections import defaultdict
|
||||
|
||||
from sql import Literal
|
||||
from sql.aggregate import Count, Max
|
||||
|
||||
from trytond.i18n import gettext
|
||||
from trytond.model import (
|
||||
Index, MatchMixin, ModelSQL, ModelView, fields, sequence_ordered)
|
||||
from trytond.modules.currency.fields import Monetary
|
||||
from trytond.modules.product import (
|
||||
ProductDeactivatableMixin, price_digits, round_price)
|
||||
from trytond.pool import Pool, PoolMeta
|
||||
from trytond.pyson import Bool, Eval, If, TimeDelta
|
||||
from trytond.tools import (
|
||||
grouped_slice, is_full_text, lstrip_wildcard, reduce_ids)
|
||||
from trytond.transaction import Transaction
|
||||
|
||||
from .exceptions import PurchaseUOMWarning
|
||||
|
||||
|
||||
class Template(metaclass=PoolMeta):
|
||||
__name__ = "product.template"
|
||||
purchasable = fields.Boolean("Purchasable")
|
||||
product_suppliers = fields.One2Many(
|
||||
'purchase.product_supplier', 'template', "Suppliers",
|
||||
states={
|
||||
'invisible': (~Eval('purchasable', False)
|
||||
| ~Eval('context', {}).get('company')),
|
||||
})
|
||||
purchase_uom = fields.Many2One(
|
||||
'product.uom', "Purchase UoM",
|
||||
states={
|
||||
'invisible': ~Eval('purchasable'),
|
||||
'required': Eval('purchasable', False),
|
||||
},
|
||||
domain=[('category', '=', Eval('default_uom_category', -1))],
|
||||
help="The default Unit of Measure for purchases.")
|
||||
|
||||
@fields.depends('default_uom', 'purchase_uom', 'purchasable')
|
||||
def on_change_default_uom(self):
|
||||
try:
|
||||
super().on_change_default_uom()
|
||||
except AttributeError:
|
||||
pass
|
||||
if self.default_uom:
|
||||
if self.purchase_uom:
|
||||
if self.default_uom.category != self.purchase_uom.category:
|
||||
self.purchase_uom = self.default_uom
|
||||
else:
|
||||
self.purchase_uom = self.default_uom
|
||||
|
||||
@classmethod
|
||||
def view_attributes(cls):
|
||||
return super().view_attributes() + [
|
||||
('//page[@id="suppliers"]', 'states', {
|
||||
'invisible': ~Eval('purchasable'),
|
||||
})]
|
||||
|
||||
def product_suppliers_used(self, **pattern):
|
||||
for product_supplier in self.product_suppliers:
|
||||
if product_supplier.match(pattern):
|
||||
yield product_supplier
|
||||
|
||||
@classmethod
|
||||
def check_modification(cls, mode, templates, values=None, external=False):
|
||||
pool = Pool()
|
||||
Warning = pool.get('res.user.warning')
|
||||
|
||||
super().check_modification(
|
||||
mode, templates, values=values, external=external)
|
||||
|
||||
if mode == 'write' and values.get("purchase_uom"):
|
||||
for template in templates:
|
||||
if not template.purchase_uom:
|
||||
continue
|
||||
if template.purchase_uom.id == values["purchase_uom"]:
|
||||
continue
|
||||
for product in template.products:
|
||||
if not product.product_suppliers:
|
||||
continue
|
||||
name = '%s@product_template' % template.id
|
||||
if Warning.check(name):
|
||||
raise PurchaseUOMWarning(
|
||||
name, gettext('purchase.msg_change_purchase_uom'))
|
||||
|
||||
@classmethod
|
||||
def copy(cls, templates, default=None):
|
||||
pool = Pool()
|
||||
ProductSupplier = pool.get('purchase.product_supplier')
|
||||
if default is None:
|
||||
default = {}
|
||||
else:
|
||||
default = default.copy()
|
||||
|
||||
copy_suppliers = 'product_suppliers' not in default
|
||||
default.setdefault('product_suppliers', None)
|
||||
new_templates = super().copy(templates, default)
|
||||
if copy_suppliers:
|
||||
old2new = {}
|
||||
to_copy = []
|
||||
for template, new_template in zip(templates, new_templates):
|
||||
to_copy.extend(
|
||||
ps for ps in template.product_suppliers if not ps.product)
|
||||
old2new[template.id] = new_template.id
|
||||
if to_copy:
|
||||
ProductSupplier.copy(to_copy, {
|
||||
'template': lambda d: old2new[d['template']],
|
||||
})
|
||||
return new_templates
|
||||
|
||||
|
||||
class Product(metaclass=PoolMeta):
|
||||
__name__ = 'product.product'
|
||||
|
||||
product_suppliers = fields.One2Many(
|
||||
'purchase.product_supplier', 'product', "Suppliers",
|
||||
domain=[
|
||||
('template', '=', Eval('template', -1)),
|
||||
],
|
||||
states={
|
||||
'invisible': (~Eval('purchasable', False)
|
||||
| ~Eval('context', {}).get('company')),
|
||||
})
|
||||
purchase_price_uom = fields.Function(fields.Numeric(
|
||||
"Purchase Price", digits=price_digits), 'get_purchase_price_uom')
|
||||
last_purchase_price_uom = fields.Function(fields.Numeric(
|
||||
"Last Purchase Price", digits=price_digits),
|
||||
'get_last_purchase_price_uom')
|
||||
|
||||
@classmethod
|
||||
def get_purchase_price_uom(cls, products, name):
|
||||
quantity = Transaction().context.get('quantity') or 0
|
||||
return cls.get_purchase_price(products, quantity=quantity)
|
||||
|
||||
@classmethod
|
||||
def get_last_purchase_price_uom(cls, products, name=None):
|
||||
pool = Pool()
|
||||
Company = pool.get('company.company')
|
||||
Currency = pool.get('currency.currency')
|
||||
Date = pool.get('ir.date')
|
||||
Line = pool.get('purchase.line')
|
||||
Purchase = pool.get('purchase.purchase')
|
||||
UoM = pool.get('product.uom')
|
||||
|
||||
transaction = Transaction()
|
||||
context = transaction.context
|
||||
cursor = transaction.connection.cursor()
|
||||
purchase = Purchase.__table__()
|
||||
line = Line.__table__()
|
||||
|
||||
line_ids = []
|
||||
where = purchase.state.in_(['confirmed', 'processing', 'done'])
|
||||
supplier = context.get('supplier')
|
||||
if supplier:
|
||||
where &= purchase.party == supplier
|
||||
for sub_products in grouped_slice(products):
|
||||
query = (line
|
||||
.join(purchase, condition=line.purchase == purchase.id)
|
||||
.select(
|
||||
Max(line.id),
|
||||
where=where & reduce_ids(
|
||||
line.product, map(int, sub_products)),
|
||||
group_by=[line.product]))
|
||||
cursor.execute(*query)
|
||||
line_ids.extend(i for i, in cursor)
|
||||
lines = Line.browse(line_ids)
|
||||
|
||||
uom = None
|
||||
if context.get('uom'):
|
||||
uom = UoM(context['uom'])
|
||||
|
||||
currency = None
|
||||
if context.get('currency'):
|
||||
currency = Currency(context['currency'])
|
||||
elif context.get('company'):
|
||||
currency = Company(context['company']).currency
|
||||
date = context.get('purchase_date') or Date.today()
|
||||
|
||||
prices = defaultdict(lambda: None)
|
||||
for line in lines:
|
||||
default_uom = line.product.default_uom
|
||||
if not uom or default_uom.category != uom.category:
|
||||
product_uom = default_uom
|
||||
else:
|
||||
product_uom = uom
|
||||
unit_price = UoM.compute_price(
|
||||
line.unit, line.unit_price, product_uom)
|
||||
if currency and line.purchase.currency != currency:
|
||||
with Transaction().set_context(date=date):
|
||||
unit_price = Currency.compute(
|
||||
line.purchase.currency, unit_price, currency,
|
||||
round=False)
|
||||
prices[line.product.id] = round_price(unit_price)
|
||||
return prices
|
||||
|
||||
def product_suppliers_used(self, **pattern):
|
||||
for product_supplier in self.product_suppliers:
|
||||
if product_supplier.match(pattern):
|
||||
yield product_supplier
|
||||
pattern['product'] = None
|
||||
yield from self.template.product_suppliers_used(**pattern)
|
||||
|
||||
def _get_purchase_unit_price(self, quantity=0):
|
||||
return
|
||||
|
||||
@classmethod
|
||||
def get_purchase_price(cls, products, quantity=0):
|
||||
'''
|
||||
Return purchase price for product ids.
|
||||
The context that can have as keys:
|
||||
uom: the unit of measure
|
||||
supplier: the supplier party id
|
||||
currency: the currency id for the returned price
|
||||
'''
|
||||
pool = Pool()
|
||||
Uom = pool.get('product.uom')
|
||||
Company = pool.get('company.company')
|
||||
Currency = pool.get('currency.currency')
|
||||
Date = pool.get('ir.date')
|
||||
ProductSupplier = pool.get('purchase.product_supplier')
|
||||
ProductSupplierPrice = pool.get('purchase.product_supplier.price')
|
||||
|
||||
context = Transaction().context
|
||||
prices = {}
|
||||
|
||||
assert len(products) == len(set(products)), "Duplicate products"
|
||||
|
||||
uom = None
|
||||
if context.get('uom'):
|
||||
uom = Uom(context['uom'])
|
||||
|
||||
currency = None
|
||||
if context.get('currency'):
|
||||
currency = Currency(context['currency'])
|
||||
elif context.get('company'):
|
||||
currency = Company(context['company']).currency
|
||||
date = context.get('purchase_date') or Date.today()
|
||||
|
||||
last_purchase_prices = cls.get_last_purchase_price_uom(products)
|
||||
|
||||
for product in products:
|
||||
unit_price = product._get_purchase_unit_price(quantity=quantity)
|
||||
default_uom = product.default_uom
|
||||
default_currency = currency
|
||||
if not uom or default_uom.category != uom.category:
|
||||
product_uom = default_uom
|
||||
else:
|
||||
product_uom = uom
|
||||
pattern = ProductSupplier.get_pattern()
|
||||
product_suppliers = product.product_suppliers_used(**pattern)
|
||||
try:
|
||||
product_supplier = next(product_suppliers)
|
||||
except StopIteration:
|
||||
pass
|
||||
else:
|
||||
pattern = ProductSupplierPrice.get_pattern()
|
||||
for price in product_supplier.prices:
|
||||
if price.match(quantity, product_uom, pattern):
|
||||
unit_price = price.unit_price
|
||||
default_uom = product_supplier.unit
|
||||
default_currency = product_supplier.currency
|
||||
if unit_price is not None:
|
||||
unit_price = Uom.compute_price(
|
||||
default_uom, unit_price, product_uom)
|
||||
if currency and default_currency:
|
||||
with Transaction().set_context(date=date):
|
||||
unit_price = Currency.compute(
|
||||
default_currency, unit_price, currency,
|
||||
round=False)
|
||||
if unit_price is None:
|
||||
unit_price = last_purchase_prices[product.id]
|
||||
else:
|
||||
unit_price = round_price(unit_price)
|
||||
prices[product.id] = unit_price
|
||||
return prices
|
||||
|
||||
@classmethod
|
||||
def copy(cls, products, default=None):
|
||||
pool = Pool()
|
||||
ProductSupplier = pool.get('purchase.product_supplier')
|
||||
if default is None:
|
||||
default = {}
|
||||
else:
|
||||
default = default.copy()
|
||||
|
||||
copy_suppliers = 'product_suppliers' not in default
|
||||
if 'template' in default:
|
||||
default.setdefault('product_suppliers', None)
|
||||
new_products = super().copy(products, default)
|
||||
if 'template' in default and copy_suppliers:
|
||||
template2new = {}
|
||||
product2new = {}
|
||||
to_copy = []
|
||||
for product, new_product in zip(products, new_products):
|
||||
if product.product_suppliers:
|
||||
to_copy.extend(product.product_suppliers)
|
||||
template2new[product.template.id] = new_product.template.id
|
||||
product2new[product.id] = new_product.id
|
||||
if to_copy:
|
||||
ProductSupplier.copy(to_copy, {
|
||||
'product': lambda d: product2new[d['product']],
|
||||
'template': lambda d: template2new[d['template']],
|
||||
})
|
||||
return new_products
|
||||
|
||||
|
||||
class ProductSupplier(
|
||||
sequence_ordered(), ProductDeactivatableMixin, MatchMixin,
|
||||
ModelSQL, ModelView):
|
||||
__name__ = 'purchase.product_supplier'
|
||||
template = fields.Many2One(
|
||||
'product.template', "Product",
|
||||
required=True, ondelete='CASCADE',
|
||||
domain=[
|
||||
If(Bool(Eval('product')),
|
||||
('products', '=', Eval('product')),
|
||||
()),
|
||||
],
|
||||
states={
|
||||
'readonly': Eval('id', -1) >= 0,
|
||||
},
|
||||
context={
|
||||
'company': Eval('company', -1),
|
||||
},
|
||||
depends={'company'})
|
||||
product = fields.Many2One(
|
||||
'product.product', "Variant",
|
||||
domain=[
|
||||
If(Bool(Eval('template')),
|
||||
('template', '=', Eval('template')),
|
||||
()),
|
||||
],
|
||||
states={
|
||||
'readonly': Eval('id', -1) >= 0,
|
||||
},
|
||||
context={
|
||||
'company': Eval('company', -1),
|
||||
},
|
||||
depends={'company'})
|
||||
party = fields.Many2One(
|
||||
'party.party', 'Supplier', required=True, ondelete='CASCADE',
|
||||
states={
|
||||
'readonly': Eval('id', -1) >= 0,
|
||||
},
|
||||
context={
|
||||
'company': Eval('company', -1),
|
||||
},
|
||||
depends={'company'})
|
||||
name = fields.Char("Name", translate=True)
|
||||
code = fields.Char("Code")
|
||||
prices = fields.One2Many(
|
||||
'purchase.product_supplier.price', 'product_supplier', "Prices",
|
||||
help="Add price for different criteria.\n"
|
||||
"The last matching line is used.")
|
||||
company = fields.Many2One(
|
||||
'company.company', "Company",
|
||||
required=True, ondelete='CASCADE')
|
||||
lead_time = fields.TimeDelta(
|
||||
"Lead Time",
|
||||
domain=['OR',
|
||||
('lead_time', '=', None),
|
||||
('lead_time', '>=', TimeDelta()),
|
||||
],
|
||||
help="The time from confirming the purchase order to receiving the "
|
||||
"products.\n"
|
||||
"If empty the lead time of the supplier is used.")
|
||||
currency = fields.Many2One('currency.currency', 'Currency', required=True,
|
||||
ondelete='RESTRICT')
|
||||
unit = fields.Function(
|
||||
fields.Many2One('product.uom', "Unit"), 'on_change_with_unit')
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
cls.code.search_unaccented = False
|
||||
super().__setup__()
|
||||
t = cls.__table__()
|
||||
cls._sql_indexes.update({
|
||||
Index(t, (t.code, Index.Similarity())),
|
||||
})
|
||||
|
||||
@staticmethod
|
||||
def default_company():
|
||||
return Transaction().context.get('company')
|
||||
|
||||
@staticmethod
|
||||
def default_currency():
|
||||
Company = Pool().get('company.company')
|
||||
if Transaction().context.get('company'):
|
||||
company = Company(Transaction().context['company'])
|
||||
return company.currency.id
|
||||
|
||||
@fields.depends(
|
||||
'product', '_parent_product.template')
|
||||
def on_change_product(self):
|
||||
if self.product:
|
||||
self.template = self.product.template
|
||||
|
||||
@fields.depends('party')
|
||||
def on_change_party(self):
|
||||
cursor = Transaction().connection.cursor()
|
||||
self.currency = self.default_currency()
|
||||
if self.party:
|
||||
table = self.__table__()
|
||||
cursor.execute(*table.select(table.currency,
|
||||
where=table.party == self.party.id,
|
||||
group_by=table.currency,
|
||||
order_by=Count(Literal(1)).desc))
|
||||
row = cursor.fetchone()
|
||||
if row:
|
||||
self.currency, = row
|
||||
|
||||
def get_rec_name(self, name):
|
||||
if not self.name and not self.code:
|
||||
if self.product:
|
||||
name = self.product.rec_name
|
||||
else:
|
||||
name = self.template.rec_name
|
||||
else:
|
||||
if self.name:
|
||||
name = self.name
|
||||
elif self.product:
|
||||
name = self.product.name
|
||||
else:
|
||||
name = self.template.name
|
||||
if self.code:
|
||||
name = '[' + self.code + ']' + name
|
||||
return name
|
||||
|
||||
@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'
|
||||
code_value = operand
|
||||
if operator.endswith('like') and is_full_text(operand):
|
||||
code_value = lstrip_wildcard(operand)
|
||||
domain = [bool_op,
|
||||
('template.rec_name', operator, operand, *extra),
|
||||
('product.rec_name', operator, operand, *extra),
|
||||
('party.rec_name', operator, operand, *extra),
|
||||
('code', operator, code_value, *extra),
|
||||
('name', operator, operand, *extra),
|
||||
]
|
||||
return domain
|
||||
|
||||
@fields.depends(
|
||||
'product', '_parent_product.purchase_uom',
|
||||
'template', '_parent_template.purchase_uom')
|
||||
def on_change_with_unit(self, name=None):
|
||||
if self.product:
|
||||
return self.product.purchase_uom
|
||||
elif self.template:
|
||||
return self.template.purchase_uom
|
||||
|
||||
@property
|
||||
def lead_time_used(self):
|
||||
# Use getattr because it can be called with an unsaved instance
|
||||
lead_time = getattr(self, 'lead_time', None)
|
||||
party = getattr(self, 'party', None)
|
||||
if lead_time is None and party:
|
||||
company = getattr(self, 'company', None)
|
||||
lead_time = party.get_multivalue(
|
||||
'supplier_lead_time', company=company.id if company else None)
|
||||
return lead_time
|
||||
|
||||
def compute_supply_date(self, date=None):
|
||||
'''
|
||||
Compute the supply date for the Product Supplier at the given date
|
||||
'''
|
||||
Date = Pool().get('ir.date')
|
||||
|
||||
if not date:
|
||||
with Transaction().set_context(company=self.company.id):
|
||||
date = Date.today()
|
||||
if self.lead_time_used is None:
|
||||
return datetime.date.max
|
||||
return date + self.lead_time_used
|
||||
|
||||
def compute_purchase_date(self, date):
|
||||
'''
|
||||
Compute the purchase date for the Product Supplier at the given date
|
||||
'''
|
||||
Date = Pool().get('ir.date')
|
||||
|
||||
if self.lead_time_used is None or date is None:
|
||||
with Transaction().set_context(company=self.company.id):
|
||||
return Date.today()
|
||||
return date - self.lead_time_used
|
||||
|
||||
@staticmethod
|
||||
def get_pattern():
|
||||
context = Transaction().context
|
||||
pattern = {'party': context.get('supplier')}
|
||||
if 'product_supplier' in context:
|
||||
pattern['id'] = context['product_supplier']
|
||||
return pattern
|
||||
|
||||
|
||||
class ProductSupplierPrice(
|
||||
sequence_ordered(), ModelSQL, ModelView, MatchMixin):
|
||||
__name__ = 'purchase.product_supplier.price'
|
||||
product_supplier = fields.Many2One('purchase.product_supplier',
|
||||
'Supplier', required=True, ondelete='CASCADE')
|
||||
quantity = fields.Float(
|
||||
"Quantity",
|
||||
required=True,
|
||||
domain=[('quantity', '>=', 0)],
|
||||
help='Minimal quantity.')
|
||||
unit_price = Monetary(
|
||||
"Unit Price", currency='currency', required=True, digits=price_digits)
|
||||
|
||||
unit = fields.Function(fields.Many2One(
|
||||
'product.uom', "Unit",
|
||||
help="The unit in which the quantity is specified."),
|
||||
'on_change_with_unit')
|
||||
currency = fields.Function(
|
||||
fields.Many2One('currency.currency', 'Currency'),
|
||||
'on_change_with_currency')
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super().__setup__()
|
||||
cls.__access__.add('product_supplier')
|
||||
|
||||
@staticmethod
|
||||
def default_quantity():
|
||||
return 0.0
|
||||
|
||||
@fields.depends('product_supplier', '_parent_product_supplier.unit')
|
||||
def on_change_with_unit(self, name=None):
|
||||
return self.product_supplier.unit if self.product_supplier else None
|
||||
|
||||
@fields.depends('product_supplier', '_parent_product_supplier.currency')
|
||||
def on_change_with_currency(self, name=None):
|
||||
if self.product_supplier:
|
||||
return self.product_supplier.currency
|
||||
|
||||
@staticmethod
|
||||
def get_pattern():
|
||||
return {}
|
||||
|
||||
def match(self, quantity, unit, pattern):
|
||||
pool = Pool()
|
||||
Uom = pool.get('product.uom')
|
||||
test_quantity = Uom.compute_qty(
|
||||
self.product_supplier.unit, self.quantity, unit)
|
||||
if test_quantity > abs(quantity):
|
||||
return False
|
||||
return super().match(pattern)
|
||||
24
modules/purchase/product.xml
Normal file
24
modules/purchase/product.xml
Normal file
@@ -0,0 +1,24 @@
|
||||
<?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="template_view_form">
|
||||
<field name="model">product.template</field>
|
||||
<field name="inherit" ref="product.template_view_form"/>
|
||||
<field name="name">template_form</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="template_view_tree">
|
||||
<field name="model">product.template</field>
|
||||
<field name="inherit" ref="product.template_view_tree"/>
|
||||
<field name="name">template_tree</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="product_view_list_purchase_line">
|
||||
<field name="model">product.product</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="priority" eval="20"/>
|
||||
<field name="name">product_list_purchase_line</field>
|
||||
</record>
|
||||
</data>
|
||||
</tryton>
|
||||
954
modules/purchase/purchase.fodt
Normal file
954
modules/purchase/purchase.fodt
Normal file
@@ -0,0 +1,954 @@
|
||||
<?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:28:22</meta:creation-date><dc:date>2009-01-10T16:03:32</dc:date><meta:editing-cycles>1</meta:editing-cycles><meta:editing-duration>PT0S</meta:editing-duration><meta:document-statistic meta:table-count="3" meta:image-count="0" meta:object-count="0" meta:page-count="8" meta:paragraph-count="101" meta:word-count="236" meta:character-count="2840" meta:non-whitespace-character-count="2704"/><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">5660</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">true</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">947783</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">4032911</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="'Andale Sans UI'" style:font-family-generic="system" style:font-pitch="variable"/>
|
||||
<style:font-face style:name="DejaVu Sans" svg:font-family="'DejaVu Sans'" style:font-family-generic="system" style:font-pitch="variable"/>
|
||||
<style:font-face style:name="Liberation Sans" svg:font-family="'Liberation Sans'" style:font-adornments="Regular" style:font-family-generic="swiss" style:font-pitch="variable"/>
|
||||
<style:font-face style:name="Liberation Serif" svg:font-family="'Liberation Serif'" style:font-family-generic="roman" style:font-pitch="variable"/>
|
||||
<style:font-face style:name="Liberation Serif1" svg:font-family="'Liberation Serif'" style:font-adornments="Bold" style:font-family-generic="roman" style:font-pitch="variable"/>
|
||||
<style:font-face style:name="Liberation Serif2" svg:font-family="'Liberation Serif'" 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="'Thorndale AMT'" 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="'Liberation Sans'" 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="'Liberation Serif'" 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="'DejaVu Sans'" 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="'DejaVu Sans'" 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="'Liberation Sans'" 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="'Liberation Serif'" 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_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="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="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: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" 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="17.588cm" table:align="left"/>
|
||||
</style:style>
|
||||
<style:style style:name="Table1.A" style:family="table-column">
|
||||
<style:table-column-properties style:column-width="6.962cm"/>
|
||||
</style:style>
|
||||
<style:style style:name="Table1.B" style:family="table-column">
|
||||
<style:table-column-properties style:column-width="2.656cm"/>
|
||||
</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.E1" 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="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="Table1.A5" 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.B5" 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.C5" 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.D5" 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.E5" 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.A6" 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.A7" 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.A8" 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.E8" 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.A9" 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.A10" 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.A11" style:family="table-cell">
|
||||
<style:table-cell-properties fo:background-color="#e6e6e6" 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:background-image/>
|
||||
</style:table-cell-properties>
|
||||
</style:style>
|
||||
<style:style style:name="Table1.A12" 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.A13" 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.A14" 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.A15" 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.A16" 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.A17" 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="Table2" style:family="table">
|
||||
<style:table-properties style:width="17.59cm" table:align="margins" style:may-break-between-rows="false"/>
|
||||
</style:style>
|
||||
<style:style style:name="Table2.A" style:family="table-column">
|
||||
<style:table-column-properties style:column-width="8.795cm" style:rel-column-width="32767*"/>
|
||||
</style:style>
|
||||
<style:style style:name="Table2.B" style:family="table-column">
|
||||
<style:table-column-properties style:column-width="8.795cm" style:rel-column-width="32768*"/>
|
||||
</style:style>
|
||||
<style:style style:name="Table4" style:family="table">
|
||||
<style:table-properties style:width="7.969cm" table:align="right" style:may-break-between-rows="true"/>
|
||||
</style:style>
|
||||
<style:style style:name="Table4.A" style:family="table-column">
|
||||
<style:table-column-properties style:column-width="5.308cm"/>
|
||||
</style:style>
|
||||
<style:style style:name="Table4.B" style:family="table-column">
|
||||
<style:table-column-properties style:column-width="2.662cm"/>
|
||||
</style:style>
|
||||
<style:style style:name="Table4.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="Table4.B1" style:family="table-cell">
|
||||
<style:table-cell-properties fo:padding="0.097cm" fo:border="0.05pt solid #000000"/>
|
||||
</style:style>
|
||||
<style:style style:name="Table4.A2" 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="none" fo:border-bottom="0.05pt solid #000000">
|
||||
<style:background-image/>
|
||||
</style:table-cell-properties>
|
||||
</style:style>
|
||||
<style:style style:name="Table4.B2" 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="Table4.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="Table4" style:family="table">
|
||||
<style:table-properties style:width="7.969cm" table:align="right" style:may-break-between-rows="true"/>
|
||||
</style:style>
|
||||
<style:style style:name="Table4.A" style:family="table-column">
|
||||
<style:table-column-properties style:column-width="5.308cm"/>
|
||||
</style:style>
|
||||
<style:style style:name="Table4.B" style:family="table-column">
|
||||
<style:table-column-properties style:column-width="2.662cm"/>
|
||||
</style:style>
|
||||
<style:style style:name="Table4.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="Table4.B1" style:family="table-cell">
|
||||
<style:table-cell-properties fo:padding="0.097cm" fo:border="0.05pt solid #000000"/>
|
||||
</style:style>
|
||||
<style:style style:name="Table4.A2" 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="none" fo:border-bottom="0.05pt solid #000000">
|
||||
<style:background-image/>
|
||||
</style:table-cell-properties>
|
||||
</style:style>
|
||||
<style:style style:name="Table4.B2" 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="Table4.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="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="0011dc8a"/>
|
||||
</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="0011dc8a"/>
|
||||
</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="003d898f" 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="Header">
|
||||
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
|
||||
<style:text-properties officeooo:paragraph-rsid="0011dc8a"/>
|
||||
</style:style>
|
||||
<style:style style:name="P6" 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="0011dc8a"/>
|
||||
</style:style>
|
||||
<style:style style:name="P7" style:family="paragraph" style:parent-style-name="Table_20_Contents">
|
||||
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
|
||||
</style:style>
|
||||
<style:style style:name="P8" 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="003c73bf" style:font-size-asian="12pt" style:font-size-complex="12pt"/>
|
||||
</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="0011dc8a" style:font-size-asian="12pt" style:font-size-complex="12pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="P10" 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="P11" style:family="paragraph" style:parent-style-name="Text_20_body">
|
||||
<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="Heading_20_2">
|
||||
<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="Standard">
|
||||
<style:paragraph-properties fo:margin-left="11.28cm" fo:margin-right="0cm" fo:text-indent="0cm" style:auto-text-indent="false"/>
|
||||
</style:style>
|
||||
<style:style style:name="P14" 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:style>
|
||||
<style:style style:name="P15" 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 officeooo:paragraph-rsid="002b1070"/>
|
||||
</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.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 officeooo:paragraph-rsid="002b1070"/>
|
||||
</style:style>
|
||||
<style:style style:name="P17" style:family="paragraph" style:parent-style-name="Table_20_Heading">
|
||||
<style:paragraph-properties fo:text-align="end" style:justify-single-word="false"/>
|
||||
</style:style>
|
||||
<style:style style:name="P18" 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="P19" 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 officeooo:paragraph-rsid="00153e37"/>
|
||||
</style:style>
|
||||
<style:style style:name="P20" style:family="paragraph" style:parent-style-name="Text_20_body">
|
||||
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false"/>
|
||||
<style:text-properties style:text-underline-style="none"/>
|
||||
</style:style>
|
||||
<style:style style:name="P21" style:family="paragraph" style:parent-style-name="Text_20_body">
|
||||
<style:text-properties officeooo:rsid="0013c923" officeooo:paragraph-rsid="0013c923"/>
|
||||
</style:style>
|
||||
<style:style style:name="P22" style:family="paragraph" style:parent-style-name="Text_20_body" style:master-page-name="">
|
||||
<style:paragraph-properties style:page-number="auto" fo:keep-with-next="always"/>
|
||||
<style:text-properties officeooo:rsid="0013c923" officeooo:paragraph-rsid="0013c923"/>
|
||||
</style:style>
|
||||
<style:style style:name="P23" style:family="paragraph" style:parent-style-name="Text_20_body">
|
||||
<style:text-properties officeooo:paragraph-rsid="003adf68"/>
|
||||
</style:style>
|
||||
<style:style style:name="P24" style:family="paragraph" style:parent-style-name="Standard" style:master-page-name="">
|
||||
<style:paragraph-properties style:page-number="auto" fo:break-before="auto" fo:break-after="auto"/>
|
||||
</style:style>
|
||||
<style:style style:name="P25" style:family="paragraph" style:parent-style-name="Text_20_body" style:master-page-name="">
|
||||
<style:paragraph-properties style:page-number="auto" fo:keep-with-next="always"/>
|
||||
</style:style>
|
||||
<style:style style:name="P26" style:family="paragraph" style:parent-style-name="Standard">
|
||||
<style:paragraph-properties fo:margin-left="0cm" fo:margin-right="0cm" fo:text-indent="0cm" style:auto-text-indent="false"/>
|
||||
<style:text-properties style:font-name="Liberation Serif"/>
|
||||
</style:style>
|
||||
<style:style style:name="P27" style:family="paragraph" style:parent-style-name="Standard">
|
||||
<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:text-properties style:font-name="Liberation Serif"/>
|
||||
</style:style>
|
||||
<style:style style:name="P28" style:family="paragraph" style:parent-style-name="Standard">
|
||||
<style:paragraph-properties fo:margin-left="0cm" fo:margin-right="0cm" fo:text-indent="0cm" style:auto-text-indent="false"/>
|
||||
<style:text-properties style:font-name="Liberation Serif" officeooo:paragraph-rsid="002cc9ef"/>
|
||||
</style:style>
|
||||
<style:style style:name="P29" style:family="paragraph" style:parent-style-name="Standard">
|
||||
<style:paragraph-properties fo:margin-left="0cm" fo:margin-right="0cm" fo:text-indent="0cm" style:auto-text-indent="false"/>
|
||||
<style:text-properties style:font-name="Liberation Serif" officeooo:paragraph-rsid="002df6ba"/>
|
||||
</style:style>
|
||||
<style:style style:name="P30" 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="P31" style:family="paragraph" style:parent-style-name="Text_20_body">
|
||||
<loext:graphic-properties draw:fill="none"/>
|
||||
<style:paragraph-properties fo:margin-left="1cm" fo:margin-right="0cm" fo:margin-top="0cm" fo:margin-bottom="0.212cm" style:contextual-spacing="false" fo:text-indent="0cm" style:auto-text-indent="false" fo:background-color="transparent"/>
|
||||
<style:text-properties officeooo:rsid="0019f6b5" officeooo:paragraph-rsid="0019f6b5"/>
|
||||
</style:style>
|
||||
<style:style style:name="P32" style:family="paragraph" style:parent-style-name="Text_20_body">
|
||||
<style:paragraph-properties fo:break-before="column"/>
|
||||
<style:text-properties officeooo:rsid="001bf6f1" officeooo:paragraph-rsid="0024fff1"/>
|
||||
</style:style>
|
||||
<style:style style:name="P33" 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 fo:font-size="6pt" officeooo:paragraph-rsid="002b1070" style:font-size-asian="5.25pt" style:font-size-complex="6pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="P34" 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="P35" 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="003d898f" style:font-size-asian="12pt" style:font-size-complex="12pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="T1" style:family="text">
|
||||
<style:text-properties officeooo:rsid="0029e76e"/>
|
||||
</style:style>
|
||||
<style:style style:name="T2" style:family="text">
|
||||
<style:text-properties officeooo:rsid="00381553"/>
|
||||
</style:style>
|
||||
<style:style style:name="T3" style:family="text">
|
||||
<style:text-properties officeooo:rsid="0039ae4c"/>
|
||||
</style:style>
|
||||
<style:style style:name="T4" style:family="text">
|
||||
<style:text-properties officeooo:rsid="003adf68"/>
|
||||
</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:style style:name="Sect1" style:family="section">
|
||||
<style:section-properties text:dont-balance-text-columns="true" style:editable="false">
|
||||
<style:columns fo:column-count="2" fo:column-gap="0cm">
|
||||
<style:column style:rel-width="32767*" fo:start-indent="0cm" fo:end-indent="0cm"/>
|
||||
<style:column style:rel-width="32768*" fo:start-indent="0cm" fo:end-indent="0cm"/>
|
||||
</style:columns>
|
||||
</style:section-properties>
|
||||
</style:style>
|
||||
<style:page-layout style:name="pm1">
|
||||
<style:page-layout-properties fo:page-width="21.59cm" fo:page-height="27.94cm" 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"><if test="company and company.header"></text:placeholder></text:p>
|
||||
<text:p text:style-name="P1"><text:placeholder text:placeholder-type="text"><for each="line in company.header_used.split('\n')"></text:placeholder></text:p>
|
||||
<text:p text:style-name="P1"><text:placeholder text:placeholder-type="text"><line></text:placeholder></text:p>
|
||||
<text:p text:style-name="P1"><text:placeholder text:placeholder-type="text"></for></text:placeholder></text:p>
|
||||
<text:p text:style-name="P2"><text:placeholder text:placeholder-type="text"></if></text:placeholder></text:p>
|
||||
<text:p text:style-name="P3"><text:placeholder text:placeholder-type="text"><choose></text:placeholder></text:p>
|
||||
<text:p text:style-name="P3"><text:placeholder text:placeholder-type="text"><when test="company and company.logo"></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="7">
|
||||
<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"></when></text:placeholder></text:p>
|
||||
<text:p text:style-name="P3"><text:placeholder text:placeholder-type="text"><when test="company"></text:placeholder></text:p>
|
||||
<text:p text:style-name="P3"><text:placeholder text:placeholder-type="text"><company.rec_name></text:placeholder></text:p>
|
||||
<text:p text:style-name="P3"><text:placeholder text:placeholder-type="text"></when></text:placeholder></text:p>
|
||||
<text:p text:style-name="P3"><text:placeholder text:placeholder-type="text"></choose></text:placeholder></text:p>
|
||||
</style:header>
|
||||
<style:footer>
|
||||
<text:p text:style-name="P2"><text:placeholder text:placeholder-type="text"><if test="company and company.footer"></text:placeholder></text:p>
|
||||
<text:p text:style-name="P2"><text:placeholder text:placeholder-type="text"><for each="line in company.footer_used.split('\n')"></text:placeholder></text:p>
|
||||
<text:p text:style-name="P2"><text:placeholder text:placeholder-type="text"><line></text:placeholder></text:p>
|
||||
<text:p text:style-name="P2"><text:placeholder text:placeholder-type="text"></for></text:placeholder></text:p>
|
||||
<text:p text:style-name="P2"><text:placeholder text:placeholder-type="text"></if></text:placeholder></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="P24"><text:placeholder text:placeholder-type="text"><for each="purchase in records"></text:placeholder></text:p>
|
||||
<text:p text:style-name="P33"/>
|
||||
<text:p text:style-name="P15"><text:placeholder text:placeholder-type="text"><replace text:p="set_lang(purchase.party.lang)"></text:placeholder></text:p>
|
||||
<text:p text:style-name="P15"><text:placeholder text:placeholder-type="text"><replace text:p="purchase.set_lang(purchase.party.lang)"></text:placeholder></text:p>
|
||||
<text:p text:style-name="P13"><text:placeholder text:placeholder-type="text"><for each="line in purchase.report_address.splitlines()"></text:placeholder></text:p>
|
||||
<text:p text:style-name="P13"><text:placeholder text:placeholder-type="text"><line></text:placeholder></text:p>
|
||||
<text:p text:style-name="P13"><text:placeholder text:placeholder-type="text"></for></text:placeholder></text:p>
|
||||
<text:p text:style-name="P13"><text:placeholder text:placeholder-type="text"><if test="purchase.party.tax_identifier"></text:placeholder></text:p>
|
||||
<text:p text:style-name="P13"><text:placeholder text:placeholder-type="text"><purchase.party.tax_identifier.type_string></text:placeholder>: <text:placeholder text:placeholder-type="text"><purchase.party.tax_identifier.code></text:placeholder></text:p>
|
||||
<text:p text:style-name="P13"><text:placeholder text:placeholder-type="text"></if></text:placeholder></text:p>
|
||||
<text:p text:style-name="P11"><text:placeholder text:placeholder-type="text"><choose test=""></text:placeholder></text:p>
|
||||
<text:p text:style-name="P20"><text:placeholder text:placeholder-type="text"><when test="purchase.state == 'draft'"></text:placeholder></text:p>
|
||||
<text:p text:style-name="P30">Draft Purchase Order</text:p>
|
||||
<text:p text:style-name="P20"><text:placeholder text:placeholder-type="text"></when></text:placeholder></text:p>
|
||||
<text:p text:style-name="P20"><text:soft-page-break/><text:placeholder text:placeholder-type="text"><when test="purchase.state == 'quotation'"></text:placeholder></text:p>
|
||||
<text:p text:style-name="P30">Request for Quotation No: <text:placeholder text:placeholder-type="text"><purchase.full_number></text:placeholder></text:p>
|
||||
<text:p text:style-name="P20"><text:placeholder text:placeholder-type="text"></when></text:placeholder></text:p>
|
||||
<text:p text:style-name="P20"><text:placeholder text:placeholder-type="text"><otherwise test=""></text:placeholder></text:p>
|
||||
<text:p text:style-name="P30">Purchase Order No: <text:placeholder text:placeholder-type="text"><purchase.full_number></text:placeholder></text:p>
|
||||
<text:p text:style-name="P20"><text:placeholder text:placeholder-type="text"></otherwise></text:placeholder></text:p>
|
||||
<text:p text:style-name="P20"><text:placeholder text:placeholder-type="text"></choose></text:placeholder></text:p>
|
||||
<text:section text:style-name="Sect1" text:name="Section1">
|
||||
<text:p text:style-name="P25">Description: <text:placeholder text:placeholder-type="text"><purchase.description or ''></text:placeholder></text:p>
|
||||
<text:p text:style-name="P22">Reference: <text:placeholder text:placeholder-type="text"><purchase.reference or ''></text:placeholder></text:p>
|
||||
<text:p text:style-name="P23">Customer Code: <text:placeholder text:placeholder-type="text"><purchase.party.get_multivalue('customer_code', company=purchase.company.id) or ''></text:placeholder></text:p>
|
||||
<text:p text:style-name="Text_20_body">Date: <text:placeholder text:placeholder-type="text"><format_date(purchase.purchase_date or today, purchase.party.lang)></text:placeholder></text:p>
|
||||
<text:p text:style-name="P32">Delivery Address:</text:p>
|
||||
<text:p text:style-name="P31"><text:placeholder text:placeholder-type="text"><for each="line in purchase.delivery_full_address.splitlines()"></text:placeholder></text:p>
|
||||
<text:p text:style-name="P31"><text:placeholder text:placeholder-type="text"><line></text:placeholder></text:p>
|
||||
<text:p text:style-name="P31"><text:placeholder text:placeholder-type="text"></for></text:placeholder></text:p>
|
||||
</text:section>
|
||||
<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:number-columns-repeated="4"/>
|
||||
<table:table-header-rows>
|
||||
<table:table-row>
|
||||
<table:table-cell table:style-name="Table1.A1" office:value-type="string">
|
||||
<text:p text:style-name="Table_20_Heading">Description</text:p>
|
||||
</table:table-cell>
|
||||
<table:table-cell table:style-name="Table1.A1" office:value-type="string">
|
||||
<text:p text:style-name="Table_20_Heading">Quantity</text:p>
|
||||
</table:table-cell>
|
||||
<table:table-cell table:style-name="Table1.A1" office:value-type="string">
|
||||
<text:p text:style-name="Table_20_Heading">Unit Price</text:p>
|
||||
</table:table-cell>
|
||||
<table:table-cell table:style-name="Table1.A1" office:value-type="string">
|
||||
<text:p text:style-name="Table_20_Heading">Taxes</text:p>
|
||||
</table:table-cell>
|
||||
<table:table-cell table:style-name="Table1.E1" office:value-type="string">
|
||||
<text:p text:style-name="Table_20_Heading">Amount</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="5" office:value-type="string">
|
||||
<text:p text:style-name="Table_20_Contents"><text:placeholder text:placeholder-type="text"><for each="line in purchase.lines"></text:placeholder></text:p>
|
||||
</table:table-cell>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
</table:table-row>
|
||||
<table:table-row>
|
||||
<table:table-cell table:style-name="Table1.A2" table:number-columns-spanned="5" office:value-type="string">
|
||||
<text:p text:style-name="Table_20_Contents"><text:placeholder text:placeholder-type="text"><choose test=""></text:placeholder></text:p>
|
||||
</table:table-cell>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
</table:table-row>
|
||||
<text:soft-page-break/>
|
||||
<table:table-row>
|
||||
<table:table-cell table:style-name="Table1.A2" table:number-columns-spanned="5" office:value-type="string">
|
||||
<text:p text:style-name="Table_20_Contents"><text:placeholder text:placeholder-type="text"><when test="line.type == 'line'"></text:placeholder></text:p>
|
||||
</table:table-cell>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
</table:table-row>
|
||||
<table:table-row>
|
||||
<table:table-cell table:style-name="Table1.A5" office:value-type="string">
|
||||
<text:p text:style-name="P28"><text:placeholder text:placeholder-type="text"><if test="line.product_name"></text:placeholder></text:p>
|
||||
<text:p text:style-name="P28"><text:placeholder text:placeholder-type="text"><line.product_name></text:placeholder></text:p>
|
||||
<text:p text:style-name="P28"><text:placeholder text:placeholder-type="text"></if></text:placeholder></text:p>
|
||||
<text:p text:style-name="P28"><text:placeholder text:placeholder-type="text"><if test="line.description"></text:placeholder></text:p>
|
||||
<text:p text:style-name="P28"><text:placeholder text:placeholder-type="text"><for each="description in line.description.split('\n')"></text:placeholder></text:p>
|
||||
<text:p text:style-name="P28"><text:placeholder text:placeholder-type="text"><description></text:placeholder></text:p>
|
||||
<text:p text:style-name="P28"><text:placeholder text:placeholder-type="text"></for></text:placeholder></text:p>
|
||||
<text:p text:style-name="P28"><text:placeholder text:placeholder-type="text"></if></text:placeholder></text:p>
|
||||
</table:table-cell>
|
||||
<table:table-cell table:style-name="Table1.B5" office:value-type="string">
|
||||
<text:p text:style-name="P18"><text:placeholder text:placeholder-type="text"><format_number_symbol(line.quantity, purchase.party.lang, line.unit, digits=line.unit.digits) if line.unit else format_number(line.quantity, purchase.party.lang)></text:placeholder></text:p>
|
||||
</table:table-cell>
|
||||
<table:table-cell table:style-name="Table1.B5" office:value-type="string">
|
||||
<text:p text:style-name="P19"><text:placeholder text:placeholder-type="text"><if test="line.unit_price is not None"></text:placeholder></text:p>
|
||||
<text:p text:style-name="P19"><text:placeholder text:placeholder-type="text"><format_currency(line.unit_price, purchase.party.lang, purchase.currency, digits=line.__class__.unit_price.digits[1])></text:placeholder></text:p>
|
||||
<text:p text:style-name="P19"><text:placeholder text:placeholder-type="text"></if></text:placeholder></text:p>
|
||||
</table:table-cell>
|
||||
<table:table-cell table:style-name="Table1.B5" office:value-type="string">
|
||||
<text:p text:style-name="P7"><text:placeholder text:placeholder-type="text"><for each="tax in line.taxes"></text:placeholder></text:p>
|
||||
<text:p text:style-name="P7"><text:placeholder text:placeholder-type="text"><tax.description></text:placeholder></text:p>
|
||||
<text:p text:style-name="P7"><text:placeholder text:placeholder-type="text"></for></text:placeholder></text:p>
|
||||
</table:table-cell>
|
||||
<table:table-cell table:style-name="Table1.E5" office:value-type="string">
|
||||
<text:p text:style-name="P18"><text:placeholder text:placeholder-type="text"><if test="line.amount is not None"></text:placeholder></text:p>
|
||||
<text:p text:style-name="P18"><text:placeholder text:placeholder-type="text"><format_currency(line.amount, purchase.party.lang, purchase.currency)></text:placeholder></text:p>
|
||||
<text:p text:style-name="P18"><text:placeholder text:placeholder-type="text"></if></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="5" office:value-type="string">
|
||||
<text:p text:style-name="Table_20_Contents"><text:placeholder text:placeholder-type="text"></when></text:placeholder></text:p>
|
||||
</table:table-cell>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
</table:table-row>
|
||||
<table:table-row>
|
||||
<table:table-cell table:style-name="Table1.A2" table:number-columns-spanned="5" office:value-type="string">
|
||||
<text:p text:style-name="Table_20_Contents"><text:placeholder text:placeholder-type="text"><when test="line.type == 'subtotal'"></text:placeholder></text:p>
|
||||
</table:table-cell>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
</table:table-row>
|
||||
<table:table-row>
|
||||
<table:table-cell table:style-name="Table1.A8" table:number-columns-spanned="4" office:value-type="string">
|
||||
<text:p text:style-name="Heading_20_2"><text:placeholder text:placeholder-type="text"><for each="description in (line.description or '').split('\n')"></text:placeholder></text:p>
|
||||
<text:p text:style-name="Heading_20_2"><text:soft-page-break/><text:placeholder text:placeholder-type="text"><description></text:placeholder></text:p>
|
||||
<text:p text:style-name="P12"><text:placeholder text:placeholder-type="text"></for></text:placeholder></text:p>
|
||||
</table:table-cell>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
<table:table-cell table:style-name="Table1.E5" office:value-type="string">
|
||||
<text:p text:style-name="P18"><text:placeholder text:placeholder-type="text"><format_currency(line.amount, purchase.party.lang, purchase.currency)></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="5" office:value-type="string">
|
||||
<text:p text:style-name="Table_20_Contents"><text:placeholder text:placeholder-type="text"></when></text:placeholder></text:p>
|
||||
</table:table-cell>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
</table:table-row>
|
||||
<table:table-row>
|
||||
<table:table-cell table:style-name="Table1.A2" table:number-columns-spanned="5" office:value-type="string">
|
||||
<text:p text:style-name="Table_20_Contents"><text:placeholder text:placeholder-type="text"><when test="line.type == 'title'"></text:placeholder></text:p>
|
||||
</table:table-cell>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
</table:table-row>
|
||||
<table:table-row>
|
||||
<table:table-cell table:style-name="Table1.A11" table:number-columns-spanned="5" office:value-type="string">
|
||||
<text:p text:style-name="Heading_20_2"><text:placeholder text:placeholder-type="text"><for each="description in (line.description or '').split('\n')"></text:placeholder></text:p>
|
||||
<text:p text:style-name="Heading_20_2"><text:placeholder text:placeholder-type="text"><description></text:placeholder></text:p>
|
||||
<text:p text:style-name="Heading_20_2"><text:placeholder text:placeholder-type="text"></for></text:placeholder></text:p>
|
||||
</table:table-cell>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
</table:table-row>
|
||||
<table:table-row>
|
||||
<table:table-cell table:style-name="Table1.A2" table:number-columns-spanned="5" office:value-type="string">
|
||||
<text:p text:style-name="Table_20_Contents"><text:placeholder text:placeholder-type="text"></when></text:placeholder></text:p>
|
||||
</table:table-cell>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
</table:table-row>
|
||||
<table:table-row>
|
||||
<table:table-cell table:style-name="Table1.A2" table:number-columns-spanned="5" office:value-type="string">
|
||||
<text:p text:style-name="Table_20_Contents"><text:placeholder text:placeholder-type="text"><otherwise test=""></text:placeholder></text:p>
|
||||
</table:table-cell>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
</table:table-row>
|
||||
<table:table-row>
|
||||
<table:table-cell table:style-name="Table1.A2" table:number-columns-spanned="5" office:value-type="string">
|
||||
<text:p text:style-name="P26"><text:placeholder text:placeholder-type="text"><for each="description in (line.description or '').split('\n')"></text:placeholder></text:p>
|
||||
<text:p text:style-name="P26"><text:placeholder text:placeholder-type="text"><description></text:placeholder></text:p>
|
||||
<text:p text:style-name="P27"><text:placeholder text:placeholder-type="text"></for></text:placeholder></text:p>
|
||||
</table:table-cell>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
</table:table-row>
|
||||
<table:table-row>
|
||||
<table:table-cell table:style-name="Table1.A2" table:number-columns-spanned="5" office:value-type="string">
|
||||
<text:p text:style-name="Table_20_Contents"><text:placeholder text:placeholder-type="text"></otherwise></text:placeholder></text:p>
|
||||
</table:table-cell>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
</table:table-row>
|
||||
<table:table-row>
|
||||
<table:table-cell table:style-name="Table1.A2" table:number-columns-spanned="5" office:value-type="string">
|
||||
<text:p text:style-name="Table_20_Contents"><text:placeholder text:placeholder-type="text"></choose></text:placeholder></text:p>
|
||||
</table:table-cell>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
</table:table-row>
|
||||
<text:soft-page-break/>
|
||||
<table:table-row>
|
||||
<table:table-cell table:style-name="Table1.A2" table:number-columns-spanned="5" office:value-type="string">
|
||||
<text:p text:style-name="Table_20_Contents"><text:placeholder text:placeholder-type="text"></for></text:placeholder></text:p>
|
||||
</table:table-cell>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
<table:covered-table-cell/>
|
||||
</table:table-row>
|
||||
</table:table>
|
||||
<text:p text:style-name="Text_20_body"/>
|
||||
<table:table table:name="Table2" table:style-name="Table2">
|
||||
<table:table-column table:style-name="Table2.A"/>
|
||||
<table:table-column table:style-name="Table2.B"/>
|
||||
<text:soft-page-break/>
|
||||
<table:table-row>
|
||||
<table:table-cell office:value-type="string">
|
||||
<text:p text:style-name="Table_20_Contents"/>
|
||||
</table:table-cell>
|
||||
<table:table-cell office:value-type="string">
|
||||
<table:table table:name="Table4" table:style-name="Table4">
|
||||
<table:table-column table:style-name="Table4.A"/>
|
||||
<table:table-column table:style-name="Table4.B"/>
|
||||
<table:table-row>
|
||||
<table:table-cell table:style-name="Table4.A1" office:value-type="string">
|
||||
<text:p text:style-name="P17">Total (excl. taxes):</text:p>
|
||||
</table:table-cell>
|
||||
<table:table-cell table:style-name="Table4.B1" office:value-type="string">
|
||||
<text:p text:style-name="P18"><text:placeholder text:placeholder-type="text"><format_currency(purchase.untaxed_amount, purchase.party.lang, purchase.currency)></text:placeholder></text:p>
|
||||
</table:table-cell>
|
||||
</table:table-row>
|
||||
<table:table-row>
|
||||
<table:table-cell table:style-name="Table4.A2" office:value-type="string">
|
||||
<text:p text:style-name="P17">Taxes:</text:p>
|
||||
</table:table-cell>
|
||||
<table:table-cell table:style-name="Table4.B2" office:value-type="string">
|
||||
<text:p text:style-name="P18"><text:placeholder text:placeholder-type="text"><format_currency(purchase.tax_amount, purchase.party.lang, purchase.currency)></text:placeholder></text:p>
|
||||
</table:table-cell>
|
||||
</table:table-row>
|
||||
<table:table-row>
|
||||
<table:table-cell table:style-name="Table4.A2" office:value-type="string">
|
||||
<text:p text:style-name="P17">Total:</text:p>
|
||||
</table:table-cell>
|
||||
<table:table-cell table:style-name="Table4.B2" office:value-type="string">
|
||||
<text:p text:style-name="P18"><text:placeholder text:placeholder-type="text"><format_currency(purchase.total_amount, purchase.party.lang, purchase.currency)></text:placeholder></text:p>
|
||||
</table:table-cell>
|
||||
</table:table-row>
|
||||
</table:table>
|
||||
<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"><for each="comment in (purchase.comment or '').split('\n')"></text:placeholder></text:p>
|
||||
<text:p text:style-name="Text_20_body"><text:soft-page-break/><text:placeholder text:placeholder-type="text"><comment></text:placeholder></text:p>
|
||||
<text:p text:style-name="Text_20_body"><text:placeholder text:placeholder-type="text"></for></text:placeholder></text:p>
|
||||
<text:p text:style-name="Text_20_body"><text:placeholder text:placeholder-type="text"></for></text:placeholder></text:p>
|
||||
</office:text>
|
||||
</office:body>
|
||||
</office:document>
|
||||
2447
modules/purchase/purchase.py
Normal file
2447
modules/purchase/purchase.py
Normal file
File diff suppressed because it is too large
Load Diff
653
modules/purchase/purchase.xml
Normal file
653
modules/purchase/purchase.xml
Normal file
@@ -0,0 +1,653 @@
|
||||
<?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_purchase">
|
||||
<field name="name">Purchase</field>
|
||||
</record>
|
||||
<record model="res.group" id="group_purchase_admin">
|
||||
<field name="name">Purchase Administrator</field>
|
||||
<field name="parent" ref="group_purchase"/>
|
||||
</record>
|
||||
<record model="res.user-res.group" id="user_admin_group_purchase">
|
||||
<field name="user" ref="res.user_admin"/>
|
||||
<field name="group" ref="group_purchase"/>
|
||||
</record>
|
||||
<record model="res.user-res.group" id="user_admin_group_purchase_admin">
|
||||
<field name="user" ref="res.user_admin"/>
|
||||
<field name="group" ref="group_purchase_admin"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.icon" id="purchase_icon">
|
||||
<field name="name">tryton-purchase</field>
|
||||
<field name="path">icons/tryton-purchase.svg</field>
|
||||
</record>
|
||||
|
||||
<menuitem
|
||||
name="Purchases"
|
||||
sequence="70"
|
||||
id="menu_purchase"
|
||||
icon="tryton-purchase"/>
|
||||
<record model="ir.ui.menu-res.group" id="menu_purchase_group_purchase">
|
||||
<field name="menu" ref="menu_purchase"/>
|
||||
<field name="group" ref="group_purchase"/>
|
||||
</record>
|
||||
|
||||
<menuitem
|
||||
name="Reporting"
|
||||
parent="menu_purchase"
|
||||
sequence="100"
|
||||
id="menu_reporting"
|
||||
active="True"/>
|
||||
|
||||
<record model="ir.action.wizard" id="wizard_shipment_handle_exception">
|
||||
<field name="name">Handle Shipment Exception</field>
|
||||
<field name="wiz_name">purchase.handle.shipment.exception</field>
|
||||
<field name="model">purchase.purchase</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.wizard" id="wizard_invoice_handle_exception">
|
||||
<field name="name">Handle Invoice Exception</field>
|
||||
<field name="wiz_name">purchase.handle.invoice.exception</field>
|
||||
<field name="model">purchase.purchase</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="purchase_view_form">
|
||||
<field name="model">purchase.purchase</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">purchase_form</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="purchase_view_tree">
|
||||
<field name="model">purchase.purchase</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="name">purchase_tree</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="handle_shipment_exception_ask_view_form">
|
||||
<field name="model">purchase.handle.shipment.exception.ask</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">handle_shipment_exception_ask_form</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="handle_invoice_exception_ask_view_form">
|
||||
<field name="model">purchase.handle.invoice.exception.ask</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">handle_invoice_exception_ask_form</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.act_window" id="act_purchase_form">
|
||||
<field name="name">Purchases</field>
|
||||
<field name="res_model">purchase.purchase</field>
|
||||
<field name="search_value"></field>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_purchase_form_view1">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="purchase_view_tree"/>
|
||||
<field name="act_window" ref="act_purchase_form"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_purchase_form_view2">
|
||||
<field name="sequence" eval="20"/>
|
||||
<field name="view" ref="purchase_view_form"/>
|
||||
<field name="act_window" ref="act_purchase_form"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.domain"
|
||||
id="act_purchase_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_purchase_form"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.domain"
|
||||
id="act_purchase_form_domain_quotation">
|
||||
<field name="name">Quotation</field>
|
||||
<field name="sequence" eval="20"/>
|
||||
<field name="domain" eval="[('state', '=', 'quotation')]" pyson="1"/>
|
||||
<field name="count" eval="True"/>
|
||||
<field name="act_window" ref="act_purchase_form"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.domain"
|
||||
id="act_purchase_form_domain_confirmed">
|
||||
<field name="name">Confirmed</field>
|
||||
<field name="sequence" eval="30"/>
|
||||
<field name="domain" eval="[('state', '=', 'confirmed')]" pyson="1"/>
|
||||
<field name="count" eval="True"/>
|
||||
<field name="act_window" ref="act_purchase_form"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.domain"
|
||||
id="act_purchase_form_domain_processing">
|
||||
<field name="name">Processing</field>
|
||||
<field name="sequence" eval="40"/>
|
||||
<field name="domain" eval="[('state', '=', 'processing')]" pyson="1"/>
|
||||
<field name="count" eval="True"/>
|
||||
<field name="act_window" ref="act_purchase_form"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.domain"
|
||||
id="act_purchase_form_domain_exception">
|
||||
<field name="name">Exception</field>
|
||||
<field name="sequence" eval="50"/>
|
||||
<field name="domain"
|
||||
eval="['OR', ('invoice_state', '=', 'exception'), ('shipment_state', '=', 'exception')]"
|
||||
pyson="1"/>
|
||||
<field name="count" eval="True"/>
|
||||
<field name="act_window" ref="act_purchase_form"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.domain"
|
||||
id="act_purchase_form_domain_all">
|
||||
<field name="name">All</field>
|
||||
<field name="sequence" eval="9999"/>
|
||||
<field name="domain"></field>
|
||||
<field name="act_window" ref="act_purchase_form"/>
|
||||
</record>
|
||||
<menuitem
|
||||
parent="menu_purchase"
|
||||
action="act_purchase_form"
|
||||
sequence="10"
|
||||
id="menu_purchase_form"/>
|
||||
|
||||
<record model="ir.action.act_window" id="act_purchase_invoice_relate">
|
||||
<field name="name">Purchases</field>
|
||||
<field name="res_model">purchase.purchase</field>
|
||||
<field name="domain"
|
||||
eval="[('invoices', 'in', Eval('active_ids'))]"
|
||||
pyson="1"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view"
|
||||
id="act_purchase_invoice_relate_view1">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="purchase_view_tree"/>
|
||||
<field name="act_window" ref="act_purchase_invoice_relate"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view"
|
||||
id="act_purchase_invoice_relate_view2">
|
||||
<field name="sequence" eval="20"/>
|
||||
<field name="view" ref="purchase_view_form"/>
|
||||
<field name="act_window" ref="act_purchase_invoice_relate"/>
|
||||
</record>
|
||||
<record model="ir.action.keyword"
|
||||
id="act_purchase_invoice_relate_keyword">
|
||||
<field name="keyword">form_relate</field>
|
||||
<field name="model">account.invoice,-1</field>
|
||||
<field name="action" ref="act_purchase_invoice_relate"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.act_window" id="act_purchase_relate">
|
||||
<field name="name">Purchases</field>
|
||||
<field name="res_model">purchase.purchase</field>
|
||||
<field name="domain"
|
||||
eval="[
|
||||
If(Eval('active_model') == 'party.party',
|
||||
('party', 'in', Eval('active_ids', [])), ()),
|
||||
]"
|
||||
pyson="1"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_purchase_relate_view1">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="purchase_view_tree"/>
|
||||
<field name="act_window" ref="act_purchase_relate"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_purchase_relate_view2">
|
||||
<field name="sequence" eval="20"/>
|
||||
<field name="view" ref="purchase_view_form"/>
|
||||
<field name="act_window" ref="act_purchase_relate"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.domain" id="act_purchase_relate_pending">
|
||||
<field name="name">Pending</field>
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="domain" eval="[('state', 'not in', ['done', 'cancelled'])]" pyson="1"/>
|
||||
<field name="count" eval="True"/>
|
||||
<field name="act_window" ref="act_purchase_relate"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.domain" id="act_purchase_relate_done">
|
||||
<field name="name">Done</field>
|
||||
<field name="sequence" eval="20"/>
|
||||
<field name="domain" eval="[('state', '=', 'done')]" pyson="1"/>
|
||||
<field name="count" eval="True"/>
|
||||
<field name="act_window" ref="act_purchase_relate"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.domain" id="act_purchase_relate_all">
|
||||
<field name="name">All</field>
|
||||
<field name="sequence" eval="9999"/>
|
||||
<field name="domain"></field>
|
||||
<field name="act_window" ref="act_purchase_relate"/>
|
||||
</record>
|
||||
<record model="ir.action.keyword" id="act_purchase_relate_keyword_party">
|
||||
<field name="keyword">form_relate</field>
|
||||
<field name="model">party.party,-1</field>
|
||||
<field name="action" ref="act_purchase_relate"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.act_window" id="act_purchase_relate_simple">
|
||||
<field name="name">Purchases</field>
|
||||
<field name="res_model">purchase.purchase</field>
|
||||
<field
|
||||
name="domain"
|
||||
eval="[
|
||||
If(Eval('active_model') == 'stock.shipment.in',
|
||||
('shipments', 'in', Eval('active_ids', [])), ()),
|
||||
If(Eval('active_model') == 'stock.shipment.in.return',
|
||||
('shipment_returns', 'in', Eval('active_ids', [])), ()),
|
||||
]"
|
||||
pyson="1"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_purchase_relate_simple_view1">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="purchase_view_tree"/>
|
||||
<field name="act_window" ref="act_purchase_relate_simple"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_purchase_relate_simple_view2">
|
||||
<field name="sequence" eval="20"/>
|
||||
<field name="view" ref="purchase_view_form"/>
|
||||
<field name="act_window" ref="act_purchase_relate_simple"/>
|
||||
</record>
|
||||
<record model="ir.action.keyword" id="act_purchase_relate_simple_keyword_shipment_in">
|
||||
<field name="keyword">form_relate</field>
|
||||
<field name="model">stock.shipment.in,-1</field>
|
||||
<field name="action" ref="act_purchase_relate_simple"/>
|
||||
</record>
|
||||
<record model="ir.action.keyword" id="act_purchase_relate_simple_keyword_shipment_in_return">
|
||||
<field name="keyword">form_relate</field>
|
||||
<field name="model">stock.shipment.in.return,-1</field>
|
||||
<field name="action" ref="act_purchase_relate_simple"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.access" id="access_purchase">
|
||||
<field name="model">purchase.purchase</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_purchase_purchase">
|
||||
<field name="model">purchase.purchase</field>
|
||||
<field name="group" ref="group_purchase"/>
|
||||
<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.access" id="access_purchase_account">
|
||||
<field name="model">purchase.purchase</field>
|
||||
<field name="group" ref="account.group_account"/>
|
||||
<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_purchase_group_stock">
|
||||
<field name="model">purchase.purchase</field>
|
||||
<field name="group" ref="stock.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.field.access" id="access_purchase_purchase_invoices_ignored">
|
||||
<field name="model">purchase.purchase</field>
|
||||
<field name="field">invoices_ignored</field>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_write" eval="False"/>
|
||||
</record>
|
||||
<record model="ir.model.field.access" id="access_purchase_purchase_invoices_ignored_purchase_admin">
|
||||
<field name="model">purchase.purchase</field>
|
||||
<field name="field">invoices_ignored</field>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_write" eval="True"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.button" id="purchase_cancel_button">
|
||||
<field name="model">purchase.purchase</field>
|
||||
<field name="name">cancel</field>
|
||||
<field name="string">Cancel</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.button" id="purchase_draft_button">
|
||||
<field name="model">purchase.purchase</field>
|
||||
<field name="name">draft</field>
|
||||
<field name="string">Draft</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.button" id="purchase_quote_button">
|
||||
<field name="model">purchase.purchase</field>
|
||||
<field name="name">quote</field>
|
||||
<field name="string">Quote</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.button" id="purchase_confirm_button">
|
||||
<field name="model">purchase.purchase</field>
|
||||
<field name="name">confirm</field>
|
||||
<field name="string">Confirm</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.button" id="purchase_process_button">
|
||||
<field name="model">purchase.purchase</field>
|
||||
<field name="name">process</field>
|
||||
<field name="string">Process</field>
|
||||
</record>
|
||||
<record model="ir.model.button-res.group" id="purchase_process_button_group_purchase_admin">
|
||||
<field name="button" ref="purchase_process_button"/>
|
||||
<field name="group" ref="group_purchase_admin"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.button" id="purchase_manual_invoice_button">
|
||||
<field name="model">purchase.purchase</field>
|
||||
<field name="name">manual_invoice</field>
|
||||
<field name="string">Create Invoice</field>
|
||||
</record>
|
||||
<record model="ir.model.button-res.group" id="purchase_manual_button_group_purchase">
|
||||
<field name="button" ref="purchase_manual_invoice_button"/>
|
||||
<field name="group" ref="group_purchase"/>
|
||||
</record>
|
||||
<record model="ir.model.button-res.group" id="purchase_manual_button_group_account">
|
||||
<field name="button" ref="purchase_manual_invoice_button"/>
|
||||
<field name="group" ref="account.group_account"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.button" id="purchase_modify_header_button">
|
||||
<field name="model">purchase.purchase</field>
|
||||
<field name="name">modify_header</field>
|
||||
<field name="string">Modify Header</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.button"
|
||||
id="purchase_hande_invoice_exception_button">
|
||||
<field name="model">purchase.purchase</field>
|
||||
<field name="name">handle_invoice_exception</field>
|
||||
<field name="string">Handle Invoice Exception</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.button"
|
||||
id="purchase_hande_shipment_exception_button">
|
||||
<field name="model">purchase.purchase</field>
|
||||
<field name="name">handle_shipment_exception</field>
|
||||
<field name="string">Handle Shipment Exception</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.sequence.type" id="sequence_type_purchase">
|
||||
<field name="name">Purchase</field>
|
||||
</record>
|
||||
<record model="ir.sequence.type-res.group"
|
||||
id="sequence_type_purchase_group_admin">
|
||||
<field name="sequence_type" ref="sequence_type_purchase"/>
|
||||
<field name="group" ref="res.group_admin"/>
|
||||
</record>
|
||||
<record model="ir.sequence.type-res.group"
|
||||
id="sequence_type_purchase_group_purchase_admin">
|
||||
<field name="sequence_type" ref="sequence_type_purchase"/>
|
||||
<field name="group" ref="group_purchase_admin"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.sequence" id="sequence_purchase">
|
||||
<field name="name">Purchase</field>
|
||||
<field name="sequence_type" ref="sequence_type_purchase"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.report" id="report_purchase">
|
||||
<field name="name">Purchase</field>
|
||||
<field name="model">purchase.purchase</field>
|
||||
<field name="report_name">purchase.purchase</field>
|
||||
<field name="report">purchase/purchase.fodt</field>
|
||||
</record>
|
||||
<record model="ir.action.keyword" id="report_purchase_keyword">
|
||||
<field name="keyword">form_print</field>
|
||||
<field name="model">purchase.purchase,-1</field>
|
||||
<field name="action" ref="report_purchase"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="purchase_line_view_form">
|
||||
<field name="model">purchase.line</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">purchase_line_form</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="purchase_line_view_tree">
|
||||
<field name="model">purchase.line</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="priority" eval="10"/>
|
||||
<field name="name">purchase_line_tree</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="purchase_line_view_tree_sequence">
|
||||
<field name="model">purchase.line</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="priority" eval="20"/>
|
||||
<field name="name">purchase_line_tree_sequence</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.act_window" id="act_purchase_line_relate">
|
||||
<field name="name">Purchase Lines</field>
|
||||
<field name="res_model">purchase.line</field>
|
||||
<field
|
||||
name="domain"
|
||||
eval="[('type', '=', 'line'),
|
||||
If(Eval('active_model') == 'purchase.purchase',
|
||||
('purchase', 'in', Eval('active_ids', [])), ()),
|
||||
If(Eval('active_model') == 'product.product',
|
||||
('product', 'in', Eval('active_ids', [])), ()),
|
||||
If(Eval('active_model') == 'product.template',
|
||||
('product.template.id', 'in', Eval('active_ids', [])), ()),
|
||||
If(Eval('active_model') == 'party.party',
|
||||
('supplier', 'in', Eval('active_ids', [])), ()),
|
||||
]"
|
||||
pyson="1"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_purchase_line_relate_view1">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="purchase_line_view_tree"/>
|
||||
<field name="act_window" ref="act_purchase_line_relate"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_purchase_line_relate_view2">
|
||||
<field name="sequence" eval="20"/>
|
||||
<field name="view" ref="purchase_line_view_form"/>
|
||||
<field name="act_window" ref="act_purchase_line_relate"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.domain" id="act_purchase_line_relate_pending">
|
||||
<field name="name">Pending</field>
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="domain" eval="[('purchase_state', 'not in', ['done', 'cancelled'])]" pyson="1"/>
|
||||
<field name="count" eval="True"/>
|
||||
<field name="act_window" ref="act_purchase_line_relate"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.domain" id="act_purchase_line_relate_done">
|
||||
<field name="name">Done</field>
|
||||
<field name="sequence" eval="20"/>
|
||||
<field name="domain" eval="[('purchase_state', '=', 'done')]" pyson="1"/>
|
||||
<field name="count" eval="True"/>
|
||||
<field name="act_window" ref="act_purchase_line_relate"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.domain" id="act_purchase_line_relate_all">
|
||||
<field name="name">All</field>
|
||||
<field name="sequence" eval="9999"/>
|
||||
<field name="domain"></field>
|
||||
<field name="act_window" ref="act_purchase_line_relate"/>
|
||||
</record>
|
||||
<record model="ir.action.keyword" id="act_purchase_line_relate_keyword_purchase">
|
||||
<field name="keyword">form_relate</field>
|
||||
<field name="model">purchase.purchase,-1</field>
|
||||
<field name="action" ref="act_purchase_line_relate"/>
|
||||
</record>
|
||||
<record model="ir.action.keyword" id="act_purchase_line_relate_keyword_product">
|
||||
<field name="keyword">form_relate</field>
|
||||
<field name="model">product.product,-1</field>
|
||||
<field name="action" ref="act_purchase_line_relate"/>
|
||||
</record>
|
||||
<record model="ir.action.keyword" id="act_purchase_line_relate_keyword_product_template">
|
||||
<field name="keyword">form_relate</field>
|
||||
<field name="model">product.template,-1</field>
|
||||
<field name="action" ref="act_purchase_line_relate"/>
|
||||
</record>
|
||||
<record model="ir.action.keyword" id="act_purchase_line_relate_keyword_party">
|
||||
<field name="keyword">form_relate</field>
|
||||
<field name="model">party.party,-1</field>
|
||||
<field name="action" ref="act_purchase_line_relate"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.access" id="access_purchase_line_group_stock">
|
||||
<field name="model">purchase.line</field>
|
||||
<field name="group" ref="stock.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.field.access" id="access_purchase_line_moves_ignored">
|
||||
<field name="model">purchase.line</field>
|
||||
<field name="field">moves_ignored</field>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_write" eval="False"/>
|
||||
</record>
|
||||
<record model="ir.model.field.access" id="access_purchase_line_moves_ignored_purchase_admin">
|
||||
<field name="model">purchase.line</field>
|
||||
<field name="field">moves_ignored</field>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_write" eval="True"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="product_supplier_view_form">
|
||||
<field name="model">purchase.product_supplier</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">product_supplier_form</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="product_supplier_view_tree">
|
||||
<field name="model">purchase.product_supplier</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="priority" eval="10"/>
|
||||
<field name="name">product_supplier_tree</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="product_supplier_view_tree_sequence">
|
||||
<field name="model">purchase.product_supplier</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="priority" eval="20"/>
|
||||
<field name="name">product_supplier_tree_sequence</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.act_window" id="act_product_supplier_form">
|
||||
<field name="name">Suppliers</field>
|
||||
<field name="res_model">purchase.product_supplier</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view"
|
||||
id="act_product_supplier_list_view">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="product_supplier_view_tree"/>
|
||||
<field name="act_window" ref="act_product_supplier_form"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view"
|
||||
id="act_product_supplier_form_view">
|
||||
<field name="sequence" eval="20"/>
|
||||
<field name="view" ref="product_supplier_view_form"/>
|
||||
<field name="act_window" ref="act_product_supplier_form"/>
|
||||
</record>
|
||||
|
||||
<menuitem
|
||||
parent="product.menu_template"
|
||||
sequence="20"
|
||||
action="act_product_supplier_form"
|
||||
id="menu_product_supplier"/>
|
||||
|
||||
<record model="ir.model.access" id="access_product_supplier">
|
||||
<field name="model">purchase.product_supplier</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_supplier_purchase">
|
||||
<field name="model">purchase.product_supplier</field>
|
||||
<field name="group" ref="group_purchase"/>
|
||||
<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.rule.group" id="rule_group_product_supplier_companies">
|
||||
<field name="name">User in companies</field>
|
||||
<field name="model">purchase.product_supplier</field>
|
||||
<field name="global_p" eval="True"/>
|
||||
</record>
|
||||
<record model="ir.rule" id="rule_product_supplier_companies">
|
||||
<field name="domain"
|
||||
eval="[('company', 'in', Eval('companies', []))]"
|
||||
pyson="1"/>
|
||||
<field name="rule_group" ref="rule_group_product_supplier_companies"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="product_supplier_price_view_form">
|
||||
<field name="model">purchase.product_supplier.price</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">product_supplier_price_form</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="product_supplier_price_view_tree">
|
||||
<field name="model">purchase.product_supplier.price</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="name">product_supplier_price_tree</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="product_supplier_price_view_list_sequence">
|
||||
<field name="model">purchase.product_supplier.price</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="name">product_supplier_price_list_sequence</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.act_window"
|
||||
id="act_product_supplier_price_form">
|
||||
<field name="name">Prices</field>
|
||||
<field name="res_model">purchase.product_supplier.price</field>
|
||||
<field name="domain"
|
||||
eval="[If(Eval('active_ids', []) == [Eval('active_id')], ('product_supplier', '=', Eval('active_id')), ('product_supplier', 'in', Eval('active_ids')))]"
|
||||
pyson="1"/>
|
||||
</record>
|
||||
<record model="ir.action.keyword"
|
||||
id="act_product_supplier_price_form_keyword1">
|
||||
<field name="keyword">form_relate</field>
|
||||
<field name="model">purchase.product_supplier,-1</field>
|
||||
<field name="action" ref="act_product_supplier_price_form"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="return_purchase_start_view_form">
|
||||
<field name="model">purchase.return_purchase.start</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">return_purchase_start_form</field>
|
||||
</record>
|
||||
<record model="ir.action.wizard" id="wizard_return_purchase">
|
||||
<field name="name">Return Purchase</field>
|
||||
<field name="wiz_name">purchase.return_purchase</field>
|
||||
<field name="model">purchase.purchase</field>
|
||||
</record>
|
||||
<record model="ir.action.keyword" id="act_wizard_return_purchase_keyword">
|
||||
<field name="keyword">form_action</field>
|
||||
<field name="model">purchase.purchase,-1</field>
|
||||
<field name="action" ref="wizard_return_purchase"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.wizard" id="wizard_modify_header">
|
||||
<field name="name">Modify Header</field>
|
||||
<field name="wiz_name">purchase.modify_header</field>
|
||||
<field name="model">purchase.purchase</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="modify_header_form">
|
||||
<field name="model">purchase.purchase</field>
|
||||
<field name="inherit" ref="purchase.purchase_view_form"/>
|
||||
<field name="name">modify_header_form</field>
|
||||
<field name="domain"
|
||||
eval="Eval('context', {}).get('modify_header', False)"
|
||||
pyson="1"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.rule.group" id="rule_group_purchase_companies">
|
||||
<field name="name">User in companies</field>
|
||||
<field name="model">purchase.purchase</field>
|
||||
<field name="global_p" eval="True"/>
|
||||
</record>
|
||||
<record model="ir.rule" id="rule_purchase_companies">
|
||||
<field name="domain"
|
||||
eval="[('company', 'in', Eval('companies', []))]"
|
||||
pyson="1"/>
|
||||
<field name="rule_group" ref="rule_group_purchase_companies"/>
|
||||
</record>
|
||||
</data>
|
||||
</tryton>
|
||||
390
modules/purchase/purchase_reporting.py
Normal file
390
modules/purchase/purchase_reporting.py
Normal file
@@ -0,0 +1,390 @@
|
||||
# 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 dateutil.relativedelta import relativedelta
|
||||
from sql import Null
|
||||
from sql.aggregate import Count, Min, Sum
|
||||
from sql.conditionals import Coalesce
|
||||
from sql.functions import DateTrunc
|
||||
|
||||
from trytond.i18n import lazy_gettext
|
||||
from trytond.model import ModelSQL, ModelView, fields
|
||||
from trytond.modules.currency.fields import Monetary
|
||||
from trytond.pool import Pool
|
||||
from trytond.pyson import Eval, If
|
||||
from trytond.tools import pairwise_longest, sql_pairing, unpair
|
||||
from trytond.tools.chart import sparkline
|
||||
from trytond.transaction import Transaction
|
||||
|
||||
|
||||
class Abstract(ModelSQL):
|
||||
|
||||
company = fields.Many2One(
|
||||
'company.company',
|
||||
lazy_gettext("purchase.msg_purchase_reporting_company"))
|
||||
number = fields.Integer(
|
||||
lazy_gettext("purchase.msg_purchase_reporting_number"),
|
||||
help=lazy_gettext("purchase.msg_purchase_reporting_number_help"))
|
||||
expense = Monetary(
|
||||
lazy_gettext("purchase.msg_purchase_reporting_expense"),
|
||||
digits='currency', currency='currency')
|
||||
expense_trend = fields.Function(
|
||||
fields.Char(
|
||||
lazy_gettext("purchase.msg_purchase_reporting_expense_trend")),
|
||||
'get_trend')
|
||||
time_series = None
|
||||
currency = fields.Many2One(
|
||||
'currency.currency',
|
||||
lazy_gettext("purchase.msg_purchase_reporting_currency"))
|
||||
|
||||
@classmethod
|
||||
def table_query(cls):
|
||||
from_item, tables = cls._joins()
|
||||
return from_item.select(*cls._columns(tables),
|
||||
where=cls._where(tables),
|
||||
group_by=cls._group_by(tables))
|
||||
|
||||
@classmethod
|
||||
def _joins(cls):
|
||||
pool = Pool()
|
||||
Line = pool.get('purchase.line')
|
||||
Purchase = pool.get('purchase.purchase')
|
||||
|
||||
tables = {}
|
||||
tables['line'] = line = Line.__table__()
|
||||
tables['line.purchase'] = purchase = Purchase.__table__()
|
||||
|
||||
from_item = (line
|
||||
.join(purchase, condition=line.purchase == purchase.id))
|
||||
return from_item, tables
|
||||
|
||||
@classmethod
|
||||
def _columns(cls, tables):
|
||||
line = tables['line']
|
||||
purchase = tables['line.purchase']
|
||||
|
||||
quantity = Coalesce(line.actual_quantity, line.quantity)
|
||||
expense = cls.expense.sql_cast(
|
||||
Sum(quantity * line.unit_price))
|
||||
return [
|
||||
cls._column_id(tables).as_('id'),
|
||||
purchase.company.as_('company'),
|
||||
purchase.currency.as_('currency'),
|
||||
expense.as_('expense'),
|
||||
Count(purchase.id, distinct=True).as_('number'),
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def _column_id(cls, tables):
|
||||
line = tables['line']
|
||||
return Min(line.id)
|
||||
|
||||
@classmethod
|
||||
def _group_by(cls, tables):
|
||||
purchase = tables['line.purchase']
|
||||
return [purchase.company, purchase.currency]
|
||||
|
||||
@classmethod
|
||||
def _where(cls, tables):
|
||||
context = Transaction().context
|
||||
purchase = tables['line.purchase']
|
||||
|
||||
where = purchase.company == context.get('company')
|
||||
where &= purchase.state.in_(cls._purchase_states())
|
||||
from_date = context.get('from_date')
|
||||
if from_date:
|
||||
where &= purchase.purchase_date >= from_date
|
||||
to_date = context.get('to_date')
|
||||
if to_date:
|
||||
where &= purchase.purchase_date <= to_date
|
||||
warehouse = context.get('warehouse')
|
||||
if warehouse:
|
||||
where &= purchase.warehouse == warehouse
|
||||
return where
|
||||
|
||||
@classmethod
|
||||
def _purchase_states(cls):
|
||||
return ['confirmed', 'processing', 'done']
|
||||
|
||||
@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) if ts else 0 for ts in self.time_series_all])
|
||||
|
||||
|
||||
class AbstractTimeseries(Abstract):
|
||||
|
||||
date = fields.Date(lazy_gettext('purchase.msg_purchase_reporting_date'))
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super().__setup__()
|
||||
cls._order.insert(0, ('date', 'ASC'))
|
||||
|
||||
@classmethod
|
||||
def _columns(cls, tables):
|
||||
return super()._columns(tables) + [
|
||||
cls._column_date(tables).as_('date')]
|
||||
|
||||
@classmethod
|
||||
def _column_date(cls, tables):
|
||||
context = Transaction().context
|
||||
purchase = tables['line.purchase']
|
||||
date = DateTrunc(context.get('period'), purchase.purchase_date)
|
||||
date = cls.date.sql_cast(date)
|
||||
return date
|
||||
|
||||
@classmethod
|
||||
def _group_by(cls, tables):
|
||||
return super()._group_by(tables) + [cls._column_date(tables)]
|
||||
|
||||
|
||||
class Context(ModelView):
|
||||
__name__ = 'purchase.reporting.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')),
|
||||
()),
|
||||
],
|
||||
depends=['to_date'])
|
||||
to_date = fields.Date("To Date",
|
||||
domain=[
|
||||
If(Eval('from_date') & Eval('to_date'),
|
||||
('to_date', '>=', Eval('from_date')),
|
||||
()),
|
||||
],
|
||||
depends=['from_date'])
|
||||
period = fields.Selection([
|
||||
('year', "Year"),
|
||||
('month', "Month"),
|
||||
('day', "Day"),
|
||||
], "Period", required=True)
|
||||
warehouse = fields.Many2One(
|
||||
'stock.location', "Warehouse",
|
||||
domain=[
|
||||
('type', '=', 'warehouse'),
|
||||
])
|
||||
|
||||
@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_warehouse(cls):
|
||||
return Transaction().context.get('warehouse')
|
||||
|
||||
|
||||
class Main(Abstract, ModelView):
|
||||
__name__ = 'purchase.reporting.main'
|
||||
|
||||
time_series = fields.Function(fields.Many2Many(
|
||||
'purchase.reporting.main.time_series', None, None,
|
||||
lazy_gettext('purchase.msg_purchase_reporting_time_series')),
|
||||
'get_time_series')
|
||||
|
||||
def get_rec_name(self, name):
|
||||
return ''
|
||||
|
||||
def get_time_series(self, name):
|
||||
pool = Pool()
|
||||
Timeseries = pool.get('purchase.reporting.main.time_series')
|
||||
return [t.id for t in Timeseries.search([])]
|
||||
|
||||
|
||||
class MainTimeseries(AbstractTimeseries, ModelView):
|
||||
__name__ = 'purchase.reporting.main.time_series'
|
||||
|
||||
|
||||
class SupplierMixin(object):
|
||||
__slots__ = ()
|
||||
supplier = fields.Many2One(
|
||||
'party.party', "Supplier",
|
||||
context={
|
||||
'company': Eval('company', -1),
|
||||
},
|
||||
depends=['company'])
|
||||
|
||||
@classmethod
|
||||
def _columns(cls, tables):
|
||||
purchase = tables['line.purchase']
|
||||
return super()._columns(tables) + [purchase.party.as_('supplier')]
|
||||
|
||||
@classmethod
|
||||
def _group_by(cls, tables):
|
||||
purchase = tables['line.purchase']
|
||||
return super()._group_by(tables) + [purchase.party]
|
||||
|
||||
def get_rec_name(self, name):
|
||||
return self.supplier.rec_name
|
||||
|
||||
@classmethod
|
||||
def search_rec_name(cls, name, clause):
|
||||
return [('supplier.rec_name', *clause[1:])]
|
||||
|
||||
|
||||
class Supplier(SupplierMixin, Abstract, ModelView):
|
||||
__name__ = 'purchase.reporting.supplier'
|
||||
|
||||
time_series = fields.One2Many(
|
||||
'purchase.reporting.supplier.time_series', 'supplier',
|
||||
lazy_gettext('purchase.msg_purchase_reporting_time_series'))
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super().__setup__()
|
||||
cls._order.insert(0, ('supplier', 'ASC'))
|
||||
|
||||
@classmethod
|
||||
def _column_id(cls, tables):
|
||||
purchase = tables['line.purchase']
|
||||
return sql_pairing(purchase.party, purchase.currency)
|
||||
|
||||
|
||||
class SupplierTimeseries(SupplierMixin, AbstractTimeseries, ModelView):
|
||||
__name__ = 'purchase.reporting.supplier.time_series'
|
||||
|
||||
supplier_currency = fields.Integer("Supplier - Currency")
|
||||
|
||||
@classmethod
|
||||
def _columns(cls, tables):
|
||||
purchase = tables['line.purchase']
|
||||
return super()._columns(tables) + [
|
||||
sql_pairing(
|
||||
purchase.party, purchase.currency).as_('supplier_currency'),
|
||||
]
|
||||
|
||||
|
||||
class ProductMixin(object):
|
||||
__slots__ = ()
|
||||
product = fields.Many2One(
|
||||
'product.product', "Product",
|
||||
context={
|
||||
'company': Eval('company', -1),
|
||||
},
|
||||
depends=['company'])
|
||||
product_supplier = fields.Many2One(
|
||||
'purchase.product_supplier', "Supplier's Product")
|
||||
|
||||
@classmethod
|
||||
def _columns(cls, tables):
|
||||
line = tables['line']
|
||||
return super()._columns(tables) + [
|
||||
line.product.as_('product'),
|
||||
line.product_supplier.as_('product_supplier'),
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def _group_by(cls, tables):
|
||||
line = tables['line']
|
||||
return super()._group_by(tables) + [
|
||||
line.product, line.product_supplier]
|
||||
|
||||
@classmethod
|
||||
def _where(cls, tables):
|
||||
context = Transaction().context
|
||||
line = tables['line']
|
||||
purchase = tables['line.purchase']
|
||||
where = super()._where(tables)
|
||||
where &= line.product != Null
|
||||
if context.get('supplier_currency') is not None:
|
||||
supplier, currency = unpair(context['supplier_currency'])
|
||||
else:
|
||||
supplier = context.get('supplier')
|
||||
currency = context.get('currency')
|
||||
where &= purchase.party == supplier
|
||||
where &= purchase.currency == currency
|
||||
return where
|
||||
|
||||
def get_rec_name(self, name):
|
||||
pool = Pool()
|
||||
Party = pool.get('party.party')
|
||||
context = Transaction().context
|
||||
|
||||
name = self.product.rec_name if self.product else None
|
||||
if context.get('supplier'):
|
||||
supplier = Party(context['supplier'])
|
||||
name += '@%s' % supplier.rec_name
|
||||
return name
|
||||
|
||||
@classmethod
|
||||
def search_rec_name(cls, name, clause):
|
||||
return [('product.rec_name', *clause[1:])]
|
||||
|
||||
|
||||
class Product(ProductMixin, Abstract, ModelView):
|
||||
__name__ = 'purchase.reporting.product'
|
||||
|
||||
time_series = fields.One2Many(
|
||||
'purchase.reporting.product.time_series', 'product',
|
||||
lazy_gettext('purchase.msg_purchase_reporting_time_series'))
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super().__setup__()
|
||||
cls._order.insert(0, ('product', 'ASC'))
|
||||
|
||||
@classmethod
|
||||
def _column_id(cls, tables):
|
||||
line = tables['line']
|
||||
purchase = tables['line.purchase']
|
||||
return sql_pairing(line.product, purchase.currency)
|
||||
|
||||
|
||||
class ProductTimeseries(ProductMixin, AbstractTimeseries, ModelView):
|
||||
__name__ = 'purchase.reporting.product.time_series'
|
||||
|
||||
product_currency = fields.Integer("Product - Currency")
|
||||
|
||||
@classmethod
|
||||
def _columns(cls, tables):
|
||||
line = tables['line']
|
||||
purchase = tables['line.purchase']
|
||||
return super()._columns(tables) + [
|
||||
sql_pairing(
|
||||
line.product, purchase.currency).as_('product_currency'),
|
||||
]
|
||||
456
modules/purchase/purchase_reporting.xml
Normal file
456
modules/purchase/purchase_reporting.xml
Normal file
@@ -0,0 +1,456 @@
|
||||
<?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 parent="menu_reporting"
|
||||
name="Purchases"
|
||||
sequence="10"
|
||||
id="menu_reporting_purchase"
|
||||
icon="tryton-graph"/>
|
||||
|
||||
<record model="ir.ui.view" id="reporting_context_view_form">
|
||||
<field name="model">purchase.reporting.context</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">purchase_reporting_context_form</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="reporting_main_view_list">
|
||||
<field name="model">purchase.reporting.main</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="name">purchase_reporting_main_list</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="reporting_main_view_graph_expense">
|
||||
<field name="model">purchase.reporting.main</field>
|
||||
<field name="type">graph</field>
|
||||
<field name="name">purchase_reporting_main_graph_expense</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="reporting_main_view_graph_number">
|
||||
<field name="model">purchase.reporting.main</field>
|
||||
<field name="type">graph</field>
|
||||
<field name="name">purchase_reporting_main_graph_number</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.act_window" id="act_reporting_main">
|
||||
<field name="name">Purchases</field>
|
||||
<field name="res_model">purchase.reporting.main</field>
|
||||
<field name="context_model">purchase.reporting.context</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_reporting_main_view1">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="reporting_main_view_list"/>
|
||||
<field name="act_window" ref="act_reporting_main"/>
|
||||
</record>
|
||||
<record model="ir.action.keyword" id="act_reporting_main_keyword1">
|
||||
<field name="keyword">tree_open</field>
|
||||
<field name="model" ref="menu_reporting_purchase"/>
|
||||
<field name="action" ref="act_reporting_main"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.rule.group" id="rule_group_reporting_main_companies">
|
||||
<field name="name">User in companies</field>
|
||||
<field name="model">purchase.reporting.main</field>
|
||||
<field name="global_p" eval="True"/>
|
||||
</record>
|
||||
<record model="ir.rule" id="rule_reporting_main_companies">
|
||||
<field name="domain"
|
||||
eval="[('company', 'in', Eval('companies', []))]"
|
||||
pyson="1"/>
|
||||
<field name="rule_group" ref="rule_group_reporting_main_companies"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.access" id="access_reporting_main">
|
||||
<field name="model">purchase.reporting.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_main_purchase">
|
||||
<field name="model">purchase.reporting.main</field>
|
||||
<field name="group" ref="group_purchase"/>
|
||||
<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_main_time_series_view_list">
|
||||
<field name="model">purchase.reporting.main.time_series</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="name">purchase_reporting_main_time_series_list</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="reporting_main_time_series_view_graph_expense">
|
||||
<field name="model">purchase.reporting.main.time_series</field>
|
||||
<field name="type">graph</field>
|
||||
<field name="name">purchase_reporting_main_time_series_graph_expense</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="reporting_main_time_series_view_graph_number">
|
||||
<field name="model">purchase.reporting.main.time_series</field>
|
||||
<field name="type">graph</field>
|
||||
<field name="name">purchase_reporting_main_time_series_graph_number</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.act_window" id="act_reporting_main_time_series">
|
||||
<field name="name">Purchases</field>
|
||||
<field name="res_model">purchase.reporting.main.time_series</field>
|
||||
<field name="context_model">purchase.reporting.context</field>
|
||||
<field name="order" eval="[('date', 'DESC')]" pyson="1"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_reporting_main_time_series_list_view1">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="reporting_main_time_series_view_list"/>
|
||||
<field name="act_window" ref="act_reporting_main_time_series"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_reporting_main_time_series_list_view2">
|
||||
<field name="sequence" eval="20"/>
|
||||
<field name="view" ref="reporting_main_time_series_view_graph_expense"/>
|
||||
<field name="act_window" ref="act_reporting_main_time_series"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_reporting_main_time_series_list_view3">
|
||||
<field name="sequence" eval="30"/>
|
||||
<field name="view" ref="reporting_main_time_series_view_graph_number"/>
|
||||
<field name="act_window" ref="act_reporting_main_time_series"/>
|
||||
</record>
|
||||
<record model="ir.action.keyword" id="act_reporting_main_time_series_list_keyword1">
|
||||
<field name="keyword">tree_open</field>
|
||||
<field name="model">purchase.reporting.main,-1</field>
|
||||
<field name="action" ref="act_reporting_main_time_series"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.rule.group" id="rule_group_reporting_main_time_series_companies">
|
||||
<field name="name">User in companies</field>
|
||||
<field name="model">purchase.reporting.main.time_series</field>
|
||||
<field name="global_p" eval="True"/>
|
||||
</record>
|
||||
<record model="ir.rule" id="rule_reporting_main_time_series_companies">
|
||||
<field name="domain"
|
||||
eval="[('company', 'in', Eval('companies', []))]"
|
||||
pyson="1"/>
|
||||
<field name="rule_group" ref="rule_group_reporting_main_time_series_companies"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.access" id="access_reporting_main_time_series">
|
||||
<field name="model">purchase.reporting.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_main_time_series_purchase">
|
||||
<field name="model">purchase.reporting.main.time_series</field>
|
||||
<field name="group" ref="group_purchase"/>
|
||||
<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>
|
||||
|
||||
<!-- Supplier -->
|
||||
|
||||
<record model="ir.ui.view" id="reporting_supplier_view_list">
|
||||
<field name="model">purchase.reporting.supplier</field>
|
||||
<field name="inherit" ref="reporting_main_view_list"/>
|
||||
<field name="name">purchase_reporting_supplier_list</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="reporting_supplier_view_graph_expense">
|
||||
<field name="model">purchase.reporting.supplier</field>
|
||||
<field name="inherit" ref="reporting_main_view_graph_expense"/>
|
||||
<field name="name">purchase_reporting_supplier_graph_expense</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="reporting_supplier_view_graph_number">
|
||||
<field name="model">purchase.reporting.supplier</field>
|
||||
<field name="inherit" ref="reporting_main_view_graph_number"/>
|
||||
<field name="name">purchase_reporting_supplier_graph_number</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.act_window" id="act_reporting_supplier">
|
||||
<field name="name">Purchases per Supplier</field>
|
||||
<field name="res_model">purchase.reporting.supplier</field>
|
||||
<field name="context_model">purchase.reporting.context</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_reporting_supplier_view1">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="reporting_supplier_view_list"/>
|
||||
<field name="act_window" ref="act_reporting_supplier"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_reporting_supplier_view2">
|
||||
<field name="sequence" eval="20"/>
|
||||
<field name="view" ref="reporting_supplier_view_graph_expense"/>
|
||||
<field name="act_window" ref="act_reporting_supplier"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_reporting_supplier_view3">
|
||||
<field name="sequence" eval="30"/>
|
||||
<field name="view" ref="reporting_supplier_view_graph_number"/>
|
||||
<field name="act_window" ref="act_reporting_supplier"/>
|
||||
</record>
|
||||
<record model="ir.action.keyword" id="act_reporting_supplier_keyword1">
|
||||
<field name="keyword">tree_open</field>
|
||||
<field name="model" ref="menu_reporting_purchase"/>
|
||||
<field name="action" ref="act_reporting_supplier"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.rule.group" id="rule_group_reporting_supplier_companies">
|
||||
<field name="name">User in companies</field>
|
||||
<field name="model">purchase.reporting.supplier</field>
|
||||
<field name="global_p" eval="True"/>
|
||||
</record>
|
||||
<record model="ir.rule" id="rule_reporting_supplier_companies">
|
||||
<field name="domain"
|
||||
eval="[('company', 'in', Eval('companies', []))]"
|
||||
pyson="1"/>
|
||||
<field name="rule_group" ref="rule_group_reporting_supplier_companies"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.access" id="access_reporting_supplier">
|
||||
<field name="model">purchase.reporting.supplier</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_supplier_purchase">
|
||||
<field name="model">purchase.reporting.supplier</field>
|
||||
<field name="group" ref="group_purchase"/>
|
||||
<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_supplier_time_series_view_list">
|
||||
<field name="model">purchase.reporting.supplier.time_series</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="name">purchase_reporting_main_time_series_list</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="reporting_supplier_time_series_view_graph_expense">
|
||||
<field name="model">purchase.reporting.supplier.time_series</field>
|
||||
<field name="type">graph</field>
|
||||
<field name="name">purchase_reporting_main_time_series_graph_expense</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="reporting_supplier_time_series_view_graph_number">
|
||||
<field name="model">purchase.reporting.supplier.time_series</field>
|
||||
<field name="type">graph</field>
|
||||
<field name="name">purchase_reporting_main_time_series_graph_number</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.act_window" id="act_reporting_supplier_time_series">
|
||||
<field name="name">Purchases per Supplier</field>
|
||||
<field name="res_model">purchase.reporting.supplier.time_series</field>
|
||||
<field name="context_model">purchase.reporting.context</field>
|
||||
<field
|
||||
name="domain"
|
||||
eval="[('supplier_currency', '=', 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_supplier_time_series_list_view1">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="reporting_supplier_time_series_view_list"/>
|
||||
<field name="act_window" ref="act_reporting_supplier_time_series"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_reporting_supplier_time_series_list_view2">
|
||||
<field name="sequence" eval="20"/>
|
||||
<field name="view" ref="reporting_supplier_time_series_view_graph_expense"/>
|
||||
<field name="act_window" ref="act_reporting_supplier_time_series"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_reporting_supplier_time_series_list_view3">
|
||||
<field name="sequence" eval="30"/>
|
||||
<field name="view" ref="reporting_supplier_time_series_view_graph_number"/>
|
||||
<field name="act_window" ref="act_reporting_supplier_time_series"/>
|
||||
</record>
|
||||
<record model="ir.action.keyword" id="act_reporting_supplier_time_series_list_keyword1">
|
||||
<field name="keyword">tree_open</field>
|
||||
<field name="model">purchase.reporting.supplier,-1</field>
|
||||
<field name="action" ref="act_reporting_supplier_time_series"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.rule.group" id="rule_group_reporting_supplier_time_series_companies">
|
||||
<field name="name">User in companies</field>
|
||||
<field name="model">purchase.reporting.supplier.time_series</field>
|
||||
<field name="global_p" eval="True"/>
|
||||
</record>
|
||||
<record model="ir.rule" id="rule_reporting_supplier_time_series_companies">
|
||||
<field name="domain"
|
||||
eval="[('company', 'in', Eval('companies', []))]"
|
||||
pyson="1"/>
|
||||
<field name="rule_group" ref="rule_group_reporting_supplier_time_series_companies"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.access" id="access_reporting_supplier_time_series">
|
||||
<field name="model">purchase.reporting.supplier.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_supplier_time_series_purchase">
|
||||
<field name="model">purchase.reporting.supplier.time_series</field>
|
||||
<field name="group" ref="group_purchase"/>
|
||||
<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_product_view_list">
|
||||
<field name="model">purchase.reporting.product</field>
|
||||
<field name="inherit" ref="reporting_main_view_list"/>
|
||||
<field name="name">purchase_reporting_product_list</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="reporting_product_view_graph_expense">
|
||||
<field name="model">purchase.reporting.product</field>
|
||||
<field name="inherit" ref="reporting_main_view_graph_expense"/>
|
||||
<field name="name">purchase_reporting_product_graph_expense</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="reporting_product_view_graph_number">
|
||||
<field name="model">purchase.reporting.product</field>
|
||||
<field name="inherit" ref="reporting_main_view_graph_number"/>
|
||||
<field name="name">purchase_reporting_product_graph_number</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.act_window" id="act_reporting_product">
|
||||
<field name="name">Purchases per Product</field>
|
||||
<field name="res_model">purchase.reporting.product</field>
|
||||
<field name="context_model">purchase.reporting.context</field>
|
||||
<field name="context" eval="{'supplier_currency': Eval('active_id')}" pyson="1"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_reporting_product_view1">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="reporting_product_view_list"/>
|
||||
<field name="act_window" ref="act_reporting_product"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_reporting_product_view2">
|
||||
<field name="sequence" eval="20"/>
|
||||
<field name="view" ref="reporting_product_view_graph_expense"/>
|
||||
<field name="act_window" ref="act_reporting_product"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_reporting_product_view3">
|
||||
<field name="sequence" eval="30"/>
|
||||
<field name="view" ref="reporting_product_view_graph_number"/>
|
||||
<field name="act_window" ref="act_reporting_product"/>
|
||||
</record>
|
||||
<record model="ir.action.keyword" id="act_reporting_product_keyword1">
|
||||
<field name="keyword">tree_open</field>
|
||||
<field name="model">purchase.reporting.supplier,-1</field>
|
||||
<field name="action" ref="act_reporting_product"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.rule.group" id="rule_group_reporting_product_companies">
|
||||
<field name="name">User in companies</field>
|
||||
<field name="model">purchase.reporting.product</field>
|
||||
<field name="global_p" eval="True"/>
|
||||
</record>
|
||||
<record model="ir.rule" id="rule_reporting_product_companies">
|
||||
<field name="domain"
|
||||
eval="[('company', 'in', Eval('companies', []))]"
|
||||
pyson="1"/>
|
||||
<field name="rule_group" ref="rule_group_reporting_product_companies"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.access" id="access_reporting_product">
|
||||
<field name="model">purchase.reporting.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_product_purchase">
|
||||
<field name="model">purchase.reporting.product</field>
|
||||
<field name="group" ref="group_purchase"/>
|
||||
<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_product_time_series_view_list">
|
||||
<field name="model">purchase.reporting.product.time_series</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="name">purchase_reporting_main_time_series_list</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="reporting_product_time_series_view_graph_expense">
|
||||
<field name="model">purchase.reporting.product.time_series</field>
|
||||
<field name="type">graph</field>
|
||||
<field name="name">purchase_reporting_main_time_series_graph_expense</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="reporting_product_time_series_view_graph_number">
|
||||
<field name="model">purchase.reporting.product.time_series</field>
|
||||
<field name="type">graph</field>
|
||||
<field name="name">purchase_reporting_main_time_series_graph_number</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.act_window" id="act_reporting_product_time_series">
|
||||
<field name="name">Purchases per Product</field>
|
||||
<field name="res_model">purchase.reporting.product.time_series</field>
|
||||
<field name="context_model">purchase.reporting.context</field>
|
||||
<field name="domain"
|
||||
eval="[('product_currency', '=', 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_product_time_series_list_view1">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="reporting_product_time_series_view_list"/>
|
||||
<field name="act_window" ref="act_reporting_product_time_series"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_reporting_product_time_series_list_view2">
|
||||
<field name="sequence" eval="20"/>
|
||||
<field name="view" ref="reporting_product_time_series_view_graph_expense"/>
|
||||
<field name="act_window" ref="act_reporting_product_time_series"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_reporting_product_time_series_list_view3">
|
||||
<field name="sequence" eval="30"/>
|
||||
<field name="view" ref="reporting_product_time_series_view_graph_number"/>
|
||||
<field name="act_window" ref="act_reporting_product_time_series"/>
|
||||
</record>
|
||||
<record model="ir.action.keyword" id="act_reporting_product_time_series_list_keyword1">
|
||||
<field name="keyword">tree_open</field>
|
||||
<field name="model">purchase.reporting.product,-1</field>
|
||||
<field name="action" ref="act_reporting_product_time_series"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.rule.group" id="rule_group_reporting_product_time_series_companies">
|
||||
<field name="name">User in companies</field>
|
||||
<field name="model">purchase.reporting.product.time_series</field>
|
||||
<field name="global_p" eval="True"/>
|
||||
</record>
|
||||
<record model="ir.rule" id="rule_reporting_product_time_series_companies">
|
||||
<field name="domain"
|
||||
eval="[('company', 'in', Eval('companies', []))]"
|
||||
pyson="1"/>
|
||||
<field name="rule_group" ref="rule_group_reporting_product_time_series_companies"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.access" id="access_reporting_product_time_series">
|
||||
<field name="model">purchase.reporting.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_product_time_series_purchase">
|
||||
<field name="model">purchase.reporting.product.time_series</field>
|
||||
<field name="group" ref="group_purchase"/>
|
||||
<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>
|
||||
270
modules/purchase/stock.py
Normal file
270
modules/purchase/stock.py
Normal file
@@ -0,0 +1,270 @@
|
||||
# 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 functools import wraps
|
||||
|
||||
from trytond.i18n import gettext
|
||||
from trytond.model import ModelView, Workflow, fields
|
||||
from trytond.model.exceptions import AccessError
|
||||
from trytond.pool import Pool, PoolMeta
|
||||
from trytond.pyson import Eval
|
||||
from trytond.tools import cached_property
|
||||
from trytond.transaction import Transaction, without_check_access
|
||||
|
||||
|
||||
def process_purchase(moves_field):
|
||||
def _process_purchase(func):
|
||||
@wraps(func)
|
||||
def wrapper(cls, shipments):
|
||||
pool = Pool()
|
||||
Purchase = pool.get('purchase.purchase')
|
||||
transaction = Transaction()
|
||||
context = transaction.context
|
||||
with without_check_access():
|
||||
purchases = set(m.purchase for s in cls.browse(shipments)
|
||||
for m in getattr(s, moves_field) if m.purchase)
|
||||
result = func(cls, shipments)
|
||||
if purchases:
|
||||
with transaction.set_context(
|
||||
queue_batch=context.get('queue_batch', True)):
|
||||
Purchase.__queue__.process(purchases)
|
||||
return result
|
||||
return wrapper
|
||||
return _process_purchase
|
||||
|
||||
|
||||
class ShipmentIn(metaclass=PoolMeta):
|
||||
__name__ = 'stock.shipment.in'
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super().__setup__()
|
||||
add_remove = [
|
||||
('supplier', '=', Eval('supplier')),
|
||||
]
|
||||
if not cls.incoming_moves.add_remove:
|
||||
cls.incoming_moves.add_remove = add_remove
|
||||
else:
|
||||
cls.incoming_moves.add_remove = [
|
||||
add_remove,
|
||||
cls.incoming_moves.add_remove,
|
||||
]
|
||||
cls.incoming_moves.depends.add('supplier')
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
@Workflow.transition('draft')
|
||||
def draft(cls, shipments):
|
||||
PurchaseLine = Pool().get('purchase.line')
|
||||
for shipment in shipments:
|
||||
for move in shipment.incoming_moves:
|
||||
if (move.state == 'cancelled'
|
||||
and isinstance(move.origin, PurchaseLine)):
|
||||
raise AccessError(
|
||||
gettext('purchase.msg_purchase_move_reset_draft',
|
||||
move=move.rec_name))
|
||||
|
||||
return super().draft(shipments)
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
@Workflow.transition('received')
|
||||
@process_purchase('incoming_moves')
|
||||
def receive(cls, shipments):
|
||||
pool = Pool()
|
||||
PurchaseLine = pool.get('purchase.line')
|
||||
for shipment in shipments:
|
||||
for move in shipment.incoming_moves:
|
||||
if isinstance(move.origin, PurchaseLine):
|
||||
move.origin.check_move_quantity()
|
||||
super().receive(shipments)
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
@Workflow.transition('cancelled')
|
||||
@process_purchase('incoming_moves')
|
||||
def cancel(cls, shipments):
|
||||
super().cancel(shipments)
|
||||
|
||||
|
||||
class ShipmentInReturn(metaclass=PoolMeta):
|
||||
__name__ = 'stock.shipment.in.return'
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
@Workflow.transition('draft')
|
||||
def draft(cls, shipments):
|
||||
PurchaseLine = Pool().get('purchase.line')
|
||||
for shipment in shipments:
|
||||
for move in shipment.moves:
|
||||
if (move.state == 'cancelled'
|
||||
and isinstance(move.origin, PurchaseLine)):
|
||||
raise AccessError(
|
||||
gettext('purchase.msg_purchase_move_reset_draft',
|
||||
move=move.rec_name))
|
||||
|
||||
return super().draft(shipments)
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
@Workflow.transition('done')
|
||||
@process_purchase('moves')
|
||||
def do(cls, shipments):
|
||||
super().do(shipments)
|
||||
|
||||
|
||||
def process_purchase_move(without_shipment=False):
|
||||
def _process_purchase_move(func):
|
||||
@wraps(func)
|
||||
def wrapper(cls, moves):
|
||||
pool = Pool()
|
||||
Purchase = pool.get('purchase.purchase')
|
||||
transaction = Transaction()
|
||||
context = transaction.context
|
||||
with without_check_access():
|
||||
p_moves = cls.browse(moves)
|
||||
if without_shipment:
|
||||
p_moves = [m for m in p_moves if not m.shipment]
|
||||
purchases = set(m.purchase for m in p_moves if m.purchase)
|
||||
if purchases:
|
||||
with transaction.set_context(
|
||||
queue_batch=context.get('queue_batch', True)):
|
||||
Purchase.__queue__.process(purchases)
|
||||
return func(cls, moves)
|
||||
return wrapper
|
||||
return _process_purchase_move
|
||||
|
||||
|
||||
class Move(metaclass=PoolMeta):
|
||||
__name__ = 'stock.move'
|
||||
purchase = fields.Function(
|
||||
fields.Many2One('purchase.purchase', 'Purchase'),
|
||||
'get_purchase', searcher='search_purchase')
|
||||
supplier = fields.Function(fields.Many2One(
|
||||
'party.party', 'Supplier',
|
||||
context={
|
||||
'company': Eval('company', -1),
|
||||
},
|
||||
depends={'company'}),
|
||||
'get_supplier', searcher='search_supplier')
|
||||
purchase_exception_state = fields.Function(fields.Selection([
|
||||
('', ''),
|
||||
('ignored', 'Ignored'),
|
||||
('recreated', 'Recreated'),
|
||||
], 'Exception State'), 'get_purchase_exception_state')
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super().__setup__()
|
||||
if not cls.origin.domain:
|
||||
cls.origin.domain = {}
|
||||
cls.origin.domain['purchase.line'] = [
|
||||
('type', '=', 'line'),
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def _get_origin(cls):
|
||||
models = super()._get_origin()
|
||||
models.append('purchase.line')
|
||||
return models
|
||||
|
||||
@classmethod
|
||||
def check_origin_types(cls):
|
||||
types = super().check_origin_types()
|
||||
types.add('supplier')
|
||||
return types
|
||||
|
||||
def get_purchase(self, name):
|
||||
PurchaseLine = Pool().get('purchase.line')
|
||||
if isinstance(self.origin, PurchaseLine):
|
||||
return self.origin.purchase.id
|
||||
|
||||
@classmethod
|
||||
def search_purchase(cls, name, clause):
|
||||
return [('origin.' + clause[0],) + tuple(clause[1:3])
|
||||
+ ('purchase.line',) + tuple(clause[3:])]
|
||||
|
||||
def get_purchase_exception_state(self, name):
|
||||
PurchaseLine = Pool().get('purchase.line')
|
||||
if not isinstance(self.origin, PurchaseLine):
|
||||
return ''
|
||||
if self in self.origin.moves_recreated:
|
||||
return 'recreated'
|
||||
if self in self.origin.moves_ignored:
|
||||
return 'ignored'
|
||||
|
||||
def get_supplier(self, name):
|
||||
PurchaseLine = Pool().get('purchase.line')
|
||||
if isinstance(self.origin, PurchaseLine):
|
||||
return self.origin.purchase.party.id
|
||||
|
||||
@cached_property
|
||||
def product_name(self):
|
||||
pool = Pool()
|
||||
PurchaseLine = pool.get('purchase.line')
|
||||
name = super().product_name
|
||||
if (isinstance(self.origin, PurchaseLine)
|
||||
and self.origin.product_supplier):
|
||||
name = self.origin.product_supplier.rec_name
|
||||
return name
|
||||
|
||||
@fields.depends('origin')
|
||||
def on_change_with_product_uom_category(self, name=None):
|
||||
pool = Pool()
|
||||
PurchaseLine = pool.get('purchase.line')
|
||||
category = super().on_change_with_product_uom_category(
|
||||
name=name)
|
||||
# Enforce the same unit category as they are used to compute the
|
||||
# remaining quantity to receive and the quantity to invoice.
|
||||
# Use getattr as reference field can have negative id
|
||||
if (isinstance(self.origin, PurchaseLine)
|
||||
and getattr(self.origin, 'unit', None)):
|
||||
category = self.origin.unit.category
|
||||
return category
|
||||
|
||||
@property
|
||||
def origin_name(self):
|
||||
pool = Pool()
|
||||
PurchaseLine = pool.get('purchase.line')
|
||||
name = super().origin_name
|
||||
if isinstance(self.origin, PurchaseLine) and self.origin.id >= 0:
|
||||
name = self.origin.purchase.rec_name
|
||||
return name
|
||||
|
||||
@classmethod
|
||||
def search_supplier(cls, name, clause):
|
||||
return [('origin.purchase.party' + clause[0][len(name):],
|
||||
*clause[1:3], 'purchase.line', *clause[3:])]
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
@Workflow.transition('done')
|
||||
@process_purchase_move(without_shipment=True)
|
||||
def do(cls, moves):
|
||||
super().do(moves)
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
@Workflow.transition('cancelled')
|
||||
@process_purchase_move(without_shipment=True)
|
||||
def cancel(cls, moves):
|
||||
super().cancel(moves)
|
||||
|
||||
@classmethod
|
||||
@process_purchase_move()
|
||||
def on_delete(cls, moves):
|
||||
return super().on_delete(moves)
|
||||
|
||||
|
||||
class Location(metaclass=PoolMeta):
|
||||
__name__ = 'stock.location'
|
||||
|
||||
supplier_return_location = fields.Many2One(
|
||||
'stock.location', 'Supplier Return',
|
||||
states={
|
||||
'invisible': Eval('type') != 'warehouse',
|
||||
},
|
||||
domain=[
|
||||
('type', '=', 'storage'),
|
||||
('parent', 'child_of', [Eval('id', -1)]),
|
||||
],
|
||||
help='If empty the Storage location is used.')
|
||||
114
modules/purchase/stock.xml
Normal file
114
modules/purchase/stock.xml
Normal file
@@ -0,0 +1,114 @@
|
||||
<?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_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.model.access" id="access_move_group_purchase">
|
||||
<field name="model">stock.move</field>
|
||||
<field name="group" ref="group_purchase"/>
|
||||
<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="location_view_form">
|
||||
<field name="model">stock.location</field>
|
||||
<field name="inherit" ref="stock.location_view_form"/>
|
||||
<field name="name">location_form</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="move_view_list_shipment_in">
|
||||
<field name="model">stock.move</field>
|
||||
<field name="inherit" ref="stock.move_view_list_shipment_in"/>
|
||||
<field name="name">move_list_shipment_in</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.act_window" id="act_purchase_move_relate">
|
||||
<field name="name">Stock Moves</field>
|
||||
<field name="res_model">stock.move</field>
|
||||
<field
|
||||
name="domain"
|
||||
eval="[
|
||||
If(Eval('active_model') == 'purchase.purchase',
|
||||
('purchase', 'in', Eval('active_ids', [])), ()),
|
||||
If(Eval('active_model') == 'purchase.line',
|
||||
('origin.id', 'in', Eval('active_ids', []), 'purchase.line'), ()),
|
||||
]"
|
||||
pyson="1"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_move_form_view1">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="move_view_list_shipment"/>
|
||||
<field name="act_window" ref="act_purchase_move_relate"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_move_form_view2">
|
||||
<field name="sequence" eval="20"/>
|
||||
<field name="view" ref="stock.move_view_form"/>
|
||||
<field name="act_window" ref="act_purchase_move_relate"/>
|
||||
</record>
|
||||
<record model="ir.action.keyword" id="act_move_form_keyword_purchase">
|
||||
<field name="keyword">form_relate</field>
|
||||
<field name="model">purchase.purchase,-1</field>
|
||||
<field name="action" ref="act_purchase_move_relate"/>
|
||||
</record>
|
||||
<record model="ir.action.keyword" id="act_move_form_keyword_purchase_line">
|
||||
<field name="keyword">form_relate</field>
|
||||
<field name="model">purchase.line,-1</field>
|
||||
<field name="action" ref="act_purchase_move_relate"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.act_window" id="act_shipment_form">
|
||||
<field name="name">Shipments</field>
|
||||
<field name="res_model">stock.shipment.in</field>
|
||||
<field name="domain"
|
||||
eval="[('moves.purchase', 'in', Eval('active_ids'))]"
|
||||
pyson="1"/>
|
||||
</record>
|
||||
<record model="ir.action.keyword"
|
||||
id="act_open_shipment_keyword1">
|
||||
<field name="keyword">form_relate</field>
|
||||
<field name="model">purchase.purchase,-1</field>
|
||||
<field name="action" ref="act_shipment_form"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.access" id="access_shipment_in_group_purchase">
|
||||
<field name="model">stock.shipment.in</field>
|
||||
<field name="group" ref="group_purchase"/>
|
||||
<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.action.act_window" id="act_return_form">
|
||||
<field name="name">Returns</field>
|
||||
<field name="res_model">stock.shipment.in.return</field>
|
||||
<field name="domain"
|
||||
eval="[('moves.purchase', 'in', Eval('active_ids'))]"
|
||||
pyson="1"/>
|
||||
</record>
|
||||
<record model="ir.action.keyword"
|
||||
id="act_open_shipment_return_keyword1">
|
||||
<field name="keyword">form_relate</field>
|
||||
<field name="model">purchase.purchase,-1</field>
|
||||
<field name="action" ref="act_return_form"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.access" id="access_shipment_in_return_group_purchase">
|
||||
<field name="model">stock.shipment.in.return</field>
|
||||
<field name="group" ref="group_purchase"/>
|
||||
<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>
|
||||
2
modules/purchase/tests/__init__.py
Normal file
2
modules/purchase/tests/__init__.py
Normal 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.
|
||||
BIN
modules/purchase/tests/__pycache__/__init__.cpython-311.pyc
Normal file
BIN
modules/purchase/tests/__pycache__/__init__.cpython-311.pyc
Normal file
Binary file not shown.
BIN
modules/purchase/tests/__pycache__/test_module.cpython-311.pyc
Normal file
BIN
modules/purchase/tests/__pycache__/test_module.cpython-311.pyc
Normal file
Binary file not shown.
BIN
modules/purchase/tests/__pycache__/test_scenario.cpython-311.pyc
Normal file
BIN
modules/purchase/tests/__pycache__/test_scenario.cpython-311.pyc
Normal file
Binary file not shown.
621
modules/purchase/tests/scenario_purchase.rst
Normal file
621
modules/purchase/tests/scenario_purchase.rst
Normal file
@@ -0,0 +1,621 @@
|
||||
=================
|
||||
Purchase Scenario
|
||||
=================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> import datetime as dt
|
||||
>>> from decimal import Decimal
|
||||
|
||||
>>> from proteus import Model, Report
|
||||
>>> from trytond.modules.account.tests.tools import (
|
||||
... create_chart, create_fiscalyear, create_tax, get_accounts)
|
||||
>>> from trytond.modules.account_invoice.tests.tools import (
|
||||
... create_payment_term, set_fiscalyear_invoice_sequences)
|
||||
>>> from trytond.modules.company.tests.tools import create_company
|
||||
>>> from trytond.tests.tools import activate_modules, assertEqual, set_user
|
||||
|
||||
>>> today = dt.date.today()
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> config = activate_modules('purchase', create_company, create_chart)
|
||||
|
||||
>>> 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 fiscal year::
|
||||
|
||||
>>> fiscalyear = set_fiscalyear_invoice_sequences(
|
||||
... create_fiscalyear(today=today))
|
||||
>>> fiscalyear.click('create_period')
|
||||
|
||||
Get accounts::
|
||||
|
||||
>>> accounts = get_accounts()
|
||||
>>> revenue = accounts['revenue']
|
||||
>>> expense = accounts['expense']
|
||||
>>> cash = accounts['cash']
|
||||
|
||||
>>> Journal = Model.get('account.journal')
|
||||
>>> PaymentMethod = Model.get('account.invoice.payment.method')
|
||||
>>> cash_journal, = Journal.find([('type', '=', 'cash')])
|
||||
>>> cash_journal.save()
|
||||
>>> payment_method = PaymentMethod()
|
||||
>>> payment_method.name = 'Cash'
|
||||
>>> payment_method.journal = cash_journal
|
||||
>>> payment_method.credit_account = cash
|
||||
>>> payment_method.debit_account = cash
|
||||
>>> payment_method.save()
|
||||
|
||||
Create tax::
|
||||
|
||||
>>> tax = create_tax(Decimal('.10'))
|
||||
>>> tax.save()
|
||||
|
||||
Create parties::
|
||||
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> supplier = Party(name='Supplier')
|
||||
>>> supplier.customer_code = '1234'
|
||||
>>> supplier.save()
|
||||
>>> customer = Party(name='Customer')
|
||||
>>> customer.save()
|
||||
|
||||
Create account categories::
|
||||
|
||||
>>> ProductCategory = Model.get('product.category')
|
||||
>>> account_category = ProductCategory(name="Account Category")
|
||||
>>> account_category.accounting = True
|
||||
>>> account_category.account_expense = expense
|
||||
>>> account_category.account_revenue = revenue
|
||||
>>> account_category.save()
|
||||
|
||||
>>> account_category_tax, = account_category.duplicate()
|
||||
>>> account_category_tax.supplier_taxes.append(tax)
|
||||
>>> account_category_tax.save()
|
||||
|
||||
Create product::
|
||||
|
||||
>>> ProductUom = Model.get('product.uom')
|
||||
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
|
||||
>>> ProductTemplate = Model.get('product.template')
|
||||
|
||||
>>> template = ProductTemplate()
|
||||
>>> template.name = 'product'
|
||||
>>> template.default_uom = unit
|
||||
>>> template.type = 'goods'
|
||||
>>> template.purchasable = True
|
||||
>>> template.list_price = Decimal('10')
|
||||
>>> template.cost_price_method = 'fixed'
|
||||
>>> template.account_category = account_category_tax
|
||||
>>> template.save()
|
||||
>>> product, = template.products
|
||||
|
||||
>>> template = ProductTemplate()
|
||||
>>> template.name = 'service'
|
||||
>>> template.default_uom = unit
|
||||
>>> template.type = 'service'
|
||||
>>> template.purchasable = True
|
||||
>>> template.list_price = Decimal('10')
|
||||
>>> template.cost_price_method = 'fixed'
|
||||
>>> template.account_category = account_category
|
||||
>>> template.save()
|
||||
>>> service, = template.products
|
||||
|
||||
Create payment term::
|
||||
|
||||
>>> payment_term = create_payment_term()
|
||||
>>> payment_term.save()
|
||||
|
||||
Create an Inventory::
|
||||
|
||||
>>> Inventory = Model.get('stock.inventory')
|
||||
>>> Location = Model.get('stock.location')
|
||||
>>> storage, = Location.find([
|
||||
... ('code', '=', 'STO'),
|
||||
... ])
|
||||
>>> inventory = Inventory()
|
||||
>>> inventory.location = storage
|
||||
>>> inventory_line = inventory.lines.new(product=product)
|
||||
>>> inventory_line.quantity = 100.0
|
||||
>>> inventory_line.expected_quantity = 0.0
|
||||
>>> inventory.click('confirm')
|
||||
>>> inventory.state
|
||||
'done'
|
||||
|
||||
Purchase 5 products::
|
||||
|
||||
>>> Purchase = Model.get('purchase.purchase')
|
||||
>>> PurchaseLine = Model.get('purchase.line')
|
||||
>>> purchase = Purchase()
|
||||
>>> purchase.party = supplier
|
||||
>>> purchase.payment_term = payment_term
|
||||
>>> purchase.invoice_method = 'order'
|
||||
>>> purchase_line = PurchaseLine()
|
||||
>>> purchase.lines.append(purchase_line)
|
||||
>>> purchase_line.product = product
|
||||
>>> purchase_line.quantity = 2.0
|
||||
>>> purchase_line.unit_price = Decimal('5.0000')
|
||||
>>> purchase_line = PurchaseLine()
|
||||
>>> purchase.lines.append(purchase_line)
|
||||
>>> purchase_line.type = 'comment'
|
||||
>>> purchase_line.description = 'Comment'
|
||||
>>> purchase_line = PurchaseLine()
|
||||
>>> purchase.lines.append(purchase_line)
|
||||
>>> purchase_line.product = product
|
||||
>>> purchase_line.quantity = 3.0
|
||||
>>> purchase_line.unit_price = Decimal('5.0000')
|
||||
>>> purchase.click('quote')
|
||||
>>> purchase.untaxed_amount, purchase.tax_amount, purchase.total_amount
|
||||
(Decimal('25.00'), Decimal('2.50'), Decimal('27.50'))
|
||||
>>> assertEqual(purchase.quoted_by, employee)
|
||||
>>> purchase.click('confirm')
|
||||
>>> purchase.untaxed_amount, purchase.tax_amount, purchase.total_amount
|
||||
(Decimal('25.00'), Decimal('2.50'), Decimal('27.50'))
|
||||
>>> assertEqual(purchase.confirmed_by, employee)
|
||||
>>> purchase.state
|
||||
'processing'
|
||||
>>> purchase.shipment_state
|
||||
'waiting'
|
||||
>>> purchase.invoice_state
|
||||
'pending'
|
||||
>>> len(purchase.moves), len(purchase.shipment_returns), len(purchase.invoices)
|
||||
(2, 0, 1)
|
||||
>>> invoice, = purchase.invoices
|
||||
>>> assertEqual(invoice.origins, purchase.rec_name)
|
||||
>>> invoice.untaxed_amount, invoice.tax_amount, invoice.total_amount
|
||||
(Decimal('25.00'), Decimal('2.50'), Decimal('27.50'))
|
||||
|
||||
Invoice line must be linked to stock move::
|
||||
|
||||
>>> invoice_line1, invoice_line2 = sorted(
|
||||
... invoice.lines, key=lambda l: l.quantity or 0)
|
||||
>>> stock_move1, stock_move2 = sorted(purchase.moves,
|
||||
... key=lambda m: m.quantity)
|
||||
>>> assertEqual(invoice_line1.stock_moves, [stock_move1])
|
||||
>>> assertEqual(stock_move1.invoice_lines, [invoice_line1])
|
||||
>>> assertEqual(invoice_line2.stock_moves, [stock_move2])
|
||||
>>> assertEqual(stock_move2.invoice_lines, [invoice_line2])
|
||||
|
||||
Check actual quantity::
|
||||
|
||||
>>> for line in purchase.lines:
|
||||
... assertEqual(line.quantity, line.actual_quantity)
|
||||
|
||||
Post invoice and check no new invoices::
|
||||
|
||||
>>> invoice.invoice_date = today
|
||||
>>> invoice.click('post')
|
||||
>>> purchase.reload()
|
||||
>>> purchase.shipment_state
|
||||
'waiting'
|
||||
>>> purchase.invoice_state
|
||||
'awaiting payment'
|
||||
>>> len(purchase.moves), len(purchase.shipment_returns), len(purchase.invoices)
|
||||
(2, 0, 1)
|
||||
|
||||
Purchase 5 products with an invoice method 'on shipment'::
|
||||
|
||||
>>> purchase = Purchase()
|
||||
>>> purchase.party = supplier
|
||||
>>> purchase.payment_term = payment_term
|
||||
>>> purchase.invoice_method = 'shipment'
|
||||
>>> purchase_line = PurchaseLine()
|
||||
>>> purchase.lines.append(purchase_line)
|
||||
>>> purchase_line.product = product
|
||||
>>> purchase_line.quantity = 2.0
|
||||
>>> purchase_line = PurchaseLine()
|
||||
>>> purchase.lines.append(purchase_line)
|
||||
>>> purchase_line.type = 'comment'
|
||||
>>> purchase_line.description = 'Comment'
|
||||
>>> purchase_line = PurchaseLine()
|
||||
>>> purchase.lines.append(purchase_line)
|
||||
>>> purchase_line.product = product
|
||||
>>> purchase_line.quantity = 3.0
|
||||
>>> purchase.click('quote')
|
||||
>>> purchase.click('confirm')
|
||||
>>> purchase.state
|
||||
'processing'
|
||||
>>> purchase.shipment_state
|
||||
'waiting'
|
||||
>>> purchase.invoice_state
|
||||
'none'
|
||||
>>> len(purchase.moves), len(purchase.shipment_returns), len(purchase.invoices)
|
||||
(2, 0, 0)
|
||||
|
||||
Not yet linked to invoice lines::
|
||||
|
||||
>>> stock_move1, stock_move2 = sorted(purchase.moves,
|
||||
... key=lambda m: m.quantity)
|
||||
>>> len(stock_move1.invoice_lines)
|
||||
0
|
||||
>>> len(stock_move2.invoice_lines)
|
||||
0
|
||||
|
||||
Validate Shipments::
|
||||
|
||||
>>> Move = Model.get('stock.move')
|
||||
>>> ShipmentIn = Model.get('stock.shipment.in')
|
||||
>>> shipment = ShipmentIn()
|
||||
>>> shipment.supplier = supplier
|
||||
>>> for move in purchase.moves:
|
||||
... incoming_move = Move(id=move.id)
|
||||
... shipment.incoming_moves.append(incoming_move)
|
||||
>>> shipment.save()
|
||||
>>> assertEqual(shipment.origins, purchase.rec_name)
|
||||
>>> shipment.click('receive')
|
||||
>>> shipment.click('do')
|
||||
>>> purchase.reload()
|
||||
>>> purchase.shipment_state
|
||||
'received'
|
||||
>>> len(purchase.shipments), len(purchase.shipment_returns)
|
||||
(1, 0)
|
||||
|
||||
Open supplier invoice::
|
||||
|
||||
>>> purchase.invoice_state
|
||||
'pending'
|
||||
>>> invoice, = purchase.invoices
|
||||
>>> invoice.type
|
||||
'in'
|
||||
>>> invoice_line1, invoice_line2 = sorted(invoice.lines,
|
||||
... key=lambda l: l.quantity or 0)
|
||||
>>> for line in invoice.lines:
|
||||
... line.quantity = 1
|
||||
... line.save()
|
||||
>>> invoice.invoice_date = today
|
||||
>>> invoice.click('post')
|
||||
|
||||
Invoice lines must be linked to each stock moves::
|
||||
|
||||
>>> assertEqual(invoice_line1.stock_moves, [stock_move1])
|
||||
>>> assertEqual(invoice_line2.stock_moves, [stock_move2])
|
||||
|
||||
Check second invoices::
|
||||
|
||||
>>> purchase.reload()
|
||||
>>> len(purchase.invoices)
|
||||
2
|
||||
>>> sum(l.quantity for i in purchase.invoices for l in i.lines)
|
||||
5.0
|
||||
|
||||
Create the report::
|
||||
|
||||
>>> purchase_report = Report('purchase.purchase')
|
||||
>>> ext, _, _, name = purchase_report.execute([purchase], {})
|
||||
>>> ext
|
||||
'odt'
|
||||
>>> name
|
||||
'Purchase-2'
|
||||
|
||||
Create a Return::
|
||||
|
||||
>>> return_ = Purchase()
|
||||
>>> return_.party = supplier
|
||||
>>> return_.payment_term = payment_term
|
||||
>>> return_.invoice_method = 'shipment'
|
||||
>>> return_line = PurchaseLine()
|
||||
>>> return_.lines.append(return_line)
|
||||
>>> return_line.product = product
|
||||
>>> return_line.quantity = -4.
|
||||
>>> return_line = PurchaseLine()
|
||||
>>> return_.lines.append(return_line)
|
||||
>>> return_line.type = 'comment'
|
||||
>>> return_line.description = 'Comment'
|
||||
>>> return_.click('quote')
|
||||
>>> return_.click('confirm')
|
||||
>>> return_.state
|
||||
'processing'
|
||||
>>> return_.shipment_state
|
||||
'waiting'
|
||||
>>> return_.invoice_state
|
||||
'none'
|
||||
>>> (len(return_.shipments), len(return_.shipment_returns),
|
||||
... len(return_.invoices))
|
||||
(0, 1, 0)
|
||||
|
||||
Check Return Shipments::
|
||||
|
||||
>>> ShipmentReturn = Model.get('stock.shipment.in.return')
|
||||
>>> ship_return, = return_.shipment_returns
|
||||
>>> ship_return.state
|
||||
'waiting'
|
||||
>>> move_return, = ship_return.moves
|
||||
>>> move_return.product.rec_name
|
||||
'product'
|
||||
>>> move_return.quantity
|
||||
4.0
|
||||
>>> ship_return.click('assign_try')
|
||||
>>> ship_return.click('do')
|
||||
>>> ship_return.state
|
||||
'done'
|
||||
>>> return_.reload()
|
||||
>>> return_.state
|
||||
'processing'
|
||||
>>> return_.shipment_state
|
||||
'received'
|
||||
>>> return_.invoice_state
|
||||
'pending'
|
||||
|
||||
Open supplier credit note::
|
||||
|
||||
>>> credit_note, = return_.invoices
|
||||
>>> credit_note.type
|
||||
'in'
|
||||
>>> len(credit_note.lines)
|
||||
1
|
||||
>>> sum(l.quantity for l in credit_note.lines)
|
||||
-4.0
|
||||
>>> credit_note.invoice_date = today
|
||||
>>> credit_note.click('post')
|
||||
|
||||
Mixing return and purchase::
|
||||
|
||||
>>> mix = Purchase()
|
||||
>>> mix.party = supplier
|
||||
>>> mix.payment_term = payment_term
|
||||
>>> mix.invoice_method = 'order'
|
||||
>>> mixline = PurchaseLine()
|
||||
>>> mix.lines.append(mixline)
|
||||
>>> mixline.product = product
|
||||
>>> mixline.quantity = 7.
|
||||
>>> mixline_comment = PurchaseLine()
|
||||
>>> mix.lines.append(mixline_comment)
|
||||
>>> mixline_comment.type = 'comment'
|
||||
>>> mixline_comment.description = 'Comment'
|
||||
>>> mixline2 = PurchaseLine()
|
||||
>>> mix.lines.append(mixline2)
|
||||
>>> mixline2.product = product
|
||||
>>> mixline2.quantity = -2.
|
||||
>>> mix.click('quote')
|
||||
>>> mix.click('confirm')
|
||||
>>> mix.state
|
||||
'processing'
|
||||
>>> mix.shipment_state
|
||||
'waiting'
|
||||
>>> mix.invoice_state
|
||||
'pending'
|
||||
>>> len(mix.moves), len(mix.shipment_returns), len(mix.invoices)
|
||||
(2, 1, 1)
|
||||
|
||||
Checking Shipments::
|
||||
|
||||
>>> mix_return, = mix.shipment_returns
|
||||
>>> mix_shipment = ShipmentIn()
|
||||
>>> mix_shipment.supplier = supplier
|
||||
>>> for move in mix.moves:
|
||||
... if move.id in [m.id for m in mix_return.moves]:
|
||||
... continue
|
||||
... incoming_move = Move(id=move.id)
|
||||
... mix_shipment.incoming_moves.append(incoming_move)
|
||||
>>> mix_shipment.click('receive')
|
||||
>>> mix_shipment.click('do')
|
||||
>>> mix.reload()
|
||||
>>> len(mix.shipments)
|
||||
1
|
||||
|
||||
>>> mix_return.click('wait')
|
||||
>>> mix_return.click('assign_try')
|
||||
>>> mix_return.click('do')
|
||||
>>> move_return, = mix_return.moves
|
||||
>>> move_return.product.rec_name
|
||||
'product'
|
||||
>>> move_return.quantity
|
||||
2.0
|
||||
|
||||
Checking the invoice::
|
||||
|
||||
>>> mix.reload()
|
||||
>>> mix_invoice, = mix.invoices
|
||||
>>> mix_invoice.type
|
||||
'in'
|
||||
>>> len(mix_invoice.lines)
|
||||
2
|
||||
>>> sorted(l.quantity for l in mix_invoice.lines)
|
||||
[-2.0, 7.0]
|
||||
>>> mix_invoice.invoice_date = today
|
||||
>>> mix_invoice.click('post')
|
||||
|
||||
Mixing stuff with an invoice method 'on shipment'::
|
||||
|
||||
>>> mix = Purchase()
|
||||
>>> mix.party = supplier
|
||||
>>> mix.payment_term = payment_term
|
||||
>>> mix.invoice_method = 'shipment'
|
||||
>>> mixline = PurchaseLine()
|
||||
>>> mix.lines.append(mixline)
|
||||
>>> mixline.product = product
|
||||
>>> mixline.quantity = 6.
|
||||
>>> mixline_comment = PurchaseLine()
|
||||
>>> mix.lines.append(mixline_comment)
|
||||
>>> mixline_comment.type = 'comment'
|
||||
>>> mixline_comment.description = 'Comment'
|
||||
>>> mixline2 = PurchaseLine()
|
||||
>>> mix.lines.append(mixline2)
|
||||
>>> mixline2.product = product
|
||||
>>> mixline2.quantity = -3.
|
||||
>>> mix.click('quote')
|
||||
>>> mix.click('confirm')
|
||||
>>> mix.state
|
||||
'processing'
|
||||
>>> mix.shipment_state
|
||||
'waiting'
|
||||
>>> mix.invoice_state
|
||||
'none'
|
||||
>>> len(mix.moves), len(mix.shipment_returns), len(mix.invoices)
|
||||
(2, 1, 0)
|
||||
|
||||
Checking Shipments::
|
||||
|
||||
>>> mix_return, = mix.shipment_returns
|
||||
>>> mix_shipment = ShipmentIn()
|
||||
>>> mix_shipment.supplier = supplier
|
||||
>>> for move in mix.moves:
|
||||
... if move.id in [m.id for m in mix_return.moves]:
|
||||
... continue
|
||||
... incoming_move = Move(id=move.id)
|
||||
... mix_shipment.incoming_moves.append(incoming_move)
|
||||
>>> mix_shipment.click('receive')
|
||||
>>> mix_shipment.click('do')
|
||||
>>> mix.reload()
|
||||
>>> len(mix.shipments)
|
||||
1
|
||||
|
||||
>>> mix_return.click('wait')
|
||||
>>> mix_return.click('assign_try')
|
||||
>>> mix_return.click('do')
|
||||
>>> move_return, = mix_return.moves
|
||||
>>> move_return.product.rec_name
|
||||
'product'
|
||||
>>> move_return.quantity
|
||||
3.0
|
||||
|
||||
Purchase services::
|
||||
|
||||
>>> service_purchase = Purchase()
|
||||
>>> service_purchase.party = supplier
|
||||
>>> service_purchase.payment_term = payment_term
|
||||
>>> purchase_line = service_purchase.lines.new()
|
||||
>>> purchase_line.product = service
|
||||
>>> purchase_line.quantity = 1
|
||||
>>> purchase_line.unit_price = Decimal('10.0000')
|
||||
>>> service_purchase.save()
|
||||
>>> service_purchase.click('quote')
|
||||
>>> service_purchase.click('confirm')
|
||||
>>> service_purchase.state
|
||||
'processing'
|
||||
>>> service_purchase.shipment_state
|
||||
'none'
|
||||
>>> service_purchase.invoice_state
|
||||
'pending'
|
||||
>>> service_invoice, = service_purchase.invoices
|
||||
|
||||
Pay the service invoice::
|
||||
|
||||
>>> service_invoice.invoice_date = today
|
||||
>>> service_invoice.click('post')
|
||||
>>> pay = service_invoice.click('pay')
|
||||
>>> pay.form.payment_method = payment_method
|
||||
>>> pay.form.amount = service_invoice.total_amount
|
||||
>>> pay.execute('choice')
|
||||
>>> service_invoice.reload()
|
||||
>>> service_invoice.state
|
||||
'paid'
|
||||
|
||||
Check service purchase states::
|
||||
|
||||
>>> service_purchase.reload()
|
||||
>>> service_purchase.invoice_state
|
||||
'paid'
|
||||
>>> service_purchase.shipment_state
|
||||
'none'
|
||||
>>> service_purchase.state
|
||||
'done'
|
||||
|
||||
Create a purchase to be invoiced on shipment partially and check correctly
|
||||
linked to invoices::
|
||||
|
||||
>>> purchase = Purchase()
|
||||
>>> purchase.party = supplier
|
||||
>>> purchase.payment_term = payment_term
|
||||
>>> purchase.invoice_method = 'shipment'
|
||||
>>> line = purchase.lines.new()
|
||||
>>> line.product = product
|
||||
>>> line.quantity = 10.0
|
||||
>>> purchase.click('quote')
|
||||
>>> purchase.click('confirm')
|
||||
>>> shipment = ShipmentIn()
|
||||
>>> shipment.supplier = supplier
|
||||
>>> for move in purchase.moves:
|
||||
... incoming_move = Move(id=move.id)
|
||||
... incoming_move.quantity = 5.0
|
||||
... shipment.incoming_moves.append(incoming_move)
|
||||
>>> shipment.save()
|
||||
>>> for move in shipment.inventory_moves:
|
||||
... move.quantity = 5.0
|
||||
>>> shipment.click('receive')
|
||||
>>> shipment.click('do')
|
||||
>>> purchase.reload()
|
||||
>>> invoice, = purchase.invoices
|
||||
>>> invoice_line, = invoice.lines
|
||||
>>> invoice_line.quantity
|
||||
5.0
|
||||
>>> stock_move, = invoice_line.stock_moves
|
||||
>>> stock_move.quantity
|
||||
5.0
|
||||
>>> stock_move.state
|
||||
'done'
|
||||
|
||||
Create a purchase to be invoiced on order, partially send it and check
|
||||
correctly linked to invoices::
|
||||
|
||||
>>> purchase = Purchase()
|
||||
>>> purchase.party = supplier
|
||||
>>> purchase.payment_term = payment_term
|
||||
>>> purchase.invoice_method = 'order'
|
||||
>>> line = purchase.lines.new()
|
||||
>>> line.product = product
|
||||
>>> line.quantity = 10.0
|
||||
>>> purchase.click('quote')
|
||||
>>> purchase.click('confirm')
|
||||
>>> shipment = ShipmentIn()
|
||||
>>> shipment.supplier = supplier
|
||||
>>> for move in purchase.moves:
|
||||
... incoming_move = Move(id=move.id)
|
||||
... incoming_move.quantity = 8.0
|
||||
... shipment.incoming_moves.append(incoming_move)
|
||||
>>> shipment.save()
|
||||
>>> for move in shipment.inventory_moves:
|
||||
... move.quantity = 8.0
|
||||
>>> shipment.click('receive')
|
||||
>>> shipment.click('do')
|
||||
>>> purchase.reload()
|
||||
>>> invoice, = purchase.invoices
|
||||
>>> invoice_line, = invoice.lines
|
||||
>>> invoice_line.quantity
|
||||
10.0
|
||||
>>> draft_stock_move, stock_move = sorted(
|
||||
... invoice_line.stock_moves, key=lambda m: m.quantity)
|
||||
>>> draft_stock_move.quantity
|
||||
2.0
|
||||
>>> draft_stock_move.state
|
||||
'draft'
|
||||
>>> stock_move.quantity
|
||||
8.0
|
||||
>>> stock_move.state
|
||||
'done'
|
||||
|
||||
Deleting a line from a invoice should recreate it::
|
||||
|
||||
>>> purchase = Purchase()
|
||||
>>> purchase.party = customer
|
||||
>>> line = purchase.lines.new()
|
||||
>>> line.product = product
|
||||
>>> line.quantity = 10.0
|
||||
>>> line.unit_price = Decimal('5.0000')
|
||||
>>> purchase.click('quote')
|
||||
>>> purchase.click('confirm')
|
||||
>>> invoice, = purchase.invoices
|
||||
>>> invoice_line, = invoice.lines
|
||||
>>> invoice.lines.remove(invoice_line)
|
||||
>>> invoice.invoice_date = today
|
||||
>>> invoice.click('post')
|
||||
>>> purchase.reload()
|
||||
>>> new_invoice, = purchase.invoices
|
||||
>>> new_invoice.number
|
||||
>>> len(new_invoice.lines)
|
||||
1
|
||||
@@ -0,0 +1,52 @@
|
||||
========================================
|
||||
Purchase Copy Product Suppliers 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, assertEqual
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> config = activate_modules('purchase', create_company)
|
||||
|
||||
Create party::
|
||||
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> supplier = Party(name='Supplier')
|
||||
>>> supplier.save()
|
||||
|
||||
Create a product with suppliers::
|
||||
|
||||
>>> ProductUom = Model.get('product.uom')
|
||||
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
|
||||
>>> ProductTemplate = Model.get('product.template')
|
||||
|
||||
>>> template = ProductTemplate()
|
||||
>>> template.name = 'product'
|
||||
>>> template.default_uom = unit
|
||||
>>> template.type = 'goods'
|
||||
>>> template.purchasable = True
|
||||
>>> template.list_price = Decimal('10')
|
||||
>>> template.cost_price_method = 'fixed'
|
||||
>>> product_supplier = template.product_suppliers.new()
|
||||
>>> product_supplier.party = supplier
|
||||
>>> template.save()
|
||||
>>> product, = template.products
|
||||
>>> product_supplier = product.product_suppliers.new()
|
||||
>>> product_supplier.party = supplier
|
||||
>>> assertEqual(product_supplier.template, template)
|
||||
>>> product.save()
|
||||
|
||||
Supplier is copied when copying the template::
|
||||
|
||||
>>> template_copy, = template.duplicate()
|
||||
>>> product_copy, = template_copy.products
|
||||
>>> len(template_copy.product_suppliers)
|
||||
2
|
||||
>>> len(product_copy.product_suppliers)
|
||||
1
|
||||
59
modules/purchase/tests/scenario_purchase_default_taxes.rst
Normal file
59
modules/purchase/tests/scenario_purchase_default_taxes.rst
Normal file
@@ -0,0 +1,59 @@
|
||||
===========================
|
||||
Purchase with Default Taxes
|
||||
===========================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> from decimal import Decimal
|
||||
|
||||
>>> from proteus import Model
|
||||
>>> from trytond.modules.account.tests.tools import (
|
||||
... create_chart, create_tax, get_accounts)
|
||||
>>> from trytond.modules.company.tests.tools import create_company
|
||||
>>> from trytond.tests.tools import activate_modules, assertEqual
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> config = activate_modules('purchase', create_company, create_chart)
|
||||
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> Purchase = Model.get('purchase.purchase')
|
||||
>>> Tax = Model.get('account.tax')
|
||||
|
||||
Get accounts::
|
||||
|
||||
>>> accounts = get_accounts()
|
||||
|
||||
Create tax::
|
||||
|
||||
>>> tax = create_tax(Decimal('.10'))
|
||||
>>> tax.save()
|
||||
>>> accounts['expense'].taxes.append(Tax(tax.id))
|
||||
>>> accounts['expense'].save()
|
||||
|
||||
Create parties::
|
||||
|
||||
>>> supplier = Party(name="Supplier")
|
||||
>>> supplier.save()
|
||||
|
||||
Create a purchase without product::
|
||||
|
||||
>>> purchase = Purchase(party=supplier)
|
||||
>>> line = purchase.lines.new()
|
||||
>>> assertEqual(line.taxes, [tax])
|
||||
>>> line.quantity = 1
|
||||
>>> line.unit_price = Decimal('100.0000')
|
||||
>>> purchase.click('quote')
|
||||
>>> purchase.total_amount
|
||||
Decimal('110.00')
|
||||
>>> purchase.click('confirm')
|
||||
>>> purchase.state
|
||||
'processing'
|
||||
|
||||
Check invoice::
|
||||
|
||||
>>> invoice, = purchase.invoices
|
||||
>>> invoice.total_amount
|
||||
Decimal('110.00')
|
||||
>>> line, = invoice.lines
|
||||
>>> assertEqual(line.account, accounts['expense'])
|
||||
48
modules/purchase/tests/scenario_purchase_empty.rst
Normal file
48
modules/purchase/tests/scenario_purchase_empty.rst
Normal file
@@ -0,0 +1,48 @@
|
||||
=======================
|
||||
Purchase Empty Scenario
|
||||
=======================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> from proteus import Model
|
||||
>>> from trytond.modules.account.tests.tools import create_chart
|
||||
>>> from trytond.modules.company.tests.tools import create_company
|
||||
>>> from trytond.tests.tools import activate_modules
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> config = activate_modules('purchase', create_company, create_chart)
|
||||
|
||||
Create parties::
|
||||
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> supplier = Party(name='Supplier')
|
||||
>>> supplier.save()
|
||||
|
||||
Create empty purchase::
|
||||
|
||||
>>> Purchase = Model.get('purchase.purchase')
|
||||
>>> purchase = Purchase()
|
||||
>>> purchase.party = supplier
|
||||
>>> purchase.click('quote')
|
||||
>>> purchase.state
|
||||
'quotation'
|
||||
>>> purchase.untaxed_amount
|
||||
Decimal('0')
|
||||
>>> purchase.tax_amount
|
||||
Decimal('0')
|
||||
>>> purchase.total_amount
|
||||
Decimal('0')
|
||||
>>> purchase.click('confirm')
|
||||
>>> purchase.state
|
||||
'done'
|
||||
>>> purchase.shipment_state
|
||||
'none'
|
||||
>>> len(purchase.moves)
|
||||
0
|
||||
>>> len(purchase.shipment_returns)
|
||||
0
|
||||
>>> purchase.invoice_state
|
||||
'none'
|
||||
>>> len(purchase.invoices)
|
||||
0
|
||||
103
modules/purchase/tests/scenario_purchase_line_cancelled.rst
Normal file
103
modules/purchase/tests/scenario_purchase_line_cancelled.rst
Normal file
@@ -0,0 +1,103 @@
|
||||
================================
|
||||
Purchase Line Cancelled Scenario
|
||||
================================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> from decimal import Decimal
|
||||
|
||||
>>> from proteus import Model
|
||||
>>> from trytond.modules.account.tests.tools import create_chart, get_accounts
|
||||
>>> from trytond.modules.company.tests.tools import create_company
|
||||
>>> from trytond.tests.tools import activate_modules
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> config = activate_modules('purchase', create_company, create_chart)
|
||||
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> ProductCategory = Model.get('product.category')
|
||||
>>> ProductTemplate = Model.get('product.template')
|
||||
>>> ProductUom = Model.get('product.uom')
|
||||
>>> Purchase = Model.get('purchase.purchase')
|
||||
|
||||
Get accounts::
|
||||
|
||||
>>> accounts = get_accounts()
|
||||
|
||||
Create party::
|
||||
|
||||
>>> supplier = Party(name="Supplier")
|
||||
>>> supplier.save()
|
||||
|
||||
Create product::
|
||||
|
||||
>>> account_category = ProductCategory(name="Account Category")
|
||||
>>> account_category.accounting = True
|
||||
>>> account_category.account_expense = accounts['expense']
|
||||
>>> account_category.save()
|
||||
|
||||
>>> template = ProductTemplate(name="Product")
|
||||
>>> template.default_uom, = ProductUom.find([('name', '=', "Unit")])
|
||||
>>> template.type = 'goods'
|
||||
>>> template.purchasable = True
|
||||
>>> template.account_category = account_category
|
||||
>>> template.save()
|
||||
>>> product, = template.products
|
||||
|
||||
Purchase product::
|
||||
|
||||
>>> purchase = Purchase(party=supplier)
|
||||
>>> line = purchase.lines.new()
|
||||
>>> line.product = product
|
||||
>>> line.quantity = 1
|
||||
>>> line.unit_price = Decimal('10.0000')
|
||||
>>> purchase.click('quote')
|
||||
>>> purchase.click('confirm')
|
||||
>>> purchase.state
|
||||
'processing'
|
||||
>>> purchase.shipment_state
|
||||
'waiting'
|
||||
>>> purchase.invoice_state
|
||||
'pending'
|
||||
|
||||
Cancel stock move and invoice::
|
||||
|
||||
>>> move, = purchase.moves
|
||||
>>> move.click('cancel')
|
||||
>>> move.state
|
||||
'cancelled'
|
||||
|
||||
>>> invoice, = purchase.invoices
|
||||
>>> invoice.click('cancel')
|
||||
>>> invoice.state
|
||||
'cancelled'
|
||||
|
||||
>>> purchase.reload()
|
||||
>>> purchase.state
|
||||
'processing'
|
||||
>>> purchase.shipment_state
|
||||
'exception'
|
||||
>>> purchase.invoice_state
|
||||
'exception'
|
||||
|
||||
Ignore exceptions::
|
||||
|
||||
>>> invoice_handle_exception = purchase.click('handle_invoice_exception')
|
||||
>>> invoice_handle_exception.form.ignore_invoices.extend(
|
||||
... invoice_handle_exception.form.ignore_invoices.find())
|
||||
>>> invoice_handle_exception.execute('handle')
|
||||
|
||||
>>> purchase.invoice_state
|
||||
'none'
|
||||
|
||||
>>> shipment_handle_exception = purchase.click('handle_shipment_exception')
|
||||
>>> shipment_handle_exception.form.ignore_moves.extend(
|
||||
... shipment_handle_exception.form.ignore_moves.find())
|
||||
>>> shipment_handle_exception.execute('handle')
|
||||
|
||||
>>> purchase.shipment_state
|
||||
'none'
|
||||
|
||||
>>> purchase.state
|
||||
'done'
|
||||
@@ -0,0 +1,91 @@
|
||||
============================================
|
||||
Purchase Line Cancelled On Shipment Scenario
|
||||
============================================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> from decimal import Decimal
|
||||
|
||||
>>> from proteus import Model
|
||||
>>> from trytond.modules.account.tests.tools import create_chart, get_accounts
|
||||
>>> from trytond.modules.company.tests.tools import create_company
|
||||
>>> from trytond.tests.tools import activate_modules
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> config = activate_modules('purchase', create_company, create_chart)
|
||||
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> ProductCategory = Model.get('product.category')
|
||||
>>> ProductTemplate = Model.get('product.template')
|
||||
>>> ProductUom = Model.get('product.uom')
|
||||
>>> Purchase = Model.get('purchase.purchase')
|
||||
|
||||
Get accounts::
|
||||
|
||||
>>> accounts = get_accounts()
|
||||
|
||||
Create party::
|
||||
|
||||
>>> supplier = Party(name="Supplier")
|
||||
>>> supplier.save()
|
||||
|
||||
Create product::
|
||||
|
||||
>>> account_category = ProductCategory(name="Account Category")
|
||||
>>> account_category.accounting = True
|
||||
>>> account_category.account_expense = accounts['expense']
|
||||
>>> account_category.save()
|
||||
|
||||
>>> template = ProductTemplate(name="Product")
|
||||
>>> template.default_uom, = ProductUom.find([('name', '=', "Unit")])
|
||||
>>> template.type = 'goods'
|
||||
>>> template.purchasable = True
|
||||
>>> template.account_category = account_category
|
||||
>>> template.save()
|
||||
>>> product, = template.products
|
||||
|
||||
Purchase product::
|
||||
|
||||
>>> purchase = Purchase(party=supplier)
|
||||
>>> purchase.invoice_method = 'shipment'
|
||||
>>> line = purchase.lines.new()
|
||||
>>> line.product = product
|
||||
>>> line.quantity = 1
|
||||
>>> line.unit_price = Decimal('10.0000')
|
||||
>>> purchase.click('quote')
|
||||
>>> purchase.click('confirm')
|
||||
>>> purchase.state
|
||||
'processing'
|
||||
>>> purchase.shipment_state
|
||||
'waiting'
|
||||
>>> purchase.invoice_state
|
||||
'none'
|
||||
|
||||
Cancel stock move::
|
||||
|
||||
>>> move, = purchase.moves
|
||||
>>> move.click('cancel')
|
||||
>>> move.state
|
||||
'cancelled'
|
||||
|
||||
>>> purchase.reload()
|
||||
>>> purchase.state
|
||||
'processing'
|
||||
>>> purchase.shipment_state
|
||||
'exception'
|
||||
>>> purchase.invoice_state
|
||||
'none'
|
||||
|
||||
Ignore exception::
|
||||
|
||||
>>> shipment_handle_exception = purchase.click('handle_shipment_exception')
|
||||
>>> shipment_handle_exception.form.ignore_moves.extend(
|
||||
... shipment_handle_exception.form.ignore_moves.find())
|
||||
>>> shipment_handle_exception.execute('handle')
|
||||
|
||||
>>> purchase.shipment_state
|
||||
'none'
|
||||
|
||||
>>> purchase.state
|
||||
'done'
|
||||
90
modules/purchase/tests/scenario_purchase_manual_invoice.rst
Normal file
90
modules/purchase/tests/scenario_purchase_manual_invoice.rst
Normal file
@@ -0,0 +1,90 @@
|
||||
================================
|
||||
Purchase Manual Invoice Scenario
|
||||
================================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> from decimal import Decimal
|
||||
|
||||
>>> from proteus import Model
|
||||
>>> from trytond.modules.account.tests.tools import create_chart, get_accounts
|
||||
>>> from trytond.modules.company.tests.tools import create_company
|
||||
>>> from trytond.tests.tools import activate_modules
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> config = activate_modules('purchase', create_company, create_chart)
|
||||
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> ProductCategory = Model.get('product.category')
|
||||
>>> ProductTemplate = Model.get('product.template')
|
||||
>>> ProductUom = Model.get('product.uom')
|
||||
>>> Purchase = Model.get('purchase.purchase')
|
||||
|
||||
Get accounts::
|
||||
|
||||
>>> accounts = get_accounts()
|
||||
|
||||
Create party::
|
||||
|
||||
>>> supplier = Party(name='Supplier')
|
||||
>>> supplier.save()
|
||||
|
||||
Create account category::
|
||||
|
||||
>>> account_category = ProductCategory(name="Account Category")
|
||||
>>> account_category.accounting = True
|
||||
>>> account_category.account_expense = accounts['expense']
|
||||
>>> account_category.save()
|
||||
|
||||
Create product::
|
||||
|
||||
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
|
||||
|
||||
>>> template = ProductTemplate()
|
||||
>>> template.name = 'product'
|
||||
>>> template.default_uom = unit
|
||||
>>> template.type = 'service'
|
||||
>>> template.purchasable = True
|
||||
>>> template.account_category = account_category
|
||||
>>> template.save()
|
||||
>>> product, = template.products
|
||||
|
||||
Purchase with manual invoice method::
|
||||
|
||||
>>> purchase = Purchase()
|
||||
>>> purchase.party = supplier
|
||||
>>> purchase.invoice_method = 'manual'
|
||||
>>> line = purchase.lines.new()
|
||||
>>> line.product = product
|
||||
>>> line.quantity = 10
|
||||
>>> line.unit_price = Decimal('5.0000')
|
||||
>>> purchase.click('quote')
|
||||
>>> purchase.click('confirm')
|
||||
>>> purchase.state
|
||||
'processing'
|
||||
>>> purchase.invoice_state
|
||||
'none'
|
||||
>>> len(purchase.invoices)
|
||||
0
|
||||
|
||||
Manually create an invoice::
|
||||
|
||||
>>> purchase.click('manual_invoice')
|
||||
>>> purchase.state
|
||||
'processing'
|
||||
>>> purchase.invoice_state
|
||||
'pending'
|
||||
|
||||
Change quantity on invoice and create a new invoice::
|
||||
|
||||
>>> invoice, = purchase.invoices
|
||||
>>> line, = invoice.lines
|
||||
>>> line.quantity = 5
|
||||
>>> invoice.save()
|
||||
|
||||
>>> len(purchase.invoices)
|
||||
1
|
||||
>>> purchase.click('manual_invoice')
|
||||
>>> len(purchase.invoices)
|
||||
2
|
||||
92
modules/purchase/tests/scenario_purchase_modify_header.rst
Normal file
92
modules/purchase/tests/scenario_purchase_modify_header.rst
Normal file
@@ -0,0 +1,92 @@
|
||||
===============================
|
||||
Purchase Modify Header Scenario
|
||||
===============================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> from decimal import Decimal
|
||||
|
||||
>>> from proteus import Model
|
||||
>>> from trytond.modules.account.tests.tools import (
|
||||
... create_chart, create_tax, get_accounts)
|
||||
>>> from trytond.modules.company.tests.tools import create_company
|
||||
>>> from trytond.tests.tools import activate_modules, assertEqual
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> config = activate_modules('purchase', create_company, create_chart)
|
||||
|
||||
Get accounts::
|
||||
|
||||
>>> accounts = get_accounts()
|
||||
>>> expense = accounts['expense']
|
||||
|
||||
Create tax and tax rule::
|
||||
|
||||
>>> tax = create_tax(Decimal('.10'))
|
||||
>>> tax.save()
|
||||
|
||||
>>> TaxRule = Model.get('account.tax.rule')
|
||||
>>> foreign = TaxRule(name='Foreign Suppliers')
|
||||
>>> no_tax = foreign.lines.new()
|
||||
>>> no_tax.origin_tax = tax
|
||||
>>> foreign.save()
|
||||
|
||||
Create account categories::
|
||||
|
||||
>>> ProductCategory = Model.get('product.category')
|
||||
>>> account_category = ProductCategory(name="Account Category")
|
||||
>>> account_category.accounting = True
|
||||
>>> account_category.account_expense = expense
|
||||
>>> account_category.supplier_taxes.append(tax)
|
||||
>>> account_category.save()
|
||||
|
||||
Create product::
|
||||
|
||||
>>> ProductUom = Model.get('product.uom')
|
||||
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
|
||||
>>> ProductTemplate = Model.get('product.template')
|
||||
|
||||
>>> template = ProductTemplate()
|
||||
>>> template.name = 'product'
|
||||
>>> template.default_uom = unit
|
||||
>>> template.type = 'goods'
|
||||
>>> template.purchasable = True
|
||||
>>> template.list_price = Decimal('10')
|
||||
>>> template.account_category = account_category
|
||||
>>> template.save()
|
||||
>>> product, = template.products
|
||||
|
||||
Create parties::
|
||||
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> supplier = Party(name='Supplier')
|
||||
>>> supplier.save()
|
||||
>>> another = Party(name='Another Supplier', supplier_tax_rule=foreign)
|
||||
>>> another.save()
|
||||
|
||||
Create a sale with a line::
|
||||
|
||||
>>> Purchase = Model.get('purchase.purchase')
|
||||
>>> purchase = Purchase()
|
||||
>>> purchase.party = supplier
|
||||
>>> purchase_line = purchase.lines.new()
|
||||
>>> purchase_line.product = product
|
||||
>>> purchase_line.quantity = 3
|
||||
>>> purchase_line.unit_price = Decimal('5.0000')
|
||||
>>> purchase_line_comment = purchase.lines.new(type='comment')
|
||||
>>> purchase.save()
|
||||
>>> purchase.untaxed_amount, purchase.tax_amount, purchase.total_amount
|
||||
(Decimal('15.00'), Decimal('1.50'), Decimal('16.50'))
|
||||
|
||||
Change the party::
|
||||
|
||||
>>> modify_header = purchase.click('modify_header')
|
||||
>>> assertEqual(modify_header.form.party, supplier)
|
||||
>>> modify_header.form.party = another
|
||||
>>> modify_header.execute('modify')
|
||||
|
||||
>>> purchase.party.name
|
||||
'Another Supplier'
|
||||
>>> purchase.untaxed_amount, purchase.tax_amount, purchase.total_amount
|
||||
(Decimal('15.00'), Decimal('0'), Decimal('15.00'))
|
||||
145
modules/purchase/tests/scenario_purchase_reporting.rst
Normal file
145
modules/purchase/tests/scenario_purchase_reporting.rst
Normal file
@@ -0,0 +1,145 @@
|
||||
===========================
|
||||
Purchase Reporting Scenario
|
||||
===========================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> from decimal import Decimal
|
||||
|
||||
>>> from dateutil.relativedelta import relativedelta
|
||||
|
||||
>>> from proteus import Model
|
||||
>>> from trytond.modules.account.tests.tools import (
|
||||
... create_chart, create_fiscalyear, get_accounts)
|
||||
>>> from trytond.modules.account_invoice.tests.tools import (
|
||||
... set_fiscalyear_invoice_sequences)
|
||||
>>> from trytond.modules.company.tests.tools import create_company
|
||||
>>> from trytond.tests.tools import activate_modules, assertEqual
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> config = activate_modules('purchase', create_company, create_chart)
|
||||
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> ProductUom = Model.get('product.uom')
|
||||
>>> ProductTemplate = Model.get('product.template')
|
||||
>>> Purchase = Model.get('purchase.purchase')
|
||||
>>> Supplier = Model.get('purchase.reporting.supplier')
|
||||
>>> SupplierTimeseries = Model.get(
|
||||
... 'purchase.reporting.supplier.time_series')
|
||||
>>> Product = Model.get('purchase.reporting.product')
|
||||
>>> ProductTimeseries = Model.get('purchase.reporting.product.time_series')
|
||||
|
||||
Create fiscal year::
|
||||
|
||||
>>> fiscalyear = set_fiscalyear_invoice_sequences(create_fiscalyear())
|
||||
>>> fiscalyear.click('create_period')
|
||||
|
||||
Get accounts::
|
||||
|
||||
>>> accounts = get_accounts()
|
||||
>>> expense = accounts['expense']
|
||||
|
||||
Create parties::
|
||||
|
||||
>>> supplier1 = Party(name='Supplier1')
|
||||
>>> supplier1.save()
|
||||
>>> supplier2 = Party(name='Supplier2')
|
||||
>>> supplier2.save()
|
||||
|
||||
Create products::
|
||||
|
||||
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
|
||||
|
||||
>>> template1 = ProductTemplate()
|
||||
>>> template1.name = "Product1"
|
||||
>>> template1.default_uom = unit
|
||||
>>> template1.type = 'service'
|
||||
>>> template1.purchasable = True
|
||||
>>> template1.list_price = Decimal('20')
|
||||
>>> template1.save()
|
||||
>>> product1, = template1.products
|
||||
|
||||
>>> template2, = template1.duplicate(default={'name': "Product2"})
|
||||
>>> product2, = template2.products
|
||||
|
||||
Create purchases::
|
||||
|
||||
>>> purchase1 = Purchase()
|
||||
>>> purchase1.party = supplier1
|
||||
>>> purchase1.purchase_date = fiscalyear.start_date
|
||||
>>> line = purchase1.lines.new()
|
||||
>>> line.product = product1
|
||||
>>> line.quantity = 2
|
||||
>>> line.unit_price = Decimal('10.0000')
|
||||
>>> line = purchase1.lines.new()
|
||||
>>> line.product = product2
|
||||
>>> line.quantity = 1
|
||||
>>> line.unit_price = Decimal('10.0000')
|
||||
>>> purchase1.click('quote')
|
||||
>>> purchase1.click('confirm')
|
||||
|
||||
>>> purchase2 = Purchase()
|
||||
>>> purchase2.party = supplier2
|
||||
>>> purchase2.purchase_date = (
|
||||
... fiscalyear.start_date + relativedelta(months=1))
|
||||
>>> line = purchase2.lines.new()
|
||||
>>> line.product = product1
|
||||
>>> line.quantity = 1
|
||||
>>> line.unit_price = Decimal('10.0000')
|
||||
>>> purchase2.click('quote')
|
||||
>>> purchase2.click('confirm')
|
||||
|
||||
Check purchase reporting per supplier::
|
||||
|
||||
>>> context = dict(
|
||||
... from_date=fiscalyear.start_date,
|
||||
... to_date=fiscalyear.end_date,
|
||||
... period='month')
|
||||
>>> with config.set_context(context=context):
|
||||
... reports = Supplier.find([])
|
||||
... time_series = SupplierTimeseries.find([])
|
||||
>>> len(reports)
|
||||
2
|
||||
>>> with config.set_context(context=context):
|
||||
... assertEqual({(r.supplier.id, r.number, r.expense) for r in reports},
|
||||
... {(supplier1.id, 1, Decimal('30')),
|
||||
... (supplier2.id, 1, Decimal('10'))})
|
||||
>>> len(time_series)
|
||||
2
|
||||
>>> purchase1_ts_date = purchase1.purchase_date.replace(day=1)
|
||||
>>> purchase2_ts_date = purchase2.purchase_date.replace(day=1)
|
||||
>>> with config.set_context(context=context):
|
||||
... assertEqual({(r.supplier.id, r.date, r.number, r.expense)
|
||||
... for r in time_series},
|
||||
... {(supplier1.id, purchase1_ts_date, 1, Decimal('30')),
|
||||
... (supplier2.id, purchase2_ts_date, 1, Decimal('10'))})
|
||||
|
||||
Check purchase reporting per product without supplier::
|
||||
|
||||
>>> with config.set_context(context=context):
|
||||
... reports = Product.find([])
|
||||
... time_series = ProductTimeseries.find([])
|
||||
>>> len(reports)
|
||||
0
|
||||
|
||||
Check purchase reporting per product with supplier::
|
||||
|
||||
>>> context['supplier'] = supplier1.id
|
||||
>>> context['currency'] = purchase1.currency.id
|
||||
>>> with config.set_context(context=context):
|
||||
... reports = Product.find([])
|
||||
... time_series = ProductTimeseries.find([])
|
||||
>>> len(reports)
|
||||
2
|
||||
>>> with config.set_context(context=context):
|
||||
... assertEqual({(r.product.id, r.number, r.expense) for r in reports},
|
||||
... {(product1.id, 1, Decimal('20')),
|
||||
... (product2.id, 1, Decimal('10'))})
|
||||
>>> len(time_series)
|
||||
2
|
||||
>>> with config.set_context(context=context):
|
||||
... assertEqual({(r.product.id, r.date, r.number, r.expense)
|
||||
... for r in time_series},
|
||||
... {(product1.id, purchase1_ts_date, 1, Decimal('20')),
|
||||
... (product2.id, purchase1_ts_date, 1, Decimal('10'))})
|
||||
86
modules/purchase/tests/scenario_purchase_return_wizard.rst
Normal file
86
modules/purchase/tests/scenario_purchase_return_wizard.rst
Normal file
@@ -0,0 +1,86 @@
|
||||
===============================
|
||||
Purchase Return Wizard Scenario
|
||||
===============================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> from decimal import Decimal
|
||||
|
||||
>>> from proteus import Model, Wizard
|
||||
>>> from trytond.modules.account.tests.tools import (
|
||||
... create_chart, create_fiscalyear, get_accounts)
|
||||
>>> from trytond.modules.account_invoice.tests.tools import (
|
||||
... set_fiscalyear_invoice_sequences)
|
||||
>>> from trytond.modules.company.tests.tools import create_company
|
||||
>>> from trytond.tests.tools import activate_modules, assertEqual
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> config = activate_modules('purchase', create_company, create_chart)
|
||||
|
||||
Create fiscal year::
|
||||
|
||||
>>> fiscalyear = set_fiscalyear_invoice_sequences(create_fiscalyear())
|
||||
>>> fiscalyear.click('create_period')
|
||||
|
||||
Get accounts::
|
||||
|
||||
>>> accounts = get_accounts()
|
||||
>>> expense = accounts['expense']
|
||||
|
||||
Create parties::
|
||||
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> supplier = Party(name='Supplier')
|
||||
>>> supplier.save()
|
||||
|
||||
Create account categories::
|
||||
|
||||
>>> ProductCategory = Model.get('product.category')
|
||||
>>> account_category = ProductCategory(name="Account Category")
|
||||
>>> account_category.accounting = True
|
||||
>>> account_category.account_expense = expense
|
||||
>>> account_category.save()
|
||||
|
||||
Create product::
|
||||
|
||||
>>> ProductUom = Model.get('product.uom')
|
||||
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
|
||||
>>> ProductTemplate = Model.get('product.template')
|
||||
|
||||
>>> template = ProductTemplate()
|
||||
>>> template.name = 'service'
|
||||
>>> template.default_uom = unit
|
||||
>>> template.type = 'service'
|
||||
>>> template.purchasable = True
|
||||
>>> template.list_price = Decimal('10')
|
||||
>>> template.cost_price_method = 'fixed'
|
||||
>>> template.account_category = account_category
|
||||
>>> template.save()
|
||||
>>> service, = template.products
|
||||
|
||||
Return purchase using the wizard::
|
||||
|
||||
>>> Purchase = Model.get('purchase.purchase')
|
||||
>>> purchase_to_return = Purchase()
|
||||
>>> purchase_to_return.party = supplier
|
||||
>>> purchase_line = purchase_to_return.lines.new()
|
||||
>>> purchase_line.product = service
|
||||
>>> purchase_line.quantity = 1
|
||||
>>> purchase_line.unit_price = Decimal('10.0000')
|
||||
>>> purchase_line = purchase_to_return.lines.new()
|
||||
>>> purchase_line.type = 'comment'
|
||||
>>> purchase_line.description = 'Test comment'
|
||||
>>> purchase_to_return.click('quote')
|
||||
>>> purchase_to_return.click('confirm')
|
||||
>>> purchase_to_return.state
|
||||
'processing'
|
||||
>>> return_purchase = Wizard('purchase.return_purchase', [
|
||||
... purchase_to_return])
|
||||
>>> return_purchase.execute('return_')
|
||||
>>> returned_purchase, = Purchase.find([
|
||||
... ('state', '=', 'draft'),
|
||||
... ])
|
||||
>>> assertEqual(returned_purchase.origin, purchase_to_return)
|
||||
>>> sorted([x.quantity or 0 for x in returned_purchase.lines])
|
||||
[-1.0, 0]
|
||||
130
modules/purchase/tests/test_module.py
Normal file
130
modules/purchase/tests/test_module.py
Normal file
@@ -0,0 +1,130 @@
|
||||
# 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 decimal import Decimal
|
||||
|
||||
from trytond.modules.account.tests import create_chart
|
||||
from trytond.modules.company.tests import (
|
||||
CompanyTestMixin, PartyCompanyCheckEraseMixin, create_company, set_company)
|
||||
from trytond.modules.party.tests import PartyCheckReplaceMixin
|
||||
from trytond.pool import Pool
|
||||
from trytond.tests.test_tryton import ModuleTestCase, with_transaction
|
||||
from trytond.transaction import Transaction
|
||||
|
||||
|
||||
class PurchaseTestCase(
|
||||
PartyCompanyCheckEraseMixin, PartyCheckReplaceMixin, CompanyTestMixin,
|
||||
ModuleTestCase):
|
||||
'Test Purchase module'
|
||||
module = 'purchase'
|
||||
|
||||
@with_transaction()
|
||||
def test_purchase_price(self):
|
||||
'Test purchase price'
|
||||
pool = Pool()
|
||||
Account = pool.get('account.account')
|
||||
Template = pool.get('product.template')
|
||||
Product = pool.get('product.product')
|
||||
Uom = pool.get('product.uom')
|
||||
ProductSupplier = pool.get('purchase.product_supplier')
|
||||
Party = pool.get('party.party')
|
||||
Purchase = pool.get('purchase.purchase')
|
||||
|
||||
company = create_company()
|
||||
with set_company(company):
|
||||
create_chart(company)
|
||||
|
||||
receivable, = Account.search([
|
||||
('closed', '!=', True),
|
||||
('type.receivable', '=', True),
|
||||
('party_required', '=', True),
|
||||
('company', '=', company.id),
|
||||
], limit=1)
|
||||
payable, = Account.search([
|
||||
('closed', '!=', True),
|
||||
('type.payable', '=', True),
|
||||
('party_required', '=', True),
|
||||
('company', '=', company.id),
|
||||
], limit=1)
|
||||
|
||||
kg, = Uom.search([('name', '=', 'Kilogram')])
|
||||
g, = Uom.search([('name', '=', 'Gram')])
|
||||
|
||||
template, = Template.create([{
|
||||
'name': 'Product',
|
||||
'default_uom': g.id,
|
||||
'purchase_uom': kg.id,
|
||||
'list_price': Decimal(5),
|
||||
'purchasable': True,
|
||||
'products': [('create', [{
|
||||
'cost_price': Decimal(3),
|
||||
}])],
|
||||
}])
|
||||
product, = template.products
|
||||
|
||||
supplier, = Party.create([{
|
||||
'name': 'Supplier',
|
||||
'account_receivable': receivable.id,
|
||||
'account_payable': payable.id,
|
||||
'addresses': [('create', [{}])],
|
||||
}])
|
||||
product_supplier, = ProductSupplier.create([{
|
||||
'template': template.id,
|
||||
'party': supplier.id,
|
||||
'prices': [('create', [{
|
||||
'sequence': 1,
|
||||
'quantity': 1,
|
||||
'unit_price': Decimal(3000),
|
||||
}, {
|
||||
'sequence': 2,
|
||||
'quantity': 2,
|
||||
'unit_price': Decimal(2500),
|
||||
}])],
|
||||
}])
|
||||
|
||||
purchase, = Purchase.create([{
|
||||
'party': supplier.id,
|
||||
'invoice_address': supplier.addresses[0].id,
|
||||
'purchase_date': dt.date.today(),
|
||||
'lines': [('create', [{
|
||||
'product': product.id,
|
||||
'quantity': 10,
|
||||
'unit': kg.id,
|
||||
'unit_price': Decimal(2000),
|
||||
}])],
|
||||
}])
|
||||
purchase.state = 'confirmed'
|
||||
purchase.save()
|
||||
|
||||
prices = Product.get_purchase_price([product], quantity=100)
|
||||
self.assertEqual(prices, {product.id: Decimal(2)})
|
||||
prices = Product.get_purchase_price([product], quantity=1500)
|
||||
self.assertEqual(prices, {product.id: Decimal(2)})
|
||||
|
||||
with Transaction().set_context(uom=kg.id):
|
||||
prices = Product.get_purchase_price([product], quantity=0.5)
|
||||
self.assertEqual(prices, {product.id: Decimal(2000)})
|
||||
prices = Product.get_purchase_price([product], quantity=1.5)
|
||||
self.assertEqual(prices, {product.id: Decimal(2000)})
|
||||
|
||||
with Transaction().set_context(supplier=supplier.id):
|
||||
prices = Product.get_purchase_price([product], quantity=100)
|
||||
self.assertEqual(prices, {product.id: Decimal(2)})
|
||||
prices = Product.get_purchase_price([product], quantity=1500)
|
||||
self.assertEqual(prices, {product.id: Decimal(3)})
|
||||
prices = Product.get_purchase_price([product], quantity=3000)
|
||||
self.assertEqual(prices, {product.id: Decimal('2.5')})
|
||||
|
||||
with Transaction().set_context(uom=kg.id, supplier=supplier.id):
|
||||
prices = Product.get_purchase_price([product], quantity=0.5)
|
||||
self.assertEqual(prices, {product.id: Decimal(2000)})
|
||||
prices = Product.get_purchase_price([product], quantity=1.5)
|
||||
self.assertEqual(prices, {product.id: Decimal(3000)})
|
||||
prices = Product.get_purchase_price([product], quantity=3)
|
||||
self.assertEqual(prices, {product.id: Decimal(2500)})
|
||||
prices = Product.get_purchase_price([product], quantity=-4)
|
||||
self.assertEqual(prices, {product.id: Decimal(2500)})
|
||||
|
||||
|
||||
del ModuleTestCase
|
||||
8
modules/purchase/tests/test_scenario.py
Normal file
8
modules/purchase/tests/test_scenario.py
Normal file
@@ -0,0 +1,8 @@
|
||||
# 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.tests.test_tryton import load_doc_tests
|
||||
|
||||
|
||||
def load_tests(*args, **kwargs):
|
||||
return load_doc_tests(__name__, __file__, *args, **kwargs)
|
||||
69
modules/purchase/tryton.cfg
Normal file
69
modules/purchase/tryton.cfg
Normal file
@@ -0,0 +1,69 @@
|
||||
[tryton]
|
||||
version=7.8.1
|
||||
depends:
|
||||
account
|
||||
account_invoice
|
||||
account_invoice_stock
|
||||
account_product
|
||||
company
|
||||
currency
|
||||
ir
|
||||
party
|
||||
product
|
||||
res
|
||||
stock
|
||||
xml:
|
||||
purchase.xml
|
||||
purchase_reporting.xml
|
||||
configuration.xml
|
||||
party.xml
|
||||
stock.xml
|
||||
product.xml
|
||||
invoice.xml
|
||||
message.xml
|
||||
|
||||
[register]
|
||||
model:
|
||||
party.Party
|
||||
party.CustomerCode
|
||||
party.SupplierLeadTime
|
||||
party.PartySupplierCurrency
|
||||
product.Template
|
||||
product.Product
|
||||
product.ProductSupplier
|
||||
product.ProductSupplierPrice
|
||||
stock.Location
|
||||
stock.Move
|
||||
stock.ShipmentIn
|
||||
stock.ShipmentInReturn
|
||||
invoice.Invoice
|
||||
invoice.InvoiceLine
|
||||
configuration.Configuration
|
||||
configuration.ConfigurationSequence
|
||||
configuration.ConfigurationPurchaseMethod
|
||||
purchase.Purchase
|
||||
purchase.PurchaseIgnoredInvoice
|
||||
purchase.PurchaseRecreatedInvoice
|
||||
purchase.Line
|
||||
purchase.LineTax
|
||||
purchase.LineIgnoredMove
|
||||
purchase.LineRecreatedMove
|
||||
purchase.HandleShipmentExceptionAsk
|
||||
purchase.HandleInvoiceExceptionAsk
|
||||
purchase.ReturnPurchaseStart
|
||||
purchase_reporting.Context
|
||||
purchase_reporting.Main
|
||||
purchase_reporting.MainTimeseries
|
||||
purchase_reporting.Supplier
|
||||
purchase_reporting.SupplierTimeseries
|
||||
purchase_reporting.Product
|
||||
purchase_reporting.ProductTimeseries
|
||||
wizard:
|
||||
purchase.HandleShipmentException
|
||||
purchase.HandleInvoiceException
|
||||
party.PartyReplace
|
||||
party.PartyErase
|
||||
purchase.ModifyHeader
|
||||
purchase.ReturnPurchase
|
||||
report:
|
||||
purchase.PurchaseReport
|
||||
12
modules/purchase/view/configuration_form.xml
Normal file
12
modules/purchase/view/configuration_form.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<?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. -->
|
||||
<form>
|
||||
<label name="purchase_sequence"/>
|
||||
<field name="purchase_sequence"/>
|
||||
<label name="purchase_invoice_method" />
|
||||
<field name="purchase_invoice_method" />
|
||||
<newline/>
|
||||
<label name="purchase_process_after"/>
|
||||
<field name="purchase_process_after"/>
|
||||
</form>
|
||||
10
modules/purchase/view/handle_invoice_exception_ask_form.xml
Normal file
10
modules/purchase/view/handle_invoice_exception_ask_form.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<?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. -->
|
||||
<form>
|
||||
<image name="tryton-question" xexpand="0" xfill="0"/>
|
||||
<label string="Choose cancelled invoices to ignore or recreate:" id="choose" yalign="0.5" xalign="0.0" xexpand="1" colspan="3"/>
|
||||
|
||||
<field name="ignore_invoices" colspan="2"/>
|
||||
<field name="recreate_invoices" colspan="2"/>
|
||||
</form>
|
||||
10
modules/purchase/view/handle_shipment_exception_ask_form.xml
Normal file
10
modules/purchase/view/handle_shipment_exception_ask_form.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<?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. -->
|
||||
<form>
|
||||
<image name="tryton-question" xexpand="0" xfill="0"/>
|
||||
<label string="Choose cancelled stock moves to ignore or recreate:" id="choose" yalign="0.5" xalign="0.0" xexpand="1" colspan="3"/>
|
||||
|
||||
<field name="ignore_moves" colspan="2" view_ids="stock.move_view_tree_simple"/>
|
||||
<field name="recreate_moves" colspan="2" view_ids="stock.move_view_tree_simple"/>
|
||||
</form>
|
||||
9
modules/purchase/view/location_form.xml
Normal file
9
modules/purchase/view/location_form.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<?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. -->
|
||||
<data>
|
||||
<xpath expr="/form/field[@name='input_location']" position="after">
|
||||
<label name="supplier_return_location"/>
|
||||
<field name="supplier_return_location"/>
|
||||
</xpath>
|
||||
</data>
|
||||
8
modules/purchase/view/modify_header_form.xml
Normal file
8
modules/purchase/view/modify_header_form.xml
Normal file
@@ -0,0 +1,8 @@
|
||||
<?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. -->
|
||||
<data>
|
||||
<xpath expr="//field[@name='lines']" position="replace_attributes">
|
||||
<field name="lines" invisible="1"/>
|
||||
</xpath>
|
||||
</data>
|
||||
13
modules/purchase/view/move_list_shipment.xml
Normal file
13
modules/purchase/view/move_list_shipment.xml
Normal file
@@ -0,0 +1,13 @@
|
||||
<?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. -->
|
||||
<tree>
|
||||
<field name="product" expand="1"/>
|
||||
<field name="from_location" expand="1"/>
|
||||
<field name="to_location" expand="1"/>
|
||||
<field name="quantity" symbol="unit"/>
|
||||
<field name="state"/>
|
||||
<field name="purchase_exception_state"/>
|
||||
<button name="cancel" multiple="1"/>
|
||||
<button name="draft" multiple="1"/>
|
||||
</tree>
|
||||
8
modules/purchase/view/move_list_shipment_in.xml
Normal file
8
modules/purchase/view/move_list_shipment_in.xml
Normal file
@@ -0,0 +1,8 @@
|
||||
<?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. -->
|
||||
<data>
|
||||
<xpath expr="//field[@name='shipment']" position="after">
|
||||
<field name="purchase" expand="1"/>
|
||||
</xpath>
|
||||
</data>
|
||||
21
modules/purchase/view/party_form.xml
Normal file
21
modules/purchase/view/party_form.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<?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. -->
|
||||
<data>
|
||||
<xpath expr="//group[@id='links']" position="inside">
|
||||
<link icon="tryton-purchase" name="purchase.act_purchase_relate" empty="hide"/>
|
||||
<link icon="tryton-purchase" name="purchase.act_purchase_line_relate" empty="hide"/>
|
||||
</xpath>
|
||||
<xpath expr="/form/notebook/page[@id='general']" position="after">
|
||||
<page string="Supplier" id="supplier">
|
||||
<label name="customer_code"/>
|
||||
<field name="customer_code"/>
|
||||
<newline/>
|
||||
|
||||
<label name="supplier_lead_time"/>
|
||||
<field name="supplier_lead_time"/>
|
||||
<label name="supplier_currency"/>
|
||||
<field name="supplier_currency"/>
|
||||
</page>
|
||||
</xpath>
|
||||
</data>
|
||||
13
modules/purchase/view/product_list_purchase_line.xml
Normal file
13
modules/purchase/view/product_list_purchase_line.xml
Normal file
@@ -0,0 +1,13 @@
|
||||
<?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. -->
|
||||
<tree>
|
||||
<field name="code"/>
|
||||
<field name="name"/>
|
||||
<field name="purchase_price_uom"/>
|
||||
<field name="cost_price_uom"/>
|
||||
<field name="list_price_uom"/>
|
||||
<field name="quantity"/>
|
||||
<field name="forecast_quantity"/>
|
||||
<field name="default_uom"/>
|
||||
</tree>
|
||||
33
modules/purchase/view/product_supplier_form.xml
Normal file
33
modules/purchase/view/product_supplier_form.xml
Normal file
@@ -0,0 +1,33 @@
|
||||
<?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. -->
|
||||
<form cursor="party">
|
||||
<label name="template"/>
|
||||
<field name="template" colspan="3"/>
|
||||
<label name="product"/>
|
||||
<field name="product" colspan="3"/>
|
||||
|
||||
<label name="company"/>
|
||||
<field name="company" colspan="3"/>
|
||||
|
||||
<label name="party"/>
|
||||
<field name="party"/>
|
||||
<group id="sequence-active" col="-1" colspan="2">
|
||||
<label name="sequence"/>
|
||||
<field name="sequence"/>
|
||||
<label name="active"/>
|
||||
<field name="active"/>
|
||||
</group>
|
||||
<label name="name"/>
|
||||
<field name="name"/>
|
||||
<label name="code"/>
|
||||
<field name="code"/>
|
||||
<label name="lead_time"/>
|
||||
<field name="lead_time"/>
|
||||
<newline/>
|
||||
<label name="currency"/>
|
||||
<field name="currency"/>
|
||||
<field name="prices" colspan="4"
|
||||
view_ids="purchase.product_supplier_price_view_list_sequence"/>
|
||||
<field name="unit" colspan="4" invisible="1"/>
|
||||
</form>
|
||||
14
modules/purchase/view/product_supplier_price_form.xml
Normal file
14
modules/purchase/view/product_supplier_price_form.xml
Normal file
@@ -0,0 +1,14 @@
|
||||
<?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. -->
|
||||
<form>
|
||||
<label name="product_supplier"/>
|
||||
<field name="product_supplier"/>
|
||||
<label name="sequence"/>
|
||||
<field name="sequence"/>
|
||||
|
||||
<label name="quantity"/>
|
||||
<field name="quantity" symbol="unit"/>
|
||||
<label name="unit_price"/>
|
||||
<field name="unit_price"/>
|
||||
</form>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?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. -->
|
||||
<tree sequence="sequence" editable="1">
|
||||
<field name="product_supplier" expand="1"/>
|
||||
<field name="quantity" symbol="unit"/>
|
||||
<field name="unit_price" expand="1"/>
|
||||
</tree>
|
||||
8
modules/purchase/view/product_supplier_price_tree.xml
Normal file
8
modules/purchase/view/product_supplier_price_tree.xml
Normal file
@@ -0,0 +1,8 @@
|
||||
<?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. -->
|
||||
<tree>
|
||||
<field name="product_supplier" expand="1"/>
|
||||
<field name="quantity" symbol="unit"/>
|
||||
<field name="unit_price"/>
|
||||
</tree>
|
||||
11
modules/purchase/view/product_supplier_tree.xml
Normal file
11
modules/purchase/view/product_supplier_tree.xml
Normal file
@@ -0,0 +1,11 @@
|
||||
<?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. -->
|
||||
<tree>
|
||||
<field name="company" expand="1" optional="1"/>
|
||||
<field name="template" expand="2"/>
|
||||
<field name="product" expand="2"/>
|
||||
<field name="party" expand="2"/>
|
||||
<field name="name" expand="1"/>
|
||||
<field name="code" expand="1"/>
|
||||
</tree>
|
||||
11
modules/purchase/view/product_supplier_tree_sequence.xml
Normal file
11
modules/purchase/view/product_supplier_tree_sequence.xml
Normal file
@@ -0,0 +1,11 @@
|
||||
<?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. -->
|
||||
<tree sequence="sequence">
|
||||
<field name="company" expand="1" optional="1"/>
|
||||
<field name="template" expand="2"/>
|
||||
<field name="product" expand="2"/>
|
||||
<field name="party" expand="2"/>
|
||||
<field name="name" expand="1"/>
|
||||
<field name="code" expand="1"/>
|
||||
</tree>
|
||||
92
modules/purchase/view/purchase_form.xml
Normal file
92
modules/purchase/view/purchase_form.xml
Normal file
@@ -0,0 +1,92 @@
|
||||
<?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. -->
|
||||
<form col="6">
|
||||
<label name="party"/>
|
||||
<field name="party"/>
|
||||
<label name="contact"/>
|
||||
<field name="contact"/>
|
||||
<label name="number"/>
|
||||
<field name="number"/>
|
||||
<label name="invoice_party"/>
|
||||
<field name="invoice_party"/>
|
||||
<label name="invoice_address"/>
|
||||
<field name="invoice_address" colspan="3"/>
|
||||
|
||||
<label name="description"/>
|
||||
<field name="description" colspan="3"/>
|
||||
<label name="reference"/>
|
||||
<field name="reference"/>
|
||||
<notebook colspan="6">
|
||||
<page string="Purchase" id="purchase" col="6">
|
||||
<label name="purchase_date"/>
|
||||
<field name="purchase_date"/>
|
||||
<label name="payment_term"/>
|
||||
<field name="payment_term"/>
|
||||
<label name="currency"/>
|
||||
<field name="currency"/>
|
||||
|
||||
<label name="warehouse"/>
|
||||
<field name="warehouse"/>
|
||||
<label name="delivery_date"/>
|
||||
<field name="delivery_date"/>
|
||||
<label name="quotation_expire"/>
|
||||
<field name="quotation_expire"/>
|
||||
|
||||
<field name="lines" colspan="6" view_ids="purchase.purchase_line_view_tree_sequence"/>
|
||||
<group col="2" colspan="3" id="states" yfill="1">
|
||||
<label name="invoice_state"/>
|
||||
<field name="invoice_state"/>
|
||||
<label name="shipment_state"/>
|
||||
<field name="shipment_state"/>
|
||||
<label name="state"/>
|
||||
<field name="state"/>
|
||||
</group>
|
||||
<group col="2" colspan="3" id="amount" yfill="1">
|
||||
<label name="untaxed_amount" xalign="1.0" xexpand="1" xfill="0"/>
|
||||
<field name="untaxed_amount" xalign="1.0" xexpand="0"/>
|
||||
<label name="tax_amount" xalign="1.0" xexpand="1" xfill="0"/>
|
||||
<field name="tax_amount" xalign="1.0" xexpand="0"/>
|
||||
<label name="total_amount" xalign="1.0" xexpand="1" xfill="0"/>
|
||||
<field name="total_amount" xalign="1.0" xexpand="0"/>
|
||||
</group>
|
||||
</page>
|
||||
<page string="Other Info" id="info">
|
||||
<label name="company"/>
|
||||
<field name="company"/>
|
||||
<label name="origin"/>
|
||||
<field name="origin"/>
|
||||
|
||||
<label name="invoice_method"/>
|
||||
<field name="invoice_method"/>
|
||||
<newline/>
|
||||
|
||||
<label name="quoted_by"/>
|
||||
<field name="quoted_by"/>
|
||||
<label name="confirmed_by"/>
|
||||
<field name="confirmed_by"/>
|
||||
<separator name="comment" colspan="4"/>
|
||||
<field name="comment" colspan="4"/>
|
||||
</page>
|
||||
<page name="invoices_ignored" col="1">
|
||||
<field name="invoices_ignored"/>
|
||||
</page>
|
||||
</notebook>
|
||||
<group id="links" col="-1" colspan="3">
|
||||
<link icon="tryton-shipment-in" name="purchase.act_shipment_form"/>
|
||||
<link icon="tryton-account" name="purchase.act_invoice_form"/>
|
||||
<link icon="tryton-shipment-out" name="purchase.act_return_form"/>
|
||||
</group>
|
||||
<group col="-1" colspan="3" id="buttons">
|
||||
<button name="cancel" icon="tryton-cancel"/>
|
||||
<button name="draft"/>
|
||||
<button name="modify_header" icon="tryton-launch"/>
|
||||
<button name="quote" icon="tryton-forward"/>
|
||||
<button name="handle_invoice_exception" icon="tryton-forward"/>
|
||||
<button name="handle_shipment_exception" icon="tryton-forward"/>
|
||||
<button name="confirm" icon="tryton-ok"/>
|
||||
<button name="process"/>
|
||||
<button name="manual_invoice" icon="tryton-forward"/>
|
||||
</group>
|
||||
<field name="party_lang" invisible="1" colspan="6"/>
|
||||
</form>
|
||||
49
modules/purchase/view/purchase_line_form.xml
Normal file
49
modules/purchase/view/purchase_line_form.xml
Normal file
@@ -0,0 +1,49 @@
|
||||
<?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. -->
|
||||
<form cursor="product">
|
||||
<label name="purchase"/>
|
||||
<field name="purchase" colspan="3"/>
|
||||
<label name="type"/>
|
||||
<field name="type"/>
|
||||
<label name="sequence"/>
|
||||
<field name="sequence"/>
|
||||
<notebook colspan="4">
|
||||
<page string="General" id="general">
|
||||
<label name="product"/>
|
||||
<field name="product"
|
||||
view_ids="purchase.product_view_list_purchase_line"/>
|
||||
<label name="product_supplier"/>
|
||||
<field name="product_supplier"/>
|
||||
<label name="quantity"/>
|
||||
<field name="quantity"/>
|
||||
<label name="unit"/>
|
||||
<field name="unit"/>
|
||||
<label name="unit_price"/>
|
||||
<field name="unit_price"/>
|
||||
<label name="amount"/>
|
||||
<field name="amount"/>
|
||||
<label id="delivery_date" string="Delivery Date:"/>
|
||||
<group id="delivery_date" col="-1">
|
||||
<field name="delivery_date" xexpand="0"/>
|
||||
<field name="delivery_date_edit" xexpand="0" xalign="0"/>
|
||||
</group>
|
||||
<separator name="description" colspan="4"/>
|
||||
<field name="description" colspan="4"/>
|
||||
</page>
|
||||
<page string="Taxes" id="taxes">
|
||||
<field name="taxes" colspan="4"/>
|
||||
</page>
|
||||
<page name="moves" col="1">
|
||||
<field name="moves"/>
|
||||
<field name="moves_ignored"/>
|
||||
</page>
|
||||
<page name="invoice_lines" col="1">
|
||||
<field name="invoice_lines"/>
|
||||
</page>
|
||||
<page string="Notes" id="notes">
|
||||
<separator name="note" colspan="4"/>
|
||||
<field name="note" colspan="4"/>
|
||||
</page>
|
||||
</notebook>
|
||||
</form>
|
||||
19
modules/purchase/view/purchase_line_tree.xml
Normal file
19
modules/purchase/view/purchase_line_tree.xml
Normal file
@@ -0,0 +1,19 @@
|
||||
<?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. -->
|
||||
<tree>
|
||||
<field name="purchase" expand="1"/>
|
||||
<field name="supplier" expand="1" optional="0"/>
|
||||
<field name="purchase_date" optional="1"/>
|
||||
<field name="type" optional="1"/>
|
||||
<field name="product" expand="1" optional="0"/>
|
||||
<field name="product_supplier" expand="1" optional="1"/>
|
||||
<field name="summary" expand="1" optional="1"/>
|
||||
<field name="actual_quantity" symbol="unit" optional="0"/>
|
||||
<field name="quantity" symbol="unit" optional="0"/>
|
||||
<field name="unit_price"/>
|
||||
<field name="amount"/>
|
||||
<field name="moves_progress" string="Shipping" widget="progressbar" optional="1"/>
|
||||
<field name="invoice_progress" string="Invoicing" widget="progressbar" optional="1"/>
|
||||
<field name="purchase_state"/>
|
||||
</tree>
|
||||
14
modules/purchase/view/purchase_line_tree_sequence.xml
Normal file
14
modules/purchase/view/purchase_line_tree_sequence.xml
Normal file
@@ -0,0 +1,14 @@
|
||||
<?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. -->
|
||||
<tree sequence="sequence">
|
||||
<field name="purchase" expand="1"/>
|
||||
<field name="type" optional="1"/>
|
||||
<field name="product" expand="1" optional="0"/>
|
||||
<field name="product_supplier" expand="1" optional="1"/>
|
||||
<field name="summary" expand="1" optional="1"/>
|
||||
<field name="quantity" symbol="unit"/>
|
||||
<field name="unit_price"/>
|
||||
<field name="taxes" optional="0"/>
|
||||
<field name="amount"/>
|
||||
</tree>
|
||||
17
modules/purchase/view/purchase_reporting_context_form.xml
Normal file
17
modules/purchase/view/purchase_reporting_context_form.xml
Normal 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. -->
|
||||
<form>
|
||||
<group id="dates" colspan="2" col="4">
|
||||
<label name="from_date"/>
|
||||
<field name="from_date"/>
|
||||
<label name="to_date"/>
|
||||
<field name="to_date"/>
|
||||
</group>
|
||||
<label name="period"/>
|
||||
<field name="period"/>
|
||||
<label name="company"/>
|
||||
<field name="company"/>
|
||||
<label name="warehouse"/>
|
||||
<field name="warehouse"/>
|
||||
</form>
|
||||
@@ -0,0 +1,11 @@
|
||||
<?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. -->
|
||||
<graph>
|
||||
<x>
|
||||
<field name="id"/>
|
||||
</x>
|
||||
<y>
|
||||
<field name="expense"/>
|
||||
</y>
|
||||
</graph>
|
||||
@@ -0,0 +1,11 @@
|
||||
<?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. -->
|
||||
<graph>
|
||||
<x>
|
||||
<field name="id"/>
|
||||
</x>
|
||||
<y>
|
||||
<field name="number"/>
|
||||
</y>
|
||||
</graph>
|
||||
8
modules/purchase/view/purchase_reporting_main_list.xml
Normal file
8
modules/purchase/view/purchase_reporting_main_list.xml
Normal file
@@ -0,0 +1,8 @@
|
||||
<?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. -->
|
||||
<tree keyword_open="1">
|
||||
<field name="number" sum="1"/>
|
||||
<field name="expense"/>
|
||||
<field name="expense_trend" expand="1"/>
|
||||
</tree>
|
||||
@@ -0,0 +1,11 @@
|
||||
<?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. -->
|
||||
<graph>
|
||||
<x>
|
||||
<field name="date"/>
|
||||
</x>
|
||||
<y>
|
||||
<field name="expense"/>
|
||||
</y>
|
||||
</graph>
|
||||
@@ -0,0 +1,11 @@
|
||||
<?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. -->
|
||||
<graph>
|
||||
<x>
|
||||
<field name="date"/>
|
||||
</x>
|
||||
<y>
|
||||
<field name="number"/>
|
||||
</y>
|
||||
</graph>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?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. -->
|
||||
<tree>
|
||||
<field name="date"/>
|
||||
<field name="number" sum="1"/>
|
||||
<field name="expense"/>
|
||||
</tree>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?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. -->
|
||||
<data>
|
||||
<xpath expr="//x/field[@name='id']" position="replace">
|
||||
<field name="product"/>
|
||||
</xpath>
|
||||
</data>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?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. -->
|
||||
<data>
|
||||
<xpath expr="//x/field[@name='id']" position="replace">
|
||||
<field name="product"/>
|
||||
</xpath>
|
||||
</data>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?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. -->
|
||||
<data>
|
||||
<xpath expr="//field[@name='number']" position="before">
|
||||
<field name="product" expand="1"/>
|
||||
<field name="product_supplier" expand="1"/>
|
||||
</xpath>
|
||||
</data>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?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. -->
|
||||
<data>
|
||||
<xpath expr="//x/field[@name='id']" position="replace">
|
||||
<field name="supplier"/>
|
||||
</xpath>
|
||||
</data>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?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. -->
|
||||
<data>
|
||||
<xpath expr="//x/field[@name='id']" position="replace">
|
||||
<field name="supplier"/>
|
||||
</xpath>
|
||||
</data>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user