first commit
This commit is contained in:
2
modules/sale/__init__.py
Normal file
2
modules/sale/__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/sale/__pycache__/__init__.cpython-311.pyc
Normal file
BIN
modules/sale/__pycache__/__init__.cpython-311.pyc
Normal file
Binary file not shown.
BIN
modules/sale/__pycache__/configuration.cpython-311.pyc
Normal file
BIN
modules/sale/__pycache__/configuration.cpython-311.pyc
Normal file
Binary file not shown.
BIN
modules/sale/__pycache__/exceptions.cpython-311.pyc
Normal file
BIN
modules/sale/__pycache__/exceptions.cpython-311.pyc
Normal file
Binary file not shown.
BIN
modules/sale/__pycache__/invoice.cpython-311.pyc
Normal file
BIN
modules/sale/__pycache__/invoice.cpython-311.pyc
Normal file
Binary file not shown.
BIN
modules/sale/__pycache__/ir.cpython-311.pyc
Normal file
BIN
modules/sale/__pycache__/ir.cpython-311.pyc
Normal file
Binary file not shown.
BIN
modules/sale/__pycache__/party.cpython-311.pyc
Normal file
BIN
modules/sale/__pycache__/party.cpython-311.pyc
Normal file
Binary file not shown.
BIN
modules/sale/__pycache__/product.cpython-311.pyc
Normal file
BIN
modules/sale/__pycache__/product.cpython-311.pyc
Normal file
Binary file not shown.
BIN
modules/sale/__pycache__/sale.cpython-311.pyc
Normal file
BIN
modules/sale/__pycache__/sale.cpython-311.pyc
Normal file
Binary file not shown.
BIN
modules/sale/__pycache__/sale_reporting.cpython-311.pyc
Normal file
BIN
modules/sale/__pycache__/sale_reporting.cpython-311.pyc
Normal file
Binary file not shown.
BIN
modules/sale/__pycache__/stock.cpython-311.pyc
Normal file
BIN
modules/sale/__pycache__/stock.cpython-311.pyc
Normal file
Binary file not shown.
121
modules/sale/configuration.py
Normal file
121
modules/sale/configuration.py
Normal file
@@ -0,0 +1,121 @@
|
||||
# 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 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
|
||||
|
||||
sale_invoice_method = fields.Selection(
|
||||
'get_sale_invoice_methods', "Sale Invoice Method")
|
||||
sale_shipment_method = fields.Selection(
|
||||
'get_sale_shipment_methods', "Sale Shipment Method")
|
||||
sale_quotation_validity = fields.TimeDelta(
|
||||
"Sale Quotation Validity",
|
||||
domain=['OR',
|
||||
('sale_quotation_validity', '>=', dt.timedelta()),
|
||||
('sale_quotation_validity', '=', None),
|
||||
])
|
||||
|
||||
|
||||
def get_sale_methods(field_name):
|
||||
@classmethod
|
||||
def func(cls):
|
||||
pool = Pool()
|
||||
Sale = pool.get('sale.sale')
|
||||
return Sale.fields_get([field_name])[field_name]['selection']
|
||||
return func
|
||||
|
||||
|
||||
def default_func(field_name):
|
||||
@classmethod
|
||||
def default(cls, **pattern):
|
||||
return getattr(
|
||||
cls.multivalue_model(field_name),
|
||||
'default_%s' % field_name, lambda: None)()
|
||||
return default
|
||||
|
||||
|
||||
class Configuration(
|
||||
ModelSingleton, ModelSQL, ModelView, CompanyMultiValueMixin):
|
||||
__name__ = 'sale.configuration'
|
||||
sale_sequence = fields.MultiValue(fields.Many2One(
|
||||
'ir.sequence', "Sale Sequence", required=True,
|
||||
domain=[
|
||||
('company', 'in',
|
||||
[Eval('context', {}).get('company', -1), None]),
|
||||
('sequence_type', '=', Id('sale', 'sequence_type_sale')),
|
||||
]))
|
||||
sale_quotation_validity = fields.MultiValue(sale_quotation_validity)
|
||||
sale_invoice_method = fields.MultiValue(sale_invoice_method)
|
||||
get_sale_invoice_methods = get_sale_methods('invoice_method')
|
||||
sale_shipment_method = fields.MultiValue(sale_shipment_method)
|
||||
get_sale_shipment_methods = get_sale_methods('shipment_method')
|
||||
sale_process_after = fields.TimeDelta(
|
||||
"Process Sale after",
|
||||
domain=['OR',
|
||||
('sale_process_after', '=', None),
|
||||
('sale_process_after', '>=', TimeDelta()),
|
||||
],
|
||||
help="The grace period during which confirmed sale "
|
||||
"can still be reset to draft.\n"
|
||||
"Applied if a worker queue is activated.")
|
||||
|
||||
@classmethod
|
||||
def multivalue_model(cls, field):
|
||||
pool = Pool()
|
||||
if field in {'sale_invoice_method', 'sale_shipment_method'}:
|
||||
return pool.get('sale.configuration.sale_method')
|
||||
if field == 'sale_sequence':
|
||||
return pool.get('sale.configuration.sequence')
|
||||
elif field == 'sale_quotation_validity':
|
||||
return pool.get('sale.configuration.quotation')
|
||||
return super().multivalue_model(field)
|
||||
|
||||
default_sale_sequence = default_func('sale_sequence')
|
||||
default_sale_invoice_method = default_func('sale_invoice_method')
|
||||
default_sale_shipment_method = default_func('sale_shipment_method')
|
||||
|
||||
|
||||
class ConfigurationSequence(ModelSQL, CompanyValueMixin):
|
||||
__name__ = 'sale.configuration.sequence'
|
||||
sale_sequence = fields.Many2One(
|
||||
'ir.sequence', "Sale Sequence", required=True,
|
||||
domain=[
|
||||
('company', 'in', [Eval('company', -1), None]),
|
||||
('sequence_type', '=', Id('sale', 'sequence_type_sale')),
|
||||
])
|
||||
|
||||
@classmethod
|
||||
def default_sale_sequence(cls):
|
||||
pool = Pool()
|
||||
ModelData = pool.get('ir.model.data')
|
||||
try:
|
||||
return ModelData.get_id('sale', 'sequence_sale')
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
|
||||
class ConfigurationSaleMethod(ModelSQL, ValueMixin):
|
||||
__name__ = 'sale.configuration.sale_method'
|
||||
sale_invoice_method = sale_invoice_method
|
||||
get_sale_invoice_methods = get_sale_methods('invoice_method')
|
||||
sale_shipment_method = sale_shipment_method
|
||||
get_sale_shipment_methods = get_sale_methods('shipment_method')
|
||||
|
||||
@classmethod
|
||||
def default_sale_invoice_method(cls):
|
||||
return 'order'
|
||||
|
||||
@classmethod
|
||||
def default_sale_shipment_method(cls):
|
||||
return 'order'
|
||||
|
||||
|
||||
class ConfigurationQuotation(ModelSQL, ValueMixin):
|
||||
__name__ = 'sale.configuration.quotation'
|
||||
sale_quotation_validity = sale_quotation_validity
|
||||
56
modules/sale/configuration.xml
Normal file
56
modules/sale/configuration.xml
Normal file
@@ -0,0 +1,56 @@
|
||||
<?xml version="1.0"?>
|
||||
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
this repository contains the full copyright notices and license terms. -->
|
||||
<tryton>
|
||||
<data>
|
||||
<menuitem
|
||||
name="Configuration"
|
||||
parent="menu_sale"
|
||||
sequence="0"
|
||||
id="menu_configuration"
|
||||
icon="tryton-settings"/>
|
||||
<record model="ir.ui.menu-res.group"
|
||||
id="menu_configuration_group_sale_admin">
|
||||
<field name="menu" ref="menu_configuration"/>
|
||||
<field name="group" ref="group_sale_admin"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="sale_configuration_view_form">
|
||||
<field name="model">sale.configuration</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">configuration_form</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window" id="act_sale_configuration_form">
|
||||
<field name="name">Configuration</field>
|
||||
<field name="res_model">sale.configuration</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view"
|
||||
id="act_sale_configuration_view1">
|
||||
<field name="sequence" eval="1"/>
|
||||
<field name="view" ref="sale_configuration_view_form"/>
|
||||
<field name="act_window" ref="act_sale_configuration_form"/>
|
||||
</record>
|
||||
<menuitem
|
||||
parent="menu_configuration"
|
||||
action="act_sale_configuration_form"
|
||||
sequence="10"
|
||||
id="menu_sale_configuration"
|
||||
icon="tryton-list"/>
|
||||
|
||||
<record model="ir.model.access" id="access_sale_configuration">
|
||||
<field name="model">sale.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_sale_configuration_sale_admin">
|
||||
<field name="model">sale.configuration</field>
|
||||
<field name="group" ref="group_sale_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>
|
||||
25
modules/sale/exceptions.py
Normal file
25
modules/sale/exceptions.py
Normal file
@@ -0,0 +1,25 @@
|
||||
# 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 SaleValidationError(ValidationError):
|
||||
pass
|
||||
|
||||
|
||||
class SaleQuotationError(ValidationError):
|
||||
pass
|
||||
|
||||
|
||||
class SaleConfirmError(UserError):
|
||||
pass
|
||||
|
||||
|
||||
class SaleMoveQuantity(UserWarning):
|
||||
pass
|
||||
|
||||
|
||||
class PartyLocationError(UserError):
|
||||
pass
|
||||
4
modules/sale/icons/tryton-sale.svg
Normal file
4
modules/sale/icons/tryton-sale.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="M0 0h24v24H0z" fill="none"/>
|
||||
<path d="M20 4H4v2h16V4zm1 10v-2l-1-5H4l-1 5v2h1v6h10v-6h4v6h2v-6h1zm-9 4H6v-4h6v4z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 224 B |
175
modules/sale/invoice.py
Normal file
175
modules/sale/invoice.py
Normal file
@@ -0,0 +1,175 @@
|
||||
# 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 Workflow, fields
|
||||
from trytond.model.exceptions import AccessError
|
||||
from trytond.pool import Pool, PoolMeta
|
||||
from trytond.pyson import Eval
|
||||
from trytond.transaction import Transaction, without_check_access
|
||||
|
||||
|
||||
def process_sale(func):
|
||||
@wraps(func)
|
||||
def wrapper(cls, invoices):
|
||||
pool = Pool()
|
||||
Sale = pool.get('sale.sale')
|
||||
transaction = Transaction()
|
||||
context = transaction.context
|
||||
with without_check_access():
|
||||
sales = set(s for i in cls.browse(invoices) for s in i.sales)
|
||||
result = func(cls, invoices)
|
||||
if sales:
|
||||
with transaction.set_context(
|
||||
queue_batch=context.get('queue_batch', True)):
|
||||
Sale.__queue__.process(sales)
|
||||
return result
|
||||
return wrapper
|
||||
|
||||
|
||||
class Invoice(metaclass=PoolMeta):
|
||||
__name__ = 'account.invoice'
|
||||
sale_exception_state = fields.Function(fields.Selection([
|
||||
('', ''),
|
||||
('ignored', 'Ignored'),
|
||||
('recreated', 'Recreated'),
|
||||
], 'Exception State'), 'get_sale_exception_state')
|
||||
sales = fields.Function(fields.Many2Many(
|
||||
'sale.sale', None, None, "Sales"),
|
||||
'get_sales', searcher='search_sales')
|
||||
|
||||
def get_sale_exception_state(self, name):
|
||||
sales = self.sales
|
||||
|
||||
recreated = tuple(i for p in sales for i in p.invoices_recreated)
|
||||
ignored = tuple(i for p in sales for i in p.invoices_ignored)
|
||||
|
||||
if self in recreated:
|
||||
return 'recreated'
|
||||
elif self in ignored:
|
||||
return 'ignored'
|
||||
return ''
|
||||
|
||||
def get_sales(self, name):
|
||||
pool = Pool()
|
||||
SaleLine = pool.get('sale.line')
|
||||
sales = set()
|
||||
for line in self.lines:
|
||||
if isinstance(line.origin, SaleLine):
|
||||
sales.add(line.origin.sale.id)
|
||||
return list(sales)
|
||||
|
||||
@classmethod
|
||||
def search_sales(cls, name, clause):
|
||||
return [('lines.origin.sale' + clause[0][len(name):],
|
||||
*clause[1:3], 'sale.line', *clause[3:])]
|
||||
|
||||
def get_tax_identifier(self, pattern=None):
|
||||
if self.sales:
|
||||
pattern = pattern.copy() if pattern is not None else {}
|
||||
shipment_countries = {
|
||||
s.shipment_address.country for s in self.sales
|
||||
if s.shipment_address and s.shipment_address.country}
|
||||
if len(shipment_countries) == 1:
|
||||
shipment_country, = shipment_countries
|
||||
pattern.setdefault('country', shipment_country.id)
|
||||
return super().get_tax_identifier(pattern=pattern)
|
||||
|
||||
@classmethod
|
||||
@process_sale
|
||||
def on_delete(cls, invoices):
|
||||
return super().on_delete(invoices)
|
||||
|
||||
@classmethod
|
||||
@process_sale
|
||||
def _post(cls, invoices):
|
||||
super()._post(invoices)
|
||||
|
||||
@classmethod
|
||||
@process_sale
|
||||
def paid(cls, invoices):
|
||||
super().paid(invoices)
|
||||
|
||||
@classmethod
|
||||
@process_sale
|
||||
def cancel(cls, invoices):
|
||||
super().cancel(invoices)
|
||||
|
||||
@classmethod
|
||||
@Workflow.transition('draft')
|
||||
def draft(cls, invoices):
|
||||
for invoice in invoices:
|
||||
if invoice.sales and invoice.state == 'cancelled':
|
||||
raise AccessError(
|
||||
gettext('sale.msg_sale_invoice_reset_draft',
|
||||
invoice=invoice.rec_name))
|
||||
|
||||
return super().draft(invoices)
|
||||
|
||||
|
||||
class Line(metaclass=PoolMeta):
|
||||
__name__ = 'account.invoice.line'
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super().__setup__()
|
||||
if not cls.origin.domain:
|
||||
cls.origin.domain = {}
|
||||
cls.origin.domain['sale.line'] = [
|
||||
('type', '=', Eval('type')),
|
||||
]
|
||||
|
||||
@fields.depends('origin')
|
||||
def on_change_with_product_uom_category(self, name=None):
|
||||
pool = Pool()
|
||||
SaleLine = pool.get('sale.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 ship.
|
||||
# Use getattr as reference field can have negative id
|
||||
if (isinstance(self.origin, SaleLine)
|
||||
and getattr(self.origin, 'unit', None)):
|
||||
category = self.origin.unit.category
|
||||
return category
|
||||
|
||||
def get_warehouse(self, name):
|
||||
pool = Pool()
|
||||
SaleLine = pool.get('sale.line')
|
||||
warehouse = super().get_warehouse(name)
|
||||
if (not warehouse
|
||||
and isinstance(self.origin, SaleLine)
|
||||
and self.origin.warehouse):
|
||||
warehouse = self.origin.warehouse.id
|
||||
return warehouse
|
||||
|
||||
@property
|
||||
def origin_name(self):
|
||||
pool = Pool()
|
||||
SaleLine = pool.get('sale.line')
|
||||
name = super().origin_name
|
||||
if isinstance(self.origin, SaleLine) and self.origin.id >= 0:
|
||||
name = self.origin.sale.rec_name
|
||||
return name
|
||||
|
||||
@classmethod
|
||||
def _get_origin(cls):
|
||||
models = super()._get_origin()
|
||||
models.append('sale.line')
|
||||
return models
|
||||
|
||||
@classmethod
|
||||
def on_delete(cls, lines):
|
||||
pool = Pool()
|
||||
Sale = pool.get('sale.sale')
|
||||
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)
|
||||
sales = set(s for i in invoices for s in i.sales)
|
||||
if sales:
|
||||
with transaction.set_context(
|
||||
queue_batch=context.get('queue_batch', True)):
|
||||
Sale.__queue__.process(sales)
|
||||
return super().on_delete(lines)
|
||||
53
modules/sale/invoice.xml
Normal file
53
modules/sale/invoice.xml
Normal file
@@ -0,0 +1,53 @@
|
||||
<?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="domain"
|
||||
eval="[('sales.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">sale.sale,-1</field>
|
||||
<field name="action" ref="act_invoice_form"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.access" id="access_invoice_sale">
|
||||
<field name="model">account.invoice</field>
|
||||
<field name="group" ref="group_sale"/>
|
||||
<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', []), 'sale.line')]"
|
||||
pyson="1"/>
|
||||
</record>
|
||||
<record model="ir.action.keyword" id="act_invoice_line_form_keyword1">
|
||||
<field name="keyword">form_relate</field>
|
||||
<field name="model">sale.line,-1</field>
|
||||
<field name="action" ref="act_invoice_line_form"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.access" id="access_invoice_line_sale">
|
||||
<field name="model">account.invoice.line</field>
|
||||
<field name="group" ref="group_sale"/>
|
||||
<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>
|
||||
16
modules/sale/ir.py
Normal file
16
modules/sale/ir.py
Normal file
@@ -0,0 +1,16 @@
|
||||
# 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.pool import PoolMeta
|
||||
|
||||
|
||||
class Cron(metaclass=PoolMeta):
|
||||
__name__ = 'ir.cron'
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super().__setup__()
|
||||
cls.method.selection.extend([
|
||||
('sale.sale|cancel_expired_quotation',
|
||||
"Cancel Expired Sale Quotation"),
|
||||
])
|
||||
1871
modules/sale/locale/bg.po
Normal file
1871
modules/sale/locale/bg.po
Normal file
File diff suppressed because it is too large
Load Diff
1737
modules/sale/locale/ca.po
Normal file
1737
modules/sale/locale/ca.po
Normal file
File diff suppressed because it is too large
Load Diff
1809
modules/sale/locale/cs.po
Normal file
1809
modules/sale/locale/cs.po
Normal file
File diff suppressed because it is too large
Load Diff
1743
modules/sale/locale/de.po
Normal file
1743
modules/sale/locale/de.po
Normal file
File diff suppressed because it is too large
Load Diff
1737
modules/sale/locale/es.po
Normal file
1737
modules/sale/locale/es.po
Normal file
File diff suppressed because it is too large
Load Diff
1715
modules/sale/locale/es_419.po
Normal file
1715
modules/sale/locale/es_419.po
Normal file
File diff suppressed because it is too large
Load Diff
1868
modules/sale/locale/et.po
Normal file
1868
modules/sale/locale/et.po
Normal file
File diff suppressed because it is too large
Load Diff
1881
modules/sale/locale/fa.po
Normal file
1881
modules/sale/locale/fa.po
Normal file
File diff suppressed because it is too large
Load Diff
1806
modules/sale/locale/fi.po
Normal file
1806
modules/sale/locale/fi.po
Normal file
File diff suppressed because it is too large
Load Diff
1734
modules/sale/locale/fr.po
Normal file
1734
modules/sale/locale/fr.po
Normal file
File diff suppressed because it is too large
Load Diff
1839
modules/sale/locale/hu.po
Normal file
1839
modules/sale/locale/hu.po
Normal file
File diff suppressed because it is too large
Load Diff
1812
modules/sale/locale/id.po
Normal file
1812
modules/sale/locale/id.po
Normal file
File diff suppressed because it is too large
Load Diff
1864
modules/sale/locale/it.po
Normal file
1864
modules/sale/locale/it.po
Normal file
File diff suppressed because it is too large
Load Diff
1905
modules/sale/locale/lo.po
Normal file
1905
modules/sale/locale/lo.po
Normal file
File diff suppressed because it is too large
Load Diff
1864
modules/sale/locale/lt.po
Normal file
1864
modules/sale/locale/lt.po
Normal file
File diff suppressed because it is too large
Load Diff
1737
modules/sale/locale/nl.po
Normal file
1737
modules/sale/locale/nl.po
Normal file
File diff suppressed because it is too large
Load Diff
1793
modules/sale/locale/pl.po
Normal file
1793
modules/sale/locale/pl.po
Normal file
File diff suppressed because it is too large
Load Diff
1735
modules/sale/locale/pt.po
Normal file
1735
modules/sale/locale/pt.po
Normal file
File diff suppressed because it is too large
Load Diff
1796
modules/sale/locale/ro.po
Normal file
1796
modules/sale/locale/ro.po
Normal file
File diff suppressed because it is too large
Load Diff
1872
modules/sale/locale/ru.po
Normal file
1872
modules/sale/locale/ru.po
Normal file
File diff suppressed because it is too large
Load Diff
1853
modules/sale/locale/sl.po
Normal file
1853
modules/sale/locale/sl.po
Normal file
File diff suppressed because it is too large
Load Diff
1805
modules/sale/locale/tr.po
Normal file
1805
modules/sale/locale/tr.po
Normal file
File diff suppressed because it is too large
Load Diff
1696
modules/sale/locale/uk.po
Normal file
1696
modules/sale/locale/uk.po
Normal file
File diff suppressed because it is too large
Load Diff
1826
modules/sale/locale/zh_CN.po
Normal file
1826
modules/sale/locale/zh_CN.po
Normal file
File diff suppressed because it is too large
Load Diff
79
modules/sale/message.xml
Normal file
79
modules/sale/message.xml
Normal file
@@ -0,0 +1,79 @@
|
||||
<?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_erase_party_pending_sale">
|
||||
<field name="text">You cannot erase party "%(party)s" while they have pending sales with company "%(company)s".</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_sale_invoice_reset_draft">
|
||||
<field name="text">You cannot reset invoice "%(invoice)s" to draft because it was generated by a sale.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_sale_move_reset_draft">
|
||||
<field name="text">You cannot reset move "%(move)s" to draft because it was generated by a sale.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_sale_invalid_method">
|
||||
<field name="text">You cannot use together invoice "%(invoice_method)s" and shipment "%(shipment_method)s" on sale "%(sale)s".</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_sale_invoice_address_required_for_quotation">
|
||||
<field name="text">To get a quote for sale "%(sale)s" you must enter an invoice address.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_sale_shipment_address_required_for_quotation">
|
||||
<field name="text">To get a quote for sale "%(sale)s" you must enter a shipment address.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_sale_warehouse_required_for_quotation">
|
||||
<field name="text">To get a quote for sale "%(sale)s" you must enter a warehouse for the line "%(line)s".</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_sale_delete_cancel">
|
||||
<field name="text">To delete sale "%(sale)s" you must cancel it.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_sale_customer_location_required">
|
||||
<field name="text">To process sale "%(sale)s" you must set a customer location on party "%(party)s".</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_sale_product_missing_account_revenue">
|
||||
<field name="text">To invoice sale "%(sale)s" you must define an account revenue for product "%(product)s".</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_sale_missing_account_revenue">
|
||||
<field name="text">To invoice sale "%(sale)s" you must configure a default account revenue.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_sale_line_delete_cancel_draft">
|
||||
<field name="text">To delete line "%(line)s" you must cancel or reset to draft sale "%(sale)s".</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_sale_line_move_quantity">
|
||||
<field name="text">The sale line "%(line)s" is moving %(extra)s in addition to the %(quantity)s ordered.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_sale_modify_header_draft">
|
||||
<field name="text">To modify the header of sale "%(sale)s", it must be in "draft" state.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_sale_line_create_draft">
|
||||
<field name="text">You cannot add lines to sale "%(sale)s" because it is no longer in a draft state.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_sale_reporting_company">
|
||||
<field name="text">Company</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_sale_reporting_number">
|
||||
<field name="text">#</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_sale_reporting_number_help">
|
||||
<field name="text">Number of sales</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_sale_reporting_revenue">
|
||||
<field name="text">Revenue</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_sale_reporting_revenue_trend">
|
||||
<field name="text">Revenue Trend</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_sale_reporting_currency">
|
||||
<field name="text">Currency</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_sale_reporting_date">
|
||||
<field name="text">Date</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_sale_reporting_time_series">
|
||||
<field name="text">Time Series</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_sale_line_tax_unique">
|
||||
<field name="text">A tax can be added only once to a sale line.</field>
|
||||
</record>
|
||||
</data>
|
||||
</tryton>
|
||||
123
modules/sale/party.py
Normal file
123
modules/sale/party.py
Normal file
@@ -0,0 +1,123 @@
|
||||
# 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
|
||||
from trytond.transaction import Transaction
|
||||
|
||||
customer_currency = fields.Many2One(
|
||||
'currency.currency', "Customer Currency", ondelete='RESTRICT',
|
||||
help="Default currency for sales to this party.")
|
||||
|
||||
|
||||
def get_sale_methods(field_name):
|
||||
@classmethod
|
||||
def func(cls):
|
||||
pool = Pool()
|
||||
Sale = pool.get('sale.sale')
|
||||
return Sale.fields_get([field_name])[field_name]['selection'] + [
|
||||
(None, '')]
|
||||
return func
|
||||
|
||||
|
||||
class Party(CompanyMultiValueMixin, metaclass=PoolMeta):
|
||||
__name__ = 'party.party'
|
||||
|
||||
sale_invoice_method = fields.MultiValue(fields.Selection(
|
||||
'get_sale_invoice_method', "Invoice Method",
|
||||
help="The default sale invoice method for the customer.\n"
|
||||
"Leave empty to use the default value from the configuration."))
|
||||
sale_shipment_method = fields.MultiValue(fields.Selection(
|
||||
'get_sale_shipment_method', "Shipment Method",
|
||||
help="The default sale shipment method for the customer.\n"
|
||||
"Leave empty to use the default value from the configuration."))
|
||||
sale_methods = fields.One2Many(
|
||||
'party.party.sale_method', 'party', "Sale Methods")
|
||||
customer_currency = fields.MultiValue(customer_currency)
|
||||
customer_currencies = fields.One2Many(
|
||||
'party.party.customer_currency', 'party', "Customer Currencies")
|
||||
|
||||
@classmethod
|
||||
def multivalue_model(cls, field):
|
||||
pool = Pool()
|
||||
if field in {'sale_invoice_method', 'sale_shipment_method'}:
|
||||
return pool.get('party.party.sale_method')
|
||||
return super().multivalue_model(field)
|
||||
|
||||
get_sale_invoice_method = get_sale_methods('invoice_method')
|
||||
get_sale_shipment_method = get_sale_methods('shipment_method')
|
||||
|
||||
@classmethod
|
||||
def copy(cls, parties, default=None):
|
||||
default = default.copy() if default else {}
|
||||
if Transaction().check_access:
|
||||
fields = [
|
||||
'sale_methods', 'sale_invoice_method', 'sale_shipment_method']
|
||||
default_values = cls.default_get(fields, with_rec_name=False)
|
||||
for fname in fields:
|
||||
default.setdefault(fname, default_values.get(fname))
|
||||
return super().copy(parties, default=default)
|
||||
|
||||
|
||||
class PartySaleMethod(ModelSQL, CompanyValueMixin):
|
||||
__name__ = 'party.party.sale_method'
|
||||
|
||||
party = fields.Many2One(
|
||||
'party.party', "Party", ondelete='CASCADE',
|
||||
context={
|
||||
'company': Eval('company', -1),
|
||||
},
|
||||
depends={'company'})
|
||||
sale_invoice_method = fields.Selection(
|
||||
'get_sale_invoice_method', "Sale Invoice Method")
|
||||
sale_shipment_method = fields.Selection(
|
||||
'get_sale_shipment_method', "Sale Shipment Method")
|
||||
|
||||
get_sale_invoice_method = get_sale_methods('invoice_method')
|
||||
get_sale_shipment_method = get_sale_methods('shipment_method')
|
||||
|
||||
|
||||
class PartyCustomerCurrency(ModelSQL, ValueMixin):
|
||||
__name__ = 'party.party.customer_currency'
|
||||
party = fields.Many2One(
|
||||
'party.party', "Party", ondelete='CASCADE')
|
||||
customer_currency = customer_currency
|
||||
|
||||
|
||||
class Replace(metaclass=PoolMeta):
|
||||
__name__ = 'party.replace'
|
||||
|
||||
@classmethod
|
||||
def fields_to_replace(cls):
|
||||
return super().fields_to_replace() + [
|
||||
('sale.sale', 'party'),
|
||||
('sale.sale', 'invoice_party'),
|
||||
('sale.sale', 'shipment_party'),
|
||||
]
|
||||
|
||||
|
||||
class Erase(metaclass=PoolMeta):
|
||||
__name__ = 'party.erase'
|
||||
|
||||
def check_erase_company(self, party, company):
|
||||
pool = Pool()
|
||||
Sale = pool.get('sale.sale')
|
||||
super().check_erase_company(party, company)
|
||||
|
||||
sales = Sale.search([
|
||||
['OR',
|
||||
('party', '=', party.id),
|
||||
('shipment_party', '=', party.id),
|
||||
],
|
||||
('company', '=', company.id),
|
||||
('state', 'not in', ['done', 'cancelled']),
|
||||
])
|
||||
if sales:
|
||||
raise EraseError(
|
||||
gettext('sale.msg_erase_party_pending_sale',
|
||||
party=party.rec_name,
|
||||
company=company.rec_name))
|
||||
54
modules/sale/party.xml
Normal file
54
modules/sale/party.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.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_sale_invoice_method">
|
||||
<field name="model">party.party</field>
|
||||
<field name="field">sale_invoice_method</field>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_write" eval="False"/>
|
||||
</record>
|
||||
<record model="ir.model.field.access" id="access_party_sale_invoice_method_group_sale">
|
||||
<field name="model">party.party</field>
|
||||
<field name="field">sale_invoice_method</field>
|
||||
<field name="group" ref="group_sale"/>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_write" eval="True"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.field.access" id="access_party_sale_shipment_method">
|
||||
<field name="model">party.party</field>
|
||||
<field name="field">sale_shipment_method</field>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_write" eval="False"/>
|
||||
</record>
|
||||
<record model="ir.model.field.access" id="access_party_sale_shipment_method_group_sale">
|
||||
<field name="model">party.party</field>
|
||||
<field name="field">sale_shipment_method</field>
|
||||
<field name="group" ref="group_sale"/>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_write" eval="True"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.field.access" id="access_party_customer_currency">
|
||||
<field name="model">party.party</field>
|
||||
<field name="field">customer_currency</field>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_write" eval="False"/>
|
||||
</record>
|
||||
<record model="ir.model.field.access" id="access_party_customer_currency_group_sale">
|
||||
<field name="model">party.party</field>
|
||||
<field name="field">customer_currency</field>
|
||||
<field name="group" ref="group_sale"/>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_write" eval="True"/>
|
||||
</record>
|
||||
</data>
|
||||
</tryton>
|
||||
281
modules/sale/product.py
Normal file
281
modules/sale/product.py
Normal file
@@ -0,0 +1,281 @@
|
||||
# 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 sql import Null
|
||||
|
||||
from trytond.model import ModelSQL, ModelView, ValueMixin, fields
|
||||
from trytond.modules.currency.fields import Monetary
|
||||
from trytond.modules.product import price_digits, round_price
|
||||
from trytond.pool import Pool, PoolMeta
|
||||
from trytond.pyson import Eval, TimeDelta
|
||||
from trytond.transaction import Transaction
|
||||
|
||||
default_lead_time = fields.TimeDelta(
|
||||
"Default Lead Time",
|
||||
domain=['OR',
|
||||
('default_lead_time', '=', None),
|
||||
('default_lead_time', '>=', TimeDelta()),
|
||||
],
|
||||
help="The time from confirming the sales order to sending the "
|
||||
"products.\n"
|
||||
"Used for products without a lead time.")
|
||||
|
||||
|
||||
class Configuration(metaclass=PoolMeta):
|
||||
__name__ = 'product.configuration'
|
||||
|
||||
default_lead_time = fields.MultiValue(default_lead_time)
|
||||
|
||||
|
||||
class DefaultLeadTime(ModelSQL, ValueMixin):
|
||||
__name__ = 'product.configuration.default_lead_time'
|
||||
default_lead_time = default_lead_time
|
||||
|
||||
|
||||
class Template(metaclass=PoolMeta):
|
||||
__name__ = 'product.template'
|
||||
salable = fields.Boolean("Salable")
|
||||
sale_uom = fields.Many2One(
|
||||
'product.uom', "Sale UoM",
|
||||
states={
|
||||
'invisible': ~Eval('salable', False),
|
||||
'required': Eval('salable', False),
|
||||
},
|
||||
domain=[
|
||||
('category', '=', Eval('default_uom_category', -1)),
|
||||
],
|
||||
help="The default Unit of Measure for sales.")
|
||||
lead_time = fields.MultiValue(fields.TimeDelta(
|
||||
"Lead Time",
|
||||
domain=['OR',
|
||||
('lead_time', '=', None),
|
||||
('lead_time', '>=', TimeDelta()),
|
||||
],
|
||||
states={
|
||||
'invisible': ~Eval('salable', False),
|
||||
},
|
||||
help="The time from confirming the sales order to sending the "
|
||||
"products.\n"
|
||||
"If empty the default lead time from the configuration is used."))
|
||||
lead_times = fields.One2Many(
|
||||
'product.lead_time', 'template', "Lead Times")
|
||||
|
||||
@classmethod
|
||||
def multivalue_model(cls, field):
|
||||
pool = Pool()
|
||||
if field == 'lead_time':
|
||||
return pool.get('product.lead_time')
|
||||
return super().multivalue_model(field)
|
||||
|
||||
@fields.depends('default_uom', 'sale_uom', 'salable')
|
||||
def on_change_default_uom(self):
|
||||
try:
|
||||
super().on_change_default_uom()
|
||||
except AttributeError:
|
||||
pass
|
||||
if self.default_uom:
|
||||
if self.sale_uom:
|
||||
if self.default_uom.category != self.sale_uom.category:
|
||||
self.sale_uom = self.default_uom
|
||||
else:
|
||||
self.sale_uom = self.default_uom
|
||||
|
||||
@classmethod
|
||||
def view_attributes(cls):
|
||||
return super().view_attributes() + [
|
||||
('//page[@id="customers"]', 'states', {
|
||||
'invisible': ~Eval('salable'),
|
||||
})]
|
||||
|
||||
|
||||
class ProductLeadTime(ModelSQL, ValueMixin):
|
||||
__name__ = 'product.lead_time'
|
||||
|
||||
template = fields.Many2One(
|
||||
'product.template', "Template", ondelete='CASCADE')
|
||||
lead_time = fields.TimeDelta(
|
||||
"Lead Time",
|
||||
domain=['OR',
|
||||
('lead_time', '=', None),
|
||||
('lead_time', '>=', TimeDelta()),
|
||||
])
|
||||
|
||||
@classmethod
|
||||
def __register__(cls, module_name):
|
||||
pool = Pool()
|
||||
Template = pool.get('product.template')
|
||||
template = Template.__table__()
|
||||
table = cls.__table__()
|
||||
|
||||
super().__register__(module_name)
|
||||
|
||||
cursor = Transaction().connection.cursor()
|
||||
template_h = Template.__table_handler__(module_name)
|
||||
if template_h.column_exist('lead_time'):
|
||||
cursor.execute(*table.insert(
|
||||
columns=[table.template, table.lead_time],
|
||||
values=template.select(
|
||||
template.id, template.lead_time,
|
||||
where=template.lead_time != Null)))
|
||||
template_h.drop_column('lead_time')
|
||||
|
||||
|
||||
class Product(metaclass=PoolMeta):
|
||||
__name__ = 'product.product'
|
||||
|
||||
sale_price_uom = fields.Function(Monetary(
|
||||
"Sale Price", digits=price_digits), 'get_sale_price_uom')
|
||||
|
||||
@classmethod
|
||||
def get_sale_price_uom(cls, products, name):
|
||||
quantity = Transaction().context.get('quantity') or 0
|
||||
return cls.get_sale_price(products, quantity=quantity)
|
||||
|
||||
def _get_sale_unit_price(self, quantity=0):
|
||||
return self.list_price_used
|
||||
|
||||
@classmethod
|
||||
def get_sale_price(cls, products, quantity=0):
|
||||
'''
|
||||
Return the sale price for products and quantity.
|
||||
It uses if exists from the context:
|
||||
uom: the unit of measure or the sale uom of the product
|
||||
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')
|
||||
|
||||
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.get('currency'))
|
||||
company = None
|
||||
if context.get('company'):
|
||||
company = Company(context['company'])
|
||||
date = context.get('sale_date') or Date.today()
|
||||
|
||||
for product in products:
|
||||
unit_price = product._get_sale_unit_price(quantity=quantity)
|
||||
if unit_price is not None:
|
||||
if uom and product.default_uom.category == uom.category:
|
||||
unit_price = Uom.compute_price(
|
||||
product.default_uom, unit_price, uom)
|
||||
elif product.sale_uom:
|
||||
unit_price = Uom.compute_price(
|
||||
product.default_uom, unit_price, product.sale_uom)
|
||||
if currency and company and unit_price is not None:
|
||||
if company.currency != currency:
|
||||
with Transaction().set_context(date=date):
|
||||
unit_price = Currency.compute(
|
||||
company.currency, unit_price,
|
||||
currency, round=False)
|
||||
if unit_price is not None:
|
||||
unit_price = round_price(unit_price)
|
||||
prices[product.id] = unit_price
|
||||
return prices
|
||||
|
||||
@property
|
||||
def lead_time_used(self):
|
||||
pool = Pool()
|
||||
Configuration = pool.get('product.configuration')
|
||||
if self.lead_time is None:
|
||||
with Transaction().set_context(self._context):
|
||||
config = Configuration(1)
|
||||
return config.get_multivalue('default_lead_time')
|
||||
else:
|
||||
return self.lead_time
|
||||
|
||||
def compute_shipping_date(self, date=None):
|
||||
'''
|
||||
Compute the shipping date at the given date
|
||||
'''
|
||||
Date = Pool().get('ir.date')
|
||||
|
||||
if not date:
|
||||
with Transaction().set_context(context=self._context):
|
||||
date = Date.today()
|
||||
|
||||
lead_time = self.lead_time_used
|
||||
if lead_time is None:
|
||||
return datetime.date.max
|
||||
return date + lead_time
|
||||
|
||||
|
||||
class SaleContext(ModelView):
|
||||
__name__ = 'product.sale.context'
|
||||
|
||||
locations = fields.Many2Many(
|
||||
'stock.location', None, None, "Warehouses",
|
||||
domain=[('type', '=', 'warehouse')])
|
||||
company = fields.Many2One('company.company', "Company")
|
||||
currency = fields.Many2One('currency.currency', "Currency")
|
||||
customer = fields.Many2One(
|
||||
'party.party', "Customer",
|
||||
context={
|
||||
'company': Eval('company', -1),
|
||||
},
|
||||
depends={'company'})
|
||||
sale_date = fields.Date("Sale Date")
|
||||
quantity = fields.Float("Quantity")
|
||||
|
||||
stock_date_end = fields.Function(
|
||||
fields.Date("Stock End Date"),
|
||||
'on_change_with_stock_date_end')
|
||||
|
||||
@classmethod
|
||||
def default_locations(cls):
|
||||
pool = Pool()
|
||||
Location = pool.get('stock.location')
|
||||
context = Transaction().context
|
||||
locations = context.get('locations')
|
||||
if locations is None:
|
||||
locations = []
|
||||
warehouse = Location.get_default_warehouse()
|
||||
if warehouse:
|
||||
locations.append(warehouse)
|
||||
return locations
|
||||
|
||||
@classmethod
|
||||
def default_company(cls):
|
||||
return Transaction().context.get('company')
|
||||
|
||||
@classmethod
|
||||
def default_currency(cls):
|
||||
pool = Pool()
|
||||
Company = pool.get('company.company')
|
||||
context = Transaction().context
|
||||
currency = context.get('currency')
|
||||
if currency is None:
|
||||
company_id = cls.default_company()
|
||||
if company_id is not None and company_id >= 0:
|
||||
company = Company(company_id)
|
||||
currency = company.currency.id
|
||||
return currency
|
||||
|
||||
@classmethod
|
||||
def default_customer(cls):
|
||||
return Transaction().context.get('customer')
|
||||
|
||||
@classmethod
|
||||
def default_sale_date(cls):
|
||||
return Transaction().context.get('sale_date')
|
||||
|
||||
@classmethod
|
||||
def default_quantity(cls):
|
||||
return Transaction().context.get('quantity')
|
||||
|
||||
@fields.depends('sale_date')
|
||||
def on_change_with_stock_date_end(self, name=None):
|
||||
return self.sale_date
|
||||
62
modules/sale/product.xml
Normal file
62
modules/sale/product.xml
Normal file
@@ -0,0 +1,62 @@
|
||||
<?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_sale_line">
|
||||
<field name="model">product.product</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="priority" eval="20"/>
|
||||
<field name="name">product_list_sale_line</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.act_window" id="act_product">
|
||||
<field name="name">Products</field>
|
||||
<field name="res_model">product.product</field>
|
||||
<field name="domain" eval="[('salable', '=', True)]" pyson="1"/>
|
||||
<field name="context_model">product.sale.context</field>
|
||||
<field
|
||||
name="context"
|
||||
eval="{'stock_skip_warehouse': True, 'with_childs': True}"
|
||||
pyson="1"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_product_view1">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="product_view_list_sale_line"/>
|
||||
<field name="act_window" ref="act_product"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_product_view2">
|
||||
<field name="sequence" eval="20"/>
|
||||
<field name="view" ref="product.product_view_form"/>
|
||||
<field name="act_window" ref="act_product"/>
|
||||
</record>
|
||||
<menuitem
|
||||
parent="menu_sale"
|
||||
action="act_product"
|
||||
sequence="50"
|
||||
id="menu_product"/>
|
||||
|
||||
<record model="ir.ui.view" id="product_sale_context_view_form">
|
||||
<field name="model">product.sale.context</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">product_sale_context_form</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="product_configuration_view_form">
|
||||
<field name="model">product.configuration</field>
|
||||
<field name="inherit" ref="product.product_configuration_view_form"/>
|
||||
<field name="name">product_configuration_form</field>
|
||||
</record>
|
||||
</data>
|
||||
</tryton>
|
||||
964
modules/sale/sale.fodt
Normal file
964
modules/sale/sale.fodt
Normal file
@@ -0,0 +1,964 @@
|
||||
<?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/24.2.7.2$Linux_X86_64 LibreOffice_project/420$Build-2</meta:generator><meta:creation-date>2008-06-07T15:28:22</meta:creation-date><dc:date>2009-01-10T16:03:33</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="7" meta:paragraph-count="99" meta:word-count="236" meta:character-count="2719" meta:non-whitespace-character-count="2581"/><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">39582</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">48103</config:config-item>
|
||||
<config:config-item config:name="ViewAreaHeight" config:type="long">22915</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">5339</config:config-item>
|
||||
<config:config-item config:name="ViewTop" config:type="long">46011</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">39582</config:config-item>
|
||||
<config:config-item config:name="VisibleRight" config:type="long">48101</config:config-item>
|
||||
<config:config-item config:name="VisibleBottom" config:type="long">62495</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="PrintProspectRTL" 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="PrintDrawings" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="PrintFaxName" config:type="string"/>
|
||||
<config:config-item config:name="PrintReversed" 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="PrintHiddenText" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="PrintEmptyPages" 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="NoNumberingShowFollowBy" 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="ImagePreferredDPI" config:type="int">0</config:config-item>
|
||||
<config:config-item config:name="FootnoteInColumnToPageEnd" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="GutterAtTop" 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="FrameAutowidthWithMorePara" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="SubtractFlysAnchoredAtFlys" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="SurroundTextWrapSmall" 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="TreatSingleColumnBreakAsPageBreak" 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="AutoFirstLineIndentDisregardLineSpace" 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="EmbedAsianScriptFonts" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="PrintTextPlaceholder" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="ApplyTextAttrToEmptyLineAtEndOfParagraph" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="EmbedSystemFonts" 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="DisableOffPagePositioning" 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="TabOverflow" 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="AddVerticalFrameOffsets" 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="AddFrameOffsets" 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="TableRowKeep" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="ApplyParagraphMarkFormatToNumbering" 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="JustifyLinesWithShrinking" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="RsidRoot" config:type="int">204959</config:config-item>
|
||||
<config:config-item config:name="PrintProspect" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="CollapseEmptyCellPara" 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="CurrentDatabaseCommand" config:type="string"/>
|
||||
<config:config-item config:name="CurrentDatabaseDataSource" config:type="string"/>
|
||||
<config:config-item config:name="SaveThumbnail" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="EmbeddedDatabaseName" config:type="string"/>
|
||||
<config:config-item config:name="UnbreakableNumberings" 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="MsWordCompTrailingBlanks" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="PrintTables" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="PrintLeftPages" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="AddParaTableSpacing" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="PrinterPaperFromSetup" 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="ChartAutoUpdate" 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="UseOldNumbering" 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="FieldAutoUpdate" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="PropLineSpacingShrinksFirstLine" 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="PrintRightPages" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="DoNotCaptureDrawObjsOnPage" 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="HeaderSpacingBelowLastPara" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="SaveVersionOnClose" 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="MathBaselineAlignment" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="AlignTabStopPosition" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="UseFormerLineSpacing" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="PrintSingleJobs" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="PrinterName" config:type="string"/>
|
||||
<config:config-item config:name="AddParaLineSpacingToTableCells" 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="DoNotJustifyLinesWithManualBreak" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="CharacterCompressionType" config:type="short">0</config:config-item>
|
||||
<config:config-item config:name="IsLabelDocument" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="EmbedLatinScriptFonts" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="PrinterSetup" config:type="base64Binary"/>
|
||||
<config:config-item config:name="UseVariableWidthNBSP" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="EmbedOnlyUsedFonts" 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="AddParaTableSpacingAtStart" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="InvertBorderSpacing" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="ProtectFields" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="AddExternalLeading" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="LinkUpdateMode" config:type="short">1</config:config-item>
|
||||
<config:config-item config:name="UseFormerObjectPositioning" 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="UseFormerTextWrapping" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="ConsiderTextWrapOnObjPos" 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="IgnoreFirstLineIndentInNumbering" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="NoGapAfterNoteNumber" 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="DoNotResetParaAttrsForNumFont" 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="ClipAsCharacterAnchoredWriterFlyFrames" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="DropCapPunctuation" 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="RedlineProtectionKey" config:type="base64Binary"/>
|
||||
<config:config-item config:name="TabsRelativeToIndent" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="Rsid" config:type="int">2203069</config:config-item>
|
||||
<config:config-item config:name="UpdateFromTemplate" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="ProtectForm" 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="SmallCapsPercentage66" 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>
|
||||
<draw:gradient draw:name="gradient" draw:style="linear" draw:start-color="#000000" draw:end-color="#ffffff" draw:start-intensity="100%" draw:end-intensity="100%" draw:angle="0deg" draw:border="0%">
|
||||
<loext:gradient-stop svg:offset="0" loext:color-type="rgb" loext:color-value="#000000"/>
|
||||
<loext:gradient-stop svg:offset="1" loext:color-type="rgb" loext:color-value="#ffffff"/></draw:gradient>
|
||||
<draw:hatch draw:name="hatch" draw:style="single" draw:color="#3465a4" draw:distance="0.02cm" draw:rotation="0"/>
|
||||
<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" draw:fill-color="#99ccff" fo:padding="0.15cm" fo:border="0.06pt solid #000000"/>
|
||||
</style:style>
|
||||
<text:outline-style style:name="Outline">
|
||||
<text:outline-level-style text:level="1" loext:num-list-format="%1%" style:num-format="">
|
||||
<style:list-level-properties text:min-label-distance="0.381cm"/>
|
||||
</text:outline-level-style>
|
||||
<text:outline-level-style text:level="2" loext:num-list-format="%2%" style:num-format="">
|
||||
<style:list-level-properties text:min-label-distance="0.381cm"/>
|
||||
</text:outline-level-style>
|
||||
<text:outline-level-style text:level="3" loext:num-list-format="%3%" style:num-format="">
|
||||
<style:list-level-properties text:min-label-distance="0.381cm"/>
|
||||
</text:outline-level-style>
|
||||
<text:outline-level-style text:level="4" loext:num-list-format="%4%" style:num-format="">
|
||||
<style:list-level-properties text:min-label-distance="0.381cm"/>
|
||||
</text:outline-level-style>
|
||||
<text:outline-level-style text:level="5" loext:num-list-format="%5%" style:num-format="">
|
||||
<style:list-level-properties text:min-label-distance="0.381cm"/>
|
||||
</text:outline-level-style>
|
||||
<text:outline-level-style text:level="6" loext:num-list-format="%6%" style:num-format="">
|
||||
<style:list-level-properties text:min-label-distance="0.381cm"/>
|
||||
</text:outline-level-style>
|
||||
<text:outline-level-style text:level="7" loext:num-list-format="%7%" style:num-format="">
|
||||
<style:list-level-properties text:min-label-distance="0.381cm"/>
|
||||
</text:outline-level-style>
|
||||
<text:outline-level-style text:level="8" loext:num-list-format="%8%" style:num-format="">
|
||||
<style:list-level-properties text:min-label-distance="0.381cm"/>
|
||||
</text:outline-level-style>
|
||||
<text:outline-level-style text:level="9" loext:num-list-format="%9%" style:num-format="">
|
||||
<style:list-level-properties text:min-label-distance="0.381cm"/>
|
||||
</text:outline-level-style>
|
||||
<text:outline-level-style text:level="10" loext:num-list-format="%10%" style:num-format="">
|
||||
<style:list-level-properties text:min-label-distance="0.381cm"/>
|
||||
</text:outline-level-style>
|
||||
</text:outline-style>
|
||||
<text:notes-configuration text:note-class="footnote" style:num-format="1" text:start-value="0" text:footnotes-position="page" text:start-numbering-at="document"/>
|
||||
<text:notes-configuration text:note-class="endnote" style:num-format="i" text:start-value="0"/>
|
||||
<text:linenumbering-configuration text:number-lines="false" text:offset="0.499cm" style:num-format="1" text:number-position="left" text:increment="5"/>
|
||||
<loext:theme loext:name="Office Theme">
|
||||
<loext:theme-colors loext:name="LibreOffice">
|
||||
<loext:color loext:name="dark1" loext:color="#000000"/>
|
||||
<loext:color loext:name="light1" loext:color="#ffffff"/>
|
||||
<loext:color loext:name="dark2" loext:color="#000000"/>
|
||||
<loext:color loext:name="light2" loext:color="#ffffff"/>
|
||||
<loext:color loext:name="accent1" loext:color="#18a303"/>
|
||||
<loext:color loext:name="accent2" loext:color="#0369a3"/>
|
||||
<loext:color loext:name="accent3" loext:color="#a33e03"/>
|
||||
<loext:color loext:name="accent4" loext:color="#8e03a3"/>
|
||||
<loext:color loext:name="accent5" loext:color="#c99c00"/>
|
||||
<loext:color loext:name="accent6" loext:color="#c9211e"/>
|
||||
<loext:color loext:name="hyperlink" loext:color="#0000ee"/>
|
||||
<loext:color loext:name="followed-hyperlink" loext:color="#551a8b"/>
|
||||
</loext:theme-colors>
|
||||
</loext:theme>
|
||||
</office:styles>
|
||||
<office:automatic-styles>
|
||||
<style:style style:name="Table1" style:family="table">
|
||||
<style:table-properties style:width="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="00066dfa"/>
|
||||
</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="00066dfa"/>
|
||||
</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="00066dfa" style:font-size-asian="12pt" style:font-size-complex="12pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="P4" 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="001be9bb" style:font-size-asian="12pt" style:font-size-complex="12pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="P5" 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="P6" style:family="paragraph" style:parent-style-name="Header">
|
||||
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
|
||||
<style:text-properties officeooo:paragraph-rsid="00066dfa"/>
|
||||
</style:style>
|
||||
<style:style style:name="P7" style:family="paragraph" style:parent-style-name="Footer">
|
||||
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
|
||||
<style:text-properties officeooo:paragraph-rsid="00066dfa"/>
|
||||
</style:style>
|
||||
<style:style style:name="P8" 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="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="00066dfa" style:font-size-asian="12pt" style:font-size-complex="12pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="P10" style:family="paragraph" style:parent-style-name="Header">
|
||||
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false"/>
|
||||
<style:text-properties fo:font-size="12pt" officeooo:paragraph-rsid="001be9bb" style:font-size-asian="12pt" style:font-size-complex="12pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="P11" style:family="paragraph" style:parent-style-name="Frame_20_contents">
|
||||
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false"/>
|
||||
</style:style>
|
||||
<style:style style:name="P12" style:family="paragraph" style:parent-style-name="Text_20_body">
|
||||
<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="Heading_20_2">
|
||||
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false"/>
|
||||
</style:style>
|
||||
<style:style style:name="P14" style:family="paragraph" style:parent-style-name="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="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="0011a01d"/>
|
||||
</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="0011a01d"/>
|
||||
</style:style>
|
||||
<style:style style:name="P17" 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="000ce218"/>
|
||||
</style:style>
|
||||
<style:style style:name="P18" 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="P19" 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="P20" 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="0008048e"/>
|
||||
</style:style>
|
||||
<style:style style:name="P21" 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="P22" 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" officeooo:paragraph-rsid="001ac103"/>
|
||||
</style:style>
|
||||
<style:style style:name="P23" style:family="paragraph" style:parent-style-name="Text_20_body">
|
||||
<style:text-properties officeooo:rsid="0007edf0" officeooo:paragraph-rsid="0007edf0"/>
|
||||
</style:style>
|
||||
<style:style style:name="P24" style:family="paragraph" style:parent-style-name="Text_20_body" style:master-page-name="">
|
||||
<loext:graphic-properties draw:fill-gradient-name="gradient" draw:fill-hatch-name="hatch"/>
|
||||
<style:paragraph-properties style:page-number="auto" fo:keep-with-next="auto"/>
|
||||
<style:text-properties officeooo:rsid="0007edf0" officeooo:paragraph-rsid="0007edf0"/>
|
||||
</style:style>
|
||||
<style:style style:name="P25" 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="P26" style:family="paragraph" style:parent-style-name="Text_20_body" style:master-page-name="">
|
||||
<loext:graphic-properties draw:fill-gradient-name="gradient" draw:fill-hatch-name="hatch"/>
|
||||
<style:paragraph-properties style:page-number="auto" fo:keep-with-next="always"/>
|
||||
</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-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-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="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="0014b27d"/>
|
||||
</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="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="0011a01d" style:font-size-asian="5.25pt" style:font-size-complex="6pt"/>
|
||||
</style:style>
|
||||
<style:style style:name="P32" style:family="paragraph" style:parent-style-name="Text_20_body" style:master-page-name="">
|
||||
<loext:graphic-properties draw:fill-gradient-name="gradient" draw:fill-hatch-name="hatch"/>
|
||||
<style:paragraph-properties style:page-number="auto" fo:keep-with-next="auto"/>
|
||||
<style:text-properties officeooo:paragraph-rsid="001ac103"/>
|
||||
</style:style>
|
||||
<style:style style:name="P33" style:family="paragraph" style:parent-style-name="Text_20_body" style:master-page-name="">
|
||||
<loext:graphic-properties draw:fill-gradient-name="gradient" draw:fill-hatch-name="hatch"/>
|
||||
<style:paragraph-properties style:page-number="auto" fo:break-before="column" fo:keep-with-next="auto"/>
|
||||
<style:text-properties officeooo:paragraph-rsid="001ac103"/>
|
||||
</style:style>
|
||||
<style:style style:name="P34" style:family="paragraph" style:parent-style-name="Text_20_body">
|
||||
<loext:graphic-properties draw:fill-gradient-name="gradient" draw:fill-hatch-name="hatch"/>
|
||||
<style:text-properties officeooo:paragraph-rsid="001ac103"/>
|
||||
</style:style>
|
||||
<style:style style:name="P35" style:family="paragraph" style:parent-style-name="Text_20_body">
|
||||
<loext:graphic-properties draw:fill-gradient-name="gradient" draw:fill-hatch-name="hatch"/>
|
||||
<style:paragraph-properties fo:margin-left="1cm" fo:margin-right="0cm" fo:text-indent="0cm" style:auto-text-indent="false" fo:keep-with-next="auto"/>
|
||||
<style:text-properties officeooo:rsid="001ac103" officeooo:paragraph-rsid="001ac103"/>
|
||||
</style:style>
|
||||
<style:style style:name="P36" style:family="paragraph" style:parent-style-name="Text_20_body" style:master-page-name="">
|
||||
<loext:graphic-properties draw:fill-gradient-name="gradient" draw:fill-hatch-name="hatch"/>
|
||||
<style:paragraph-properties style:page-number="auto" fo:break-before="column" fo:keep-with-next="auto"/>
|
||||
<style:text-properties officeooo:rsid="001ac103" officeooo:paragraph-rsid="001ac103"/>
|
||||
</style:style>
|
||||
<style:style style:name="P37" style:family="paragraph" style:parent-style-name="Text_20_body">
|
||||
<loext:graphic-properties draw:fill-gradient-name="gradient" draw:fill-hatch-name="hatch"/>
|
||||
<style:text-properties officeooo:rsid="002014be" officeooo:paragraph-rsid="002014be"/>
|
||||
</style:style>
|
||||
<style:style style:name="T1" style:family="text">
|
||||
<style:text-properties officeooo:rsid="001ac103"/>
|
||||
</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="P4"><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="6">
|
||||
<draw:text-box fo:min-height="3cm">
|
||||
<text:p text:style-name="P5"/>
|
||||
</draw:text-box>
|
||||
</draw:frame></text:p>
|
||||
<text:p text:style-name="P4"><text:placeholder text:placeholder-type="text"></when></text:placeholder></text:p>
|
||||
<text:p text:style-name="P4"><text:placeholder text:placeholder-type="text"><when test="company"></text:placeholder></text:p>
|
||||
<text:p text:style-name="P4"><text:placeholder text:placeholder-type="text"><company.rec_name></text:placeholder></text:p>
|
||||
<text:p text:style-name="P4"><text:placeholder text:placeholder-type="text"></when></text:placeholder></text:p>
|
||||
<text:p text:style-name="P4"><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="P25"><text:placeholder text:placeholder-type="text"><for each="sale in records"></text:placeholder></text:p>
|
||||
<text:p text:style-name="P31"/>
|
||||
<text:p text:style-name="P15"><text:placeholder text:placeholder-type="text"><replace text:p="set_lang(sale.party.lang)"></text:placeholder></text:p>
|
||||
<text:p text:style-name="P15"><text:placeholder text:placeholder-type="text"><replace text:p="sale.set_lang(sale.party.lang)"></text:placeholder></text:p>
|
||||
<text:p text:style-name="P14"><text:placeholder text:placeholder-type="text"><for each="line in sale.report_address.splitlines()"></text:placeholder></text:p>
|
||||
<text:p text:style-name="P14"><text:placeholder text:placeholder-type="text"><line></text:placeholder></text:p>
|
||||
<text:p text:style-name="P14"><text:placeholder text:placeholder-type="text"></for></text:placeholder></text:p>
|
||||
<text:p text:style-name="P14"><text:placeholder text:placeholder-type="text"><if test="sale.party.tax_identifier"></text:placeholder></text:p>
|
||||
<text:p text:style-name="P14"><text:placeholder text:placeholder-type="text"><sale.party.tax_identifier.type_string></text:placeholder>: <text:placeholder text:placeholder-type="text"><sale.party.tax_identifier.code></text:placeholder></text:p>
|
||||
<text:p text:style-name="P14"><text:placeholder text:placeholder-type="text"></if></text:placeholder></text:p>
|
||||
<text:p text:style-name="P12"><text:placeholder text:placeholder-type="text"><choose test=""></text:placeholder></text:p>
|
||||
<text:p text:style-name="P21"><text:placeholder text:placeholder-type="text"><when test="sale.state == 'draft'"></text:placeholder></text:p>
|
||||
<text:p text:style-name="P30">Draft Sale Order</text:p>
|
||||
<text:p text:style-name="P21"><text:placeholder text:placeholder-type="text"></when></text:placeholder></text:p>
|
||||
<text:p text:style-name="P21"><text:placeholder text:placeholder-type="text"><when test="sale.state == 'quotation'"></text:placeholder></text:p>
|
||||
<text:p text:style-name="P30"><text:soft-page-break/>Quotation No: <text:placeholder text:placeholder-type="text"><sale.full_number></text:placeholder></text:p>
|
||||
<text:p text:style-name="P21"><text:placeholder text:placeholder-type="text"></when></text:placeholder></text:p>
|
||||
<text:p text:style-name="P21"><text:placeholder text:placeholder-type="text"><otherwise test=""></text:placeholder></text:p>
|
||||
<text:p text:style-name="P30">Sale Order No: <text:placeholder text:placeholder-type="text"><sale.full_number></text:placeholder></text:p>
|
||||
<text:p text:style-name="P21"><text:placeholder text:placeholder-type="text"></otherwise></text:placeholder></text:p>
|
||||
<text:p text:style-name="P22"><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="P26">Description: <text:placeholder text:placeholder-type="text"><sale.description or ''></text:placeholder></text:p>
|
||||
<text:p text:style-name="P24">Reference: <text:placeholder text:placeholder-type="text"><sale.reference or ''></text:placeholder></text:p>
|
||||
<text:p text:style-name="P32">Date: <text:placeholder text:placeholder-type="text"><format_date((sale.quotation_date if sale.state in ['draft', 'quotation'] and sale.quotation_date else sale.sale_date) or today, sale.party.lang)></text:placeholder></text:p>
|
||||
<text:p text:style-name="P34"><text:placeholder text:placeholder-type="text"><if test="sale.state in ['draft', 'quotation'] and sale.quotation_expire"></text:placeholder></text:p>
|
||||
<text:p text:style-name="P37">Expiration Date: <text:placeholder text:placeholder-type="text"><format_date(sale.quotation_expire, sale.party.lang)></text:placeholder></text:p>
|
||||
<text:p text:style-name="P34"><text:placeholder text:placeholder-type="text"></if></text:placeholder></text:p>
|
||||
<text:p text:style-name="P36">Delivery Address:</text:p>
|
||||
<text:p text:style-name="P35"><text:placeholder text:placeholder-type="text"><for each="line in sale.delivery_full_address.splitlines()"></text:placeholder></text:p>
|
||||
<text:p text:style-name="P35"><text:placeholder text:placeholder-type="text"><line></text:placeholder></text:p>
|
||||
<text:p text:style-name="P35"><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 sale.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>
|
||||
<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="P27"><text:placeholder text:placeholder-type="text"><if test="line.product_name"></text:placeholder></text:p>
|
||||
<text:p text:style-name="P27"><text:placeholder text:placeholder-type="text"><line.product_name></text:placeholder></text:p>
|
||||
<text:p text:style-name="P27"><text:placeholder text:placeholder-type="text"></if></text:placeholder></text:p>
|
||||
<text:p text:style-name="P27"><text:placeholder text:placeholder-type="text"><if test="line.description"></text:placeholder></text:p>
|
||||
<text:p text:style-name="P27"><text:placeholder text:placeholder-type="text"><for each="description in line.description.split('\n')"></text:placeholder></text:p>
|
||||
<text:p text:style-name="P29"><text:placeholder text:placeholder-type="text"><description></text:placeholder></text:p>
|
||||
<text:p text:style-name="P29"><text:placeholder text:placeholder-type="text"></for></text:placeholder></text:p>
|
||||
<text:p text:style-name="P29"><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="P19"><text:placeholder text:placeholder-type="text"><format_number_symbol(line.quantity, sale.party.lang, line.unit, digits=line.unit.digits) if line.unit else format_number(line.quantity, sale.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="P20"><text:placeholder text:placeholder-type="text"><format_currency(line.unit_price, sale.party.lang, sale.currency, digits=line.__class__.unit_price.digits[1])></text:placeholder></text:p>
|
||||
</table:table-cell>
|
||||
<table:table-cell table:style-name="Table1.B5" office:value-type="string">
|
||||
<text:p text:style-name="P8"><text:placeholder text:placeholder-type="text"><for each="tax in line.taxes"></text:placeholder></text:p>
|
||||
<text:p text:style-name="P8"><text:placeholder text:placeholder-type="text"><tax.description></text:placeholder></text:p>
|
||||
<text:p text:style-name="P8"><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="P19"><text:placeholder text:placeholder-type="text"><format_currency(line.amount, sale.party.lang, sale.currency)></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="P13"><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="P19"><text:placeholder text:placeholder-type="text"><format_currency(line.amount, sale.party.lang, sale.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="P27"><text:placeholder text:placeholder-type="text"><for each="description in (line.description or '').split('\n')"></text:placeholder></text:p>
|
||||
<text:p text:style-name="P27"><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>
|
||||
</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"/>
|
||||
<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="P18">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="P19"><text:placeholder text:placeholder-type="text"><format_currency(sale.untaxed_amount, sale.party.lang, sale.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="P18">Taxes:</text:p>
|
||||
</table:table-cell>
|
||||
<table:table-cell table:style-name="Table4.B2" office:value-type="string">
|
||||
<text:p text:style-name="P19"><text:placeholder text:placeholder-type="text"><format_currency(sale.tax_amount, sale.party.lang, sale.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="P18">Total:</text:p>
|
||||
</table:table-cell>
|
||||
<table:table-cell table:style-name="Table4.B2" office:value-type="string">
|
||||
<text:p text:style-name="P19"><text:placeholder text:placeholder-type="text"><format_currency(sale.total_amount, sale.party.lang, sale.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:soft-page-break/><text:placeholder text:placeholder-type="text"><for each="comment in (sale.comment or '').split('\n')"></text:placeholder></text:p>
|
||||
<text:p text:style-name="Text_20_body"><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>
|
||||
2619
modules/sale/sale.py
Normal file
2619
modules/sale/sale.py
Normal file
File diff suppressed because it is too large
Load Diff
565
modules/sale/sale.xml
Normal file
565
modules/sale/sale.xml
Normal file
@@ -0,0 +1,565 @@
|
||||
<?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_sale">
|
||||
<field name="name">Sales</field>
|
||||
</record>
|
||||
<record model="res.group" id="group_sale_admin">
|
||||
<field name="name">Sales Administrator</field>
|
||||
<field name="parent" ref="group_sale"/>
|
||||
</record>
|
||||
<record model="res.user-res.group" id="user_admin_group_sale">
|
||||
<field name="user" ref="res.user_admin"/>
|
||||
<field name="group" ref="group_sale"/>
|
||||
</record>
|
||||
<record model="res.user-res.group" id="user_admin_group_sale_admin">
|
||||
<field name="user" ref="res.user_admin"/>
|
||||
<field name="group" ref="group_sale_admin"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.icon" id="sale_icon">
|
||||
<field name="name">tryton-sale</field>
|
||||
<field name="path">icons/tryton-sale.svg</field>
|
||||
</record>
|
||||
|
||||
<menuitem
|
||||
name="Sales"
|
||||
sequence="80"
|
||||
id="menu_sale"
|
||||
icon="tryton-sale"/>
|
||||
<record model="ir.ui.menu-res.group" id="menu_sale_group_sale">
|
||||
<field name="menu" ref="menu_sale"/>
|
||||
<field name="group" ref="group_sale"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.wizard" id="wizard_shipment_handle_exception">
|
||||
<field name="name">Handle Shipment Exception</field>
|
||||
<field name="wiz_name">sale.handle.shipment.exception</field>
|
||||
<field name="model">sale.sale</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.wizard" id="wizard_invoice_handle_exception">
|
||||
<field name="name">Handle Invoice Exception</field>
|
||||
<field name="wiz_name">sale.handle.invoice.exception</field>
|
||||
<field name="model">sale.sale</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="sale_view_form">
|
||||
<field name="model">sale.sale</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">sale_form</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="sale_view_tree">
|
||||
<field name="model">sale.sale</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="name">sale_tree</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="handle_shipment_exception_ask_view_form">
|
||||
<field name="model">sale.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">sale.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_shipment_form">
|
||||
<field name="name">Shipments</field>
|
||||
<field name="res_model">stock.shipment.out</field>
|
||||
<field name="domain"
|
||||
eval="[If(Eval('active_ids', []) == [Eval('active_id')], ('moves.sale', '=', Eval('active_id')), ('moves.sale', '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">sale.sale,-1</field>
|
||||
<field name="action" ref="act_shipment_form"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window" id="act_return_form">
|
||||
<field name="name">Returns</field>
|
||||
<field name="res_model">stock.shipment.out.return</field>
|
||||
<field name="domain"
|
||||
eval="[If(Eval('active_ids', []) == [Eval('active_id')], ('moves.sale', '=', Eval('active_id')), ('moves.sale', '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">sale.sale,-1</field>
|
||||
<field name="action" ref="act_return_form"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.act_window" id="act_sale_form">
|
||||
<field name="name">Sales</field>
|
||||
<field name="res_model">sale.sale</field>
|
||||
<field name="search_value"></field>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_sale_form_view1">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="sale_view_tree"/>
|
||||
<field name="act_window" ref="act_sale_form"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_sale_form_view2">
|
||||
<field name="sequence" eval="20"/>
|
||||
<field name="view" ref="sale_view_form"/>
|
||||
<field name="act_window" ref="act_sale_form"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.domain" id="act_sale_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_sale_form"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.domain" id="act_sale_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_sale_form"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.domain" id="act_sale_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_sale_form"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.domain" id="act_sale_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_sale_form"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.domain" id="act_sale_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_sale_form"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.domain" id="act_sale_form_domain_all">
|
||||
<field name="name">All</field>
|
||||
<field name="sequence" eval="9999"/>
|
||||
<field name="domain"></field>
|
||||
<field name="act_window" ref="act_sale_form"/>
|
||||
</record>
|
||||
|
||||
<menuitem
|
||||
parent="menu_sale"
|
||||
action="act_sale_form"
|
||||
sequence="10"
|
||||
id="menu_sale_form"/>
|
||||
|
||||
<record model="ir.action.act_window" id="act_sale_relate">
|
||||
<field name="name">Sales</field>
|
||||
<field name="res_model">sale.sale</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_sale_relate_view1">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="sale_view_tree"/>
|
||||
<field name="act_window" ref="act_sale_relate"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_sale_relate_view2">
|
||||
<field name="sequence" eval="20"/>
|
||||
<field name="view" ref="sale_view_form"/>
|
||||
<field name="act_window" ref="act_sale_relate"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.domain" id="act_sale_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_sale_relate"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.domain" id="act_sale_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_sale_relate"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.domain" id="act_sale_relate_all">
|
||||
<field name="name">All</field>
|
||||
<field name="sequence" eval="9999"/>
|
||||
<field name="domain"></field>
|
||||
<field name="act_window" ref="act_sale_relate"/>
|
||||
</record>
|
||||
<record model="ir.action.keyword" id="act_sale_relate_keyword_party">
|
||||
<field name="keyword">form_relate</field>
|
||||
<field name="model">party.party,-1</field>
|
||||
<field name="action" ref="act_sale_relate"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.act_window" id="act_sale_relate_simple">
|
||||
<field name="name">Sales</field>
|
||||
<field name="res_model">sale.sale</field>
|
||||
<field
|
||||
name="domain"
|
||||
eval="[
|
||||
If(Eval('active_model') == 'account.invoice',
|
||||
('invoices', 'in', Eval('active_ids', [])), ()),
|
||||
If(Eval('active_model') == 'stock.shipment.out',
|
||||
('shipments', 'in', Eval('active_ids', [])), ()),
|
||||
If(Eval('active_model') == 'stock.shipment.out.return',
|
||||
('shipment_returns', 'in', Eval('active_ids', [])), ()),
|
||||
]"
|
||||
pyson="1"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_sale_relate_simple_view1">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="sale_view_tree"/>
|
||||
<field name="act_window" ref="act_sale_relate_simple"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_sale_relate_simple_view2">
|
||||
<field name="sequence" eval="20"/>
|
||||
<field name="view" ref="sale_view_form"/>
|
||||
<field name="act_window" ref="act_sale_relate_simple"/>
|
||||
</record>
|
||||
<record model="ir.action.keyword" id="act_sale_relate_simple_keyword_invoice">
|
||||
<field name="keyword">form_relate</field>
|
||||
<field name="model">account.invoice,-1</field>
|
||||
<field name="action" ref="act_sale_relate_simple"/>
|
||||
</record>
|
||||
<record model="ir.action.keyword" id="act_sale_relate_simple_keyword_shipment_out">
|
||||
<field name="keyword">form_relate</field>
|
||||
<field name="model">stock.shipment.out,-1</field>
|
||||
<field name="action" ref="act_sale_relate_simple"/>
|
||||
</record>
|
||||
<record model="ir.action.keyword" id="act_sale_relate_simple_keyword_shipment_out_return">
|
||||
<field name="keyword">form_relate</field>
|
||||
<field name="model">stock.shipment.out.return,-1</field>
|
||||
<field name="action" ref="act_sale_relate_simple"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.access" id="access_sale">
|
||||
<field name="model">sale.sale</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_sale_sale">
|
||||
<field name="model">sale.sale</field>
|
||||
<field name="group" ref="group_sale"/>
|
||||
<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.field.access" id="access_sale_sale_invoices_ignored">
|
||||
<field name="model">sale.sale</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_sale_sale_invoices_ignored_sale_admin">
|
||||
<field name="model">sale.sale</field>
|
||||
<field name="field">invoices_ignored</field>
|
||||
<field name="group" ref="group_sale_admin"/>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_write" eval="True"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.rule.group" id="rule_group_sale_companies">
|
||||
<field name="name">User in companies</field>
|
||||
<field name="model">sale.sale</field>
|
||||
<field name="global_p" eval="True"/>
|
||||
</record>
|
||||
<record model="ir.rule" id="rule_sale_companies">
|
||||
<field name="domain"
|
||||
eval="[('company', 'in', Eval('companies', []))]"
|
||||
pyson="1"/>
|
||||
<field name="rule_group" ref="rule_group_sale_companies"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.button" id="sale_cancel_button">
|
||||
<field name="model">sale.sale</field>
|
||||
<field name="name">cancel</field>
|
||||
<field name="string">Cancel</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.button" id="sale_draft_button">
|
||||
<field name="model">sale.sale</field>
|
||||
<field name="name">draft</field>
|
||||
<field name="string">Draft</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.button" id="sale_quote_button">
|
||||
<field name="model">sale.sale</field>
|
||||
<field name="name">quote</field>
|
||||
<field name="string">Quote</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.button" id="sale_confirm_button">
|
||||
<field name="model">sale.sale</field>
|
||||
<field name="name">confirm</field>
|
||||
<field name="string">Confirm</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.button" id="sale_process_button">
|
||||
<field name="model">sale.sale</field>
|
||||
<field name="name">process</field>
|
||||
<field name="string">Process</field>
|
||||
</record>
|
||||
<record model="ir.model.button-res.group" id="sale_process_button_group_sale_admin">
|
||||
<field name="button" ref="sale_process_button"/>
|
||||
<field name="group" ref="group_sale_admin"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.button" id="sale_manual_invoice_button">
|
||||
<field name="model">sale.sale</field>
|
||||
<field name="name">manual_invoice</field>
|
||||
<field name="string">Create Invoice</field>
|
||||
</record>
|
||||
<record model="ir.model.button-res.group" id="sale_manual_invoice_button_group_sale">
|
||||
<field name="button" ref="sale_manual_invoice_button"/>
|
||||
<field name="group" ref="group_sale"/>
|
||||
</record>
|
||||
<record model="ir.model.button-res.group" id="sale_manual_invoice_button_group_account">
|
||||
<field name="button" ref="sale_manual_invoice_button"/>
|
||||
<field name="group" ref="account.group_account"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.button" id="sale_manual_shipment_button">
|
||||
<field name="model">sale.sale</field>
|
||||
<field name="name">manual_shipment</field>
|
||||
<field name="string">Create Shipment</field>
|
||||
</record>
|
||||
<record model="ir.model.button-res.group" id="sale_manual_shipment_button_group_sale">
|
||||
<field name="button" ref="sale_manual_shipment_button"/>
|
||||
<field name="group" ref="group_sale"/>
|
||||
</record>
|
||||
<record model="ir.model.button-res.group" id="sale_manual_shipment_button_group_stock">
|
||||
<field name="button" ref="sale_manual_shipment_button"/>
|
||||
<field name="group" ref="stock.group_stock"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.button" id="sale_modify_header_button">
|
||||
<field name="model">sale.sale</field>
|
||||
<field name="name">modify_header</field>
|
||||
<field name="string">Modify Header</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.button"
|
||||
id="sale_handle_shipment_exception_button">
|
||||
<field name="model">sale.sale</field>
|
||||
<field name="name">handle_shipment_exception</field>
|
||||
<field name="string">Handle Shipment Exception</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.button"
|
||||
id="sale_handle_invoice_exception_button">
|
||||
<field name="model">sale.sale</field>
|
||||
<field name="name">handle_invoice_exception</field>
|
||||
<field name="string">Handle Invoice Exception</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.sequence.type" id="sequence_type_sale">
|
||||
<field name="name">Sale</field>
|
||||
</record>
|
||||
<record model="ir.sequence.type-res.group"
|
||||
id="sequence_type_sale_group_admin">
|
||||
<field name="sequence_type" ref="sequence_type_sale"/>
|
||||
<field name="group" ref="res.group_admin"/>
|
||||
</record>
|
||||
<record model="ir.sequence.type-res.group"
|
||||
id="sequence_type_sale_group_sale_admin">
|
||||
<field name="sequence_type" ref="sequence_type_sale"/>
|
||||
<field name="group" ref="group_sale_admin"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.sequence" id="sequence_sale">
|
||||
<field name="name">Sale</field>
|
||||
<field name="sequence_type" ref="sequence_type_sale"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.report" id="report_sale">
|
||||
<field name="name">Sale</field>
|
||||
<field name="model">sale.sale</field>
|
||||
<field name="report_name">sale.sale</field>
|
||||
<field name="report">sale/sale.fodt</field>
|
||||
</record>
|
||||
<record model="ir.action.keyword" id="report_sale_keyword">
|
||||
<field name="keyword">form_print</field>
|
||||
<field name="model">sale.sale,-1</field>
|
||||
<field name="action" ref="report_sale"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="sale_line_view_form">
|
||||
<field name="model">sale.line</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">sale_line_form</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="sale_line_view_tree">
|
||||
<field name="model">sale.line</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="priority" eval="10"/>
|
||||
<field name="name">sale_line_tree</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="sale_line_view_tree_sequence">
|
||||
<field name="model">sale.line</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="priority" eval="20"/>
|
||||
<field name="name">sale_line_tree_sequence</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.field.access" id="access_sale_line_moves_ignored">
|
||||
<field name="model">sale.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_sale_line_moves_ignored_sale_admin">
|
||||
<field name="model">sale.line</field>
|
||||
<field name="field">moves_ignored</field>
|
||||
<field name="group" ref="group_sale_admin"/>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_write" eval="True"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.act_window" id="act_sale_line_relate">
|
||||
<field name="name">Sale Lines</field>
|
||||
<field name="res_model">sale.line</field>
|
||||
<field
|
||||
name="domain"
|
||||
eval="[('type', '=', 'line'),
|
||||
If(Eval('active_model') == 'sale.sale',
|
||||
('sale', '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',
|
||||
('customer', 'in', Eval('active_ids', [])), ()),
|
||||
]"
|
||||
pyson="1"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_sale_line_relate_view1">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="sale_line_view_tree"/>
|
||||
<field name="act_window" ref="act_sale_line_relate"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_sale_line_relate_view2">
|
||||
<field name="sequence" eval="20"/>
|
||||
<field name="view" ref="sale_line_view_form"/>
|
||||
<field name="act_window" ref="act_sale_line_relate"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.domain" id="act_sale_line_relate_pending">
|
||||
<field name="name">Pending</field>
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="domain" eval="[('sale_state', 'not in', ['done', 'cancelled'])]" pyson="1"/>
|
||||
<field name="count" eval="True"/>
|
||||
<field name="act_window" ref="act_sale_line_relate"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.domain" id="act_sale_line_relate_done">
|
||||
<field name="name">Done</field>
|
||||
<field name="sequence" eval="20"/>
|
||||
<field name="domain" eval="[('sale_state', '=', 'done')]" pyson="1"/>
|
||||
<field name="count" eval="True"/>
|
||||
<field name="act_window" ref="act_sale_line_relate"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.domain" id="act_sale_line_relate_all">
|
||||
<field name="name">All</field>
|
||||
<field name="sequence" eval="9999"/>
|
||||
<field name="domain"></field>
|
||||
<field name="act_window" ref="act_sale_line_relate"/>
|
||||
</record>
|
||||
<record model="ir.action.keyword" id="act_sale_line_relate_keyword_sale">
|
||||
<field name="keyword">form_relate</field>
|
||||
<field name="model">sale.sale,-1</field>
|
||||
<field name="action" ref="act_sale_line_relate"/>
|
||||
</record>
|
||||
<record model="ir.action.keyword" id="act_sale_line_relate_keyword_product">
|
||||
<field name="keyword">form_relate</field>
|
||||
<field name="model">product.product,-1</field>
|
||||
<field name="action" ref="act_sale_line_relate"/>
|
||||
</record>
|
||||
<record model="ir.action.keyword" id="act_sale_line_relate_keyword_product_template">
|
||||
<field name="keyword">form_relate</field>
|
||||
<field name="model">product.template,-1</field>
|
||||
<field name="action" ref="act_sale_line_relate"/>
|
||||
</record>
|
||||
<record model="ir.action.keyword" id="act_sale_line_relate_keyword_party">
|
||||
<field name="keyword">form_relate</field>
|
||||
<field name="model">party.party,-1</field>
|
||||
<field name="action" ref="act_sale_line_relate"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="return_sale_start_view_form">
|
||||
<field name="model">sale.return_sale.start</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">return_sale_start_form</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.wizard" id="wizard_return_sale">
|
||||
<field name="name">Return Sale</field>
|
||||
<field name="wiz_name">sale.return_sale</field>
|
||||
<field name="model">sale.sale</field>
|
||||
</record>
|
||||
<record model="ir.action.keyword" id="act_wizard_return_sale_keyword">
|
||||
<field name="keyword">form_action</field>
|
||||
<field name="model">sale.sale,-1</field>
|
||||
<field name="action" ref="wizard_return_sale"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.wizard" id="wizard_modify_header">
|
||||
<field name="name">Modify Header</field>
|
||||
<field name="wiz_name">sale.modify_header</field>
|
||||
<field name="model">sale.sale</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="modify_header_form">
|
||||
<field name="model">sale.sale</field>
|
||||
<field name="inherit" ref="sale.sale_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.action.wizard" id="wizard_sale_open_product">
|
||||
<field name="name">Products</field>
|
||||
<field name="wiz_name">sale.open_product</field>
|
||||
<field name="model">sale.sale</field>
|
||||
</record>
|
||||
<record model="ir.action.keyword" id="wizard_sale_open_product_keyword1">
|
||||
<field name="keyword">form_relate</field>
|
||||
<field name="model">sale.sale,-1</field>
|
||||
<field name="action" ref="wizard_sale_open_product"/>
|
||||
</record>
|
||||
</data>
|
||||
|
||||
<data noupdate="1">
|
||||
<record model="ir.cron" id="cron_cancel_expired_quotation">
|
||||
<field name="method">sale.sale|cancel_expired_quotation</field>
|
||||
<field name="interval_number" eval="1"/>
|
||||
<field name="interval_type">days</field>
|
||||
</record>
|
||||
</data>
|
||||
</tryton>
|
||||
1081
modules/sale/sale_reporting.py
Normal file
1081
modules/sale/sale_reporting.py
Normal file
File diff suppressed because it is too large
Load Diff
1042
modules/sale/sale_reporting.xml
Normal file
1042
modules/sale/sale_reporting.xml
Normal file
File diff suppressed because it is too large
Load Diff
236
modules/sale/stock.py
Normal file
236
modules/sale/stock.py
Normal file
@@ -0,0 +1,236 @@
|
||||
# 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 decimal import Decimal
|
||||
from functools import wraps
|
||||
|
||||
from trytond.i18n import gettext
|
||||
from trytond.model import ModelView, Workflow, fields
|
||||
from trytond.model.exceptions import AccessError
|
||||
from trytond.modules.product import round_price
|
||||
from trytond.pool import Pool, PoolMeta
|
||||
from trytond.transaction import Transaction, without_check_access
|
||||
|
||||
|
||||
def process_sale(moves_field):
|
||||
def _process_sale(func):
|
||||
@wraps(func)
|
||||
def wrapper(cls, shipments):
|
||||
pool = Pool()
|
||||
Sale = pool.get('sale.sale')
|
||||
transaction = Transaction()
|
||||
context = transaction.context
|
||||
with without_check_access():
|
||||
sales = set(m.sale for s in cls.browse(shipments)
|
||||
for m in getattr(s, moves_field) if m.sale)
|
||||
result = func(cls, shipments)
|
||||
if sales:
|
||||
with transaction.set_context(
|
||||
queue_batch=context.get('queue_batch', True)):
|
||||
Sale.__queue__.process(sales)
|
||||
return result
|
||||
return wrapper
|
||||
return _process_sale
|
||||
|
||||
|
||||
class ShipmentOut(metaclass=PoolMeta):
|
||||
__name__ = 'stock.shipment.out'
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
@Workflow.transition('draft')
|
||||
def draft(cls, shipments):
|
||||
SaleLine = Pool().get('sale.line')
|
||||
for shipment in shipments:
|
||||
for move in shipment.outgoing_moves:
|
||||
if (move.state == 'cancelled'
|
||||
and isinstance(move.origin, SaleLine)):
|
||||
raise AccessError(
|
||||
gettext('sale.msg_sale_move_reset_draft',
|
||||
move=move.rec_name))
|
||||
|
||||
return super().draft(shipments)
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
@Workflow.transition('shipped')
|
||||
@process_sale('outgoing_moves')
|
||||
def ship(cls, shipments):
|
||||
super().ship(shipments)
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
@Workflow.transition('done')
|
||||
@process_sale('outgoing_moves')
|
||||
def do(cls, shipments):
|
||||
super().do(shipments)
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
@Workflow.transition('cancelled')
|
||||
@process_sale('outgoing_moves')
|
||||
def cancel(cls, shipments):
|
||||
super().cancel(shipments)
|
||||
|
||||
|
||||
class ShipmentOutReturn(metaclass=PoolMeta):
|
||||
__name__ = 'stock.shipment.out.return'
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
@Workflow.transition('draft')
|
||||
def draft(cls, shipments):
|
||||
SaleLine = Pool().get('sale.line')
|
||||
for shipment in shipments:
|
||||
for move in shipment.incoming_moves:
|
||||
if (move.state == 'cancelled'
|
||||
and isinstance(move.origin, SaleLine)):
|
||||
raise AccessError(
|
||||
gettext('sale.msg_sale_move_reset_draft',
|
||||
move=move.rec_name))
|
||||
|
||||
return super().draft(shipments)
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
@Workflow.transition('received')
|
||||
@process_sale('incoming_moves')
|
||||
def receive(cls, shipments):
|
||||
pool = Pool()
|
||||
SaleLine = pool.get('sale.line')
|
||||
for shipment in shipments:
|
||||
for move in shipment.incoming_moves:
|
||||
if isinstance(move.origin, SaleLine):
|
||||
move.origin.check_move_quantity()
|
||||
super().receive(shipments)
|
||||
|
||||
|
||||
def process_sale_move(func):
|
||||
@wraps(func)
|
||||
def wrapper(cls, moves):
|
||||
pool = Pool()
|
||||
Sale = pool.get('sale.sale')
|
||||
transaction = Transaction()
|
||||
context = transaction.context
|
||||
with without_check_access():
|
||||
sales = set(m.sale for m in cls.browse(moves) if m.sale)
|
||||
result = func(cls, moves)
|
||||
if sales:
|
||||
with transaction.set_context(
|
||||
queue_batch=context.get('queue_batch', True)):
|
||||
Sale.__queue__.process(sales)
|
||||
return result
|
||||
return wrapper
|
||||
|
||||
|
||||
class Move(metaclass=PoolMeta):
|
||||
__name__ = 'stock.move'
|
||||
sale = fields.Function(
|
||||
fields.Many2One('sale.sale', "Sale"),
|
||||
'get_sale', searcher='search_sale')
|
||||
sale_exception_state = fields.Function(fields.Selection([
|
||||
('', ''),
|
||||
('ignored', 'Ignored'),
|
||||
('recreated', 'Recreated'),
|
||||
], 'Exception State'), 'get_sale_exception_state')
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super().__setup__()
|
||||
if not cls.origin.domain:
|
||||
cls.origin.domain = {}
|
||||
cls.origin.domain['sale.line'] = [
|
||||
('type', '=', 'line'),
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def _get_origin(cls):
|
||||
models = super()._get_origin()
|
||||
models.append('sale.line')
|
||||
return models
|
||||
|
||||
@classmethod
|
||||
def check_origin_types(cls):
|
||||
types = super().check_origin_types()
|
||||
types.add('customer')
|
||||
return types
|
||||
|
||||
def get_sale(self, name):
|
||||
SaleLine = Pool().get('sale.line')
|
||||
if isinstance(self.origin, SaleLine):
|
||||
return self.origin.sale.id
|
||||
|
||||
@classmethod
|
||||
def search_sale(cls, name, clause):
|
||||
return [('origin.' + clause[0],) + tuple(clause[1:3])
|
||||
+ ('sale.line',) + tuple(clause[3:])]
|
||||
|
||||
def get_sale_exception_state(self, name):
|
||||
SaleLine = Pool().get('sale.line')
|
||||
if not isinstance(self.origin, SaleLine):
|
||||
return ''
|
||||
if self in self.origin.moves_recreated:
|
||||
return 'recreated'
|
||||
if self in self.origin.moves_ignored:
|
||||
return 'ignored'
|
||||
return ''
|
||||
|
||||
@fields.depends('origin')
|
||||
def on_change_with_product_uom_category(self, name=None):
|
||||
pool = Pool()
|
||||
SaleLine = pool.get('sale.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 ship and the quantity to invoice.
|
||||
# Use getattr as reference field can have negative id
|
||||
if (isinstance(self.origin, SaleLine)
|
||||
and getattr(self.origin, 'unit', None)):
|
||||
category = self.origin.unit.category
|
||||
return category
|
||||
|
||||
def get_cost_price(self, product_cost_price=None):
|
||||
pool = Pool()
|
||||
SaleLine = pool.get('sale.line')
|
||||
Sale = pool.get('sale.sale')
|
||||
# For return sale's move use the cost price of the original sale
|
||||
if (isinstance(self.origin, SaleLine)
|
||||
and self.origin.quantity < 0
|
||||
and self.from_location.type != 'storage'
|
||||
and self.to_location.type == 'storage'
|
||||
and isinstance(self.origin.sale.origin, Sale)):
|
||||
sale = self.origin.sale.origin
|
||||
cost = Decimal(0)
|
||||
qty = Decimal(0)
|
||||
for move in sale.moves:
|
||||
if (move.state == 'done'
|
||||
and move.from_location.type == 'storage'
|
||||
and move.to_location.type == 'customer'
|
||||
and move.product == self.product):
|
||||
move_quantity = Decimal(str(move.internal_quantity))
|
||||
cost_price = move.get_cost_price(
|
||||
product_cost_price=move.cost_price)
|
||||
qty += move_quantity
|
||||
cost += cost_price * move_quantity
|
||||
if qty:
|
||||
product_cost_price = round_price(cost / qty)
|
||||
return super().get_cost_price(product_cost_price=product_cost_price)
|
||||
|
||||
@property
|
||||
def origin_name(self):
|
||||
pool = Pool()
|
||||
SaleLine = pool.get('sale.line')
|
||||
name = super().origin_name
|
||||
if isinstance(self.origin, SaleLine) and self.origin.id >= 0:
|
||||
name = self.origin.sale.rec_name
|
||||
return name
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
@Workflow.transition('cancelled')
|
||||
@process_sale_move
|
||||
def cancel(cls, moves):
|
||||
super().cancel(moves)
|
||||
|
||||
@classmethod
|
||||
@process_sale_move
|
||||
def on_delete(cls, moves):
|
||||
return super().on_delete(moves)
|
||||
74
modules/sale/stock.xml
Normal file
74
modules/sale/stock.xml
Normal file
@@ -0,0 +1,74 @@
|
||||
<?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_sale">
|
||||
<field name="model">stock.move</field>
|
||||
<field name="group" ref="group_sale"/>
|
||||
<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_sale_move_relate">
|
||||
<field name="name">Stock Moves</field>
|
||||
<field name="res_model">stock.move</field>
|
||||
<field
|
||||
name="domain"
|
||||
eval="[
|
||||
If(Eval('active_model') == 'sale.sale',
|
||||
('sale', 'in', Eval('active_ids', [])), ()),
|
||||
If(Eval('active_model') == 'sale.line',
|
||||
('origin.id', 'in', Eval('active_ids', []), 'sale.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_sale_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_sale_move_relate"/>
|
||||
</record>
|
||||
<record model="ir.action.keyword" id="act_move_form_keyword_sale">
|
||||
<field name="keyword">form_relate</field>
|
||||
<field name="model">sale.sale,-1</field>
|
||||
<field name="action" ref="act_sale_move_relate"/>
|
||||
</record>
|
||||
<record model="ir.action.keyword" id="act_move_form_keyword_sale_line">
|
||||
<field name="keyword">form_relate</field>
|
||||
<field name="model">sale.line,-1</field>
|
||||
<field name="action" ref="act_sale_move_relate"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.access" id="access_shipment_out_group_sale">
|
||||
<field name="model">stock.shipment.out</field>
|
||||
<field name="group" ref="group_sale"/>
|
||||
<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_shipment_out_return_group_sale">
|
||||
<field name="model">stock.shipment.out.return</field>
|
||||
<field name="group" ref="group_sale"/>
|
||||
<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/sale/tests/__init__.py
Normal file
2
modules/sale/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/sale/tests/__pycache__/__init__.cpython-311.pyc
Normal file
BIN
modules/sale/tests/__pycache__/__init__.cpython-311.pyc
Normal file
Binary file not shown.
BIN
modules/sale/tests/__pycache__/test_module.cpython-311.pyc
Normal file
BIN
modules/sale/tests/__pycache__/test_module.cpython-311.pyc
Normal file
Binary file not shown.
BIN
modules/sale/tests/__pycache__/test_scenario.cpython-311.pyc
Normal file
BIN
modules/sale/tests/__pycache__/test_scenario.cpython-311.pyc
Normal file
Binary file not shown.
703
modules/sale/tests/scenario_sale.rst
Normal file
703
modules/sale/tests/scenario_sale.rst
Normal file
@@ -0,0 +1,703 @@
|
||||
=============
|
||||
Sale Scenario
|
||||
=============
|
||||
|
||||
Imports::
|
||||
|
||||
>>> from decimal import Decimal
|
||||
|
||||
>>> from proteus import Model, Report, Wizard
|
||||
>>> 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
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> config = activate_modules('sale', 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())
|
||||
>>> 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.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.customer_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.salable = True
|
||||
>>> template.list_price = Decimal('10')
|
||||
>>> template.account_category = account_category_tax
|
||||
>>> template.save()
|
||||
>>> product, = template.products
|
||||
|
||||
>>> template = ProductTemplate()
|
||||
>>> template.name = 'service'
|
||||
>>> template.default_uom = unit
|
||||
>>> template.type = 'service'
|
||||
>>> template.salable = True
|
||||
>>> template.list_price = Decimal('30')
|
||||
>>> 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'
|
||||
|
||||
Sale 5 products::
|
||||
|
||||
>>> Sale = Model.get('sale.sale')
|
||||
>>> SaleLine = Model.get('sale.line')
|
||||
>>> sale = Sale()
|
||||
>>> sale.party = customer
|
||||
>>> sale.payment_term = payment_term
|
||||
>>> sale.invoice_method = 'order'
|
||||
>>> sale_line = SaleLine()
|
||||
>>> sale.lines.append(sale_line)
|
||||
>>> sale_line.product = product
|
||||
>>> sale_line.quantity = 2.0
|
||||
>>> sale_line = SaleLine()
|
||||
>>> sale.lines.append(sale_line)
|
||||
>>> sale_line.type = 'comment'
|
||||
>>> sale_line.description = 'Comment'
|
||||
>>> sale_line = SaleLine()
|
||||
>>> sale.lines.append(sale_line)
|
||||
>>> sale_line.product = product
|
||||
>>> sale_line.quantity = 3.0
|
||||
>>> sale.click('quote')
|
||||
>>> sale.untaxed_amount, sale.tax_amount, sale.total_amount
|
||||
(Decimal('50.00'), Decimal('5.00'), Decimal('55.00'))
|
||||
>>> assertEqual(sale.quoted_by, employee)
|
||||
>>> sale.click('confirm')
|
||||
>>> sale.untaxed_amount, sale.tax_amount, sale.total_amount
|
||||
(Decimal('50.00'), Decimal('5.00'), Decimal('55.00'))
|
||||
>>> assertEqual(sale.confirmed_by, employee)
|
||||
>>> sale.state
|
||||
'processing'
|
||||
>>> sale.shipment_state
|
||||
'waiting'
|
||||
>>> sale.invoice_state
|
||||
'pending'
|
||||
>>> len(sale.shipments), len(sale.shipment_returns), len(sale.invoices)
|
||||
(1, 0, 1)
|
||||
>>> invoice, = sale.invoices
|
||||
>>> assertEqual(invoice.origins, sale.rec_name)
|
||||
>>> shipment, = sale.shipments
|
||||
>>> assertEqual(shipment.origins, sale.rec_name)
|
||||
|
||||
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(shipment.outgoing_moves,
|
||||
... key=lambda m: m.quantity or 0)
|
||||
>>> 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 sale.lines:
|
||||
... assertEqual(line.quantity, line.actual_quantity)
|
||||
|
||||
Post invoice and check no new invoices::
|
||||
|
||||
|
||||
>>> for invoice in sale.invoices:
|
||||
... invoice.click('post')
|
||||
>>> sale.reload()
|
||||
>>> len(sale.shipments), len(sale.shipment_returns), len(sale.invoices)
|
||||
(1, 0, 1)
|
||||
>>> sale.invoice_state
|
||||
'awaiting payment'
|
||||
|
||||
Testing the report::
|
||||
|
||||
>>> sale_report = Report('sale.sale')
|
||||
>>> ext, _, _, name = sale_report.execute([sale], {})
|
||||
>>> ext
|
||||
'odt'
|
||||
>>> name
|
||||
'Sale-1'
|
||||
|
||||
Sale 5 products with an invoice method 'on shipment'::
|
||||
|
||||
>>> Sale = Model.get('sale.sale')
|
||||
>>> SaleLine = Model.get('sale.line')
|
||||
>>> sale = Sale()
|
||||
>>> sale.party = customer
|
||||
>>> sale.payment_term = payment_term
|
||||
>>> sale.invoice_method = 'shipment'
|
||||
>>> sale_line = SaleLine()
|
||||
>>> sale.lines.append(sale_line)
|
||||
>>> sale_line.product = product
|
||||
>>> sale_line.quantity = 2.0
|
||||
>>> sale_line = SaleLine()
|
||||
>>> sale.lines.append(sale_line)
|
||||
>>> sale_line.type = 'comment'
|
||||
>>> sale_line.description = 'Comment'
|
||||
>>> sale_line = SaleLine()
|
||||
>>> sale.lines.append(sale_line)
|
||||
>>> sale_line.product = product
|
||||
>>> sale_line.quantity = 3.0
|
||||
>>> sale.click('quote')
|
||||
>>> sale.click('confirm')
|
||||
>>> sale.state
|
||||
'processing'
|
||||
>>> sale.shipment_state
|
||||
'waiting'
|
||||
>>> sale.invoice_state
|
||||
'none'
|
||||
>>> sale.reload()
|
||||
>>> len(sale.shipments), len(sale.shipment_returns), len(sale.invoices)
|
||||
(1, 0, 0)
|
||||
|
||||
Not yet linked to invoice lines::
|
||||
|
||||
>>> shipment, = sale.shipments
|
||||
>>> stock_move1, stock_move2 = sorted(shipment.outgoing_moves,
|
||||
... key=lambda m: m.quantity or 0)
|
||||
>>> len(stock_move1.invoice_lines)
|
||||
0
|
||||
>>> len(stock_move2.invoice_lines)
|
||||
0
|
||||
|
||||
Validate Shipments::
|
||||
|
||||
>>> shipment.click('assign_try')
|
||||
>>> shipment.click('pick')
|
||||
>>> shipment.click('pack')
|
||||
>>> shipment.click('do')
|
||||
|
||||
Open customer invoice::
|
||||
|
||||
>>> sale.reload()
|
||||
>>> sale.invoice_state
|
||||
'pending'
|
||||
>>> invoice, = sale.invoices
|
||||
>>> invoice.type
|
||||
'out'
|
||||
>>> 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.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::
|
||||
|
||||
>>> sale.reload()
|
||||
>>> len(sale.invoices)
|
||||
2
|
||||
>>> sum(l.quantity for i in sale.invoices for l in i.lines)
|
||||
5.0
|
||||
|
||||
Sale 5 products with shipment method 'on invoice'::
|
||||
|
||||
>>> sale = Sale()
|
||||
>>> sale.party = customer
|
||||
>>> sale.payment_term = payment_term
|
||||
>>> sale.shipment_method = 'invoice'
|
||||
>>> sale_line = sale.lines.new()
|
||||
>>> sale_line.product = product
|
||||
>>> sale_line.quantity = 5.0
|
||||
>>> sale.click('quote')
|
||||
>>> sale.click('confirm')
|
||||
>>> sale.state
|
||||
'processing'
|
||||
>>> sale.shipment_state
|
||||
'none'
|
||||
>>> sale.invoice_state
|
||||
'pending'
|
||||
>>> len(sale.shipments), len(sale.shipment_returns), len(sale.invoices)
|
||||
(0, 0, 1)
|
||||
|
||||
Not yet linked to stock moves::
|
||||
|
||||
>>> invoice, = sale.invoices
|
||||
>>> invoice_line, = invoice.lines
|
||||
>>> len(invoice_line.stock_moves)
|
||||
0
|
||||
|
||||
Post and Pay Invoice for 4 products::
|
||||
|
||||
>>> invoice_line, = invoice.lines
|
||||
>>> invoice_line.quantity
|
||||
5.0
|
||||
>>> invoice_line.quantity = 4.0
|
||||
>>> invoice.click('post')
|
||||
>>> pay = invoice.click('pay')
|
||||
>>> pay.form.payment_method = payment_method
|
||||
>>> pay.execute('choice')
|
||||
>>> invoice.reload()
|
||||
>>> invoice.state
|
||||
'paid'
|
||||
|
||||
Invoice lines linked to 1 move::
|
||||
|
||||
>>> invoice_line, = invoice.lines
|
||||
>>> len(invoice_line.stock_moves)
|
||||
1
|
||||
|
||||
Stock moves must be linked to invoice line::
|
||||
|
||||
>>> sale.reload()
|
||||
>>> shipment, = sale.shipments
|
||||
>>> shipment.reload()
|
||||
>>> stock_move, = shipment.outgoing_moves
|
||||
>>> stock_move.quantity
|
||||
4.0
|
||||
>>> assertEqual(stock_move.invoice_lines, [invoice_line])
|
||||
|
||||
Ship 3 products::
|
||||
|
||||
>>> stock_inventory_move, = shipment.inventory_moves
|
||||
>>> stock_inventory_move.quantity
|
||||
4.0
|
||||
>>> stock_inventory_move.quantity = 3.0
|
||||
>>> shipment.click('assign_try')
|
||||
>>> shipment.click('pick')
|
||||
>>> shipment.click('pack')
|
||||
>>> shipment.click('do')
|
||||
>>> shipment.state
|
||||
'done'
|
||||
|
||||
New shipments created::
|
||||
|
||||
>>> sale.reload()
|
||||
>>> len(sale.shipments)
|
||||
2
|
||||
|
||||
Invoice lines linked to new moves::
|
||||
|
||||
>>> invoice.reload()
|
||||
>>> invoice_line, = invoice.lines
|
||||
>>> len(invoice_line.stock_moves)
|
||||
2
|
||||
|
||||
Create a Return::
|
||||
|
||||
>>> return_ = Sale()
|
||||
>>> return_.party = customer
|
||||
>>> return_.payment_term = payment_term
|
||||
>>> return_.invoice_method = 'shipment'
|
||||
>>> return_line = SaleLine()
|
||||
>>> return_.lines.append(return_line)
|
||||
>>> return_line.product = product
|
||||
>>> return_line.quantity = -4.
|
||||
>>> return_line = SaleLine()
|
||||
>>> 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)
|
||||
|
||||
Receive Return Shipment for 3 products::
|
||||
|
||||
>>> ship_return, = return_.shipment_returns
|
||||
>>> move_return, = ship_return.incoming_moves
|
||||
>>> move_return.product.rec_name
|
||||
'product'
|
||||
>>> move_return.quantity
|
||||
4.0
|
||||
>>> move_return.quantity = 3
|
||||
>>> ship_return.click('receive')
|
||||
|
||||
Check Return::
|
||||
|
||||
>>> return_.reload()
|
||||
>>> return_.shipment_state
|
||||
'partially shipped'
|
||||
>>> return_.invoice_state
|
||||
'pending'
|
||||
>>> (len(return_.shipments), len(return_.shipment_returns),
|
||||
... len(return_.invoices))
|
||||
(0, 2, 1)
|
||||
|
||||
Open customer credit note::
|
||||
|
||||
>>> credit_note, = return_.invoices
|
||||
>>> credit_note.type
|
||||
'out'
|
||||
>>> len(credit_note.lines)
|
||||
1
|
||||
>>> sum(l.quantity for l in credit_note.lines)
|
||||
-3.0
|
||||
>>> credit_note.click('post')
|
||||
|
||||
Receive Remaining Return Shipment::
|
||||
|
||||
>>> return_.reload()
|
||||
>>> _, ship_return = return_.shipment_returns
|
||||
>>> move_return, = ship_return.incoming_moves
|
||||
>>> move_return.product.rec_name
|
||||
'product'
|
||||
>>> move_return.quantity
|
||||
1.0
|
||||
>>> ship_return.click('receive')
|
||||
|
||||
Check Return::
|
||||
|
||||
>>> return_.reload()
|
||||
>>> return_.shipment_state
|
||||
'sent'
|
||||
>>> return_.invoice_state
|
||||
'awaiting payment'
|
||||
>>> (len(return_.shipments), len(return_.shipment_returns),
|
||||
... len(return_.invoices))
|
||||
(0, 2, 2)
|
||||
|
||||
Mixing return and sale::
|
||||
|
||||
>>> mix = Sale()
|
||||
>>> mix.party = customer
|
||||
>>> mix.payment_term = payment_term
|
||||
>>> mix.invoice_method = 'order'
|
||||
>>> mixline = SaleLine()
|
||||
>>> mix.lines.append(mixline)
|
||||
>>> mixline.product = product
|
||||
>>> mixline.quantity = 7.
|
||||
>>> mixline_comment = SaleLine()
|
||||
>>> mix.lines.append(mixline_comment)
|
||||
>>> mixline_comment.type = 'comment'
|
||||
>>> mixline_comment.description = 'Comment'
|
||||
>>> mixline2 = SaleLine()
|
||||
>>> 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.shipments), len(mix.shipment_returns), len(mix.invoices)
|
||||
(1, 1, 1)
|
||||
|
||||
Checking Shipments::
|
||||
|
||||
>>> mix_return, = mix.shipment_returns
|
||||
>>> mix_shipment, = mix.shipments
|
||||
>>> mix_return.click('receive')
|
||||
>>> move_return, = mix_return.incoming_moves
|
||||
>>> move_return.product.rec_name
|
||||
'product'
|
||||
>>> move_return.quantity
|
||||
2.0
|
||||
>>> mix_shipment.click('assign_try')
|
||||
>>> mix_shipment.click('pick')
|
||||
>>> mix_shipment.click('pack')
|
||||
>>> mix_shipment.click('do')
|
||||
>>> move_shipment, = mix_shipment.outgoing_moves
|
||||
>>> move_shipment.product.rec_name
|
||||
'product'
|
||||
>>> move_shipment.quantity
|
||||
7.0
|
||||
|
||||
Checking the invoice::
|
||||
|
||||
>>> mix.reload()
|
||||
>>> mix_invoice, = mix.invoices
|
||||
>>> mix_invoice.type
|
||||
'out'
|
||||
>>> len(mix_invoice.lines)
|
||||
2
|
||||
>>> sorted(l.quantity for l in mix_invoice.lines)
|
||||
[-2.0, 7.0]
|
||||
>>> mix_invoice.click('post')
|
||||
|
||||
Mixing stuff with an invoice method 'on shipment'::
|
||||
|
||||
>>> mix = Sale()
|
||||
>>> mix.party = customer
|
||||
>>> mix.payment_term = payment_term
|
||||
>>> mix.invoice_method = 'shipment'
|
||||
>>> mixline = SaleLine()
|
||||
>>> mix.lines.append(mixline)
|
||||
>>> mixline.product = product
|
||||
>>> mixline.quantity = 6.
|
||||
>>> mixline_comment = SaleLine()
|
||||
>>> mix.lines.append(mixline_comment)
|
||||
>>> mixline_comment.type = 'comment'
|
||||
>>> mixline_comment.description = 'Comment'
|
||||
>>> mixline2 = SaleLine()
|
||||
>>> 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.shipments), len(mix.shipment_returns), len(mix.invoices)
|
||||
(1, 1, 0)
|
||||
|
||||
Checking Shipments::
|
||||
|
||||
>>> mix_return, = mix.shipment_returns
|
||||
>>> mix_shipment, = mix.shipments
|
||||
>>> mix_return.click('receive')
|
||||
>>> move_return, = mix_return.incoming_moves
|
||||
>>> move_return.product.rec_name
|
||||
'product'
|
||||
>>> move_return.quantity
|
||||
3.0
|
||||
>>> mix_shipment.click('assign_try')
|
||||
>>> mix_shipment.click('pick')
|
||||
>>> mix_shipment.click('pack')
|
||||
>>> move_shipment, = mix_shipment.outgoing_moves
|
||||
>>> move_shipment.product.rec_name
|
||||
'product'
|
||||
>>> move_shipment.quantity
|
||||
6.0
|
||||
|
||||
Sale services::
|
||||
|
||||
>>> service_sale = Sale()
|
||||
>>> service_sale.party = customer
|
||||
>>> service_sale.payment_term = payment_term
|
||||
>>> sale_line = service_sale.lines.new()
|
||||
>>> sale_line.product = service
|
||||
>>> sale_line.quantity = 1
|
||||
>>> service_sale.save()
|
||||
>>> service_sale.click('quote')
|
||||
>>> service_sale.click('confirm')
|
||||
>>> service_sale.state
|
||||
'processing'
|
||||
>>> service_sale.shipment_state
|
||||
'none'
|
||||
>>> service_sale.invoice_state
|
||||
'pending'
|
||||
>>> service_invoice, = service_sale.invoices
|
||||
|
||||
Pay the service invoice::
|
||||
|
||||
>>> service_invoice.click('post')
|
||||
>>> pay = service_invoice.click('pay')
|
||||
>>> pay.form.payment_method = payment_method
|
||||
>>> pay.execute('choice')
|
||||
>>> service_invoice.reload()
|
||||
>>> service_invoice.state
|
||||
'paid'
|
||||
|
||||
Check service sale states::
|
||||
|
||||
>>> service_sale.reload()
|
||||
>>> service_sale.invoice_state
|
||||
'paid'
|
||||
>>> service_sale.shipment_state
|
||||
'none'
|
||||
>>> service_sale.state
|
||||
'done'
|
||||
|
||||
Return sales using the wizard::
|
||||
|
||||
>>> sale_to_return = Sale()
|
||||
>>> sale_to_return.party = customer
|
||||
>>> sale_to_return.payment_term = payment_term
|
||||
>>> sale_line = sale_to_return.lines.new()
|
||||
>>> sale_line.product = service
|
||||
>>> sale_line.quantity = 1
|
||||
>>> sale_line = sale_to_return.lines.new()
|
||||
>>> sale_line.type = 'comment'
|
||||
>>> sale_line.description = 'Test comment'
|
||||
>>> sale_to_return.click('quote')
|
||||
>>> sale_to_return.click('confirm')
|
||||
>>> sale_to_return.state
|
||||
'processing'
|
||||
>>> return_sale = Wizard('sale.return_sale', [sale_to_return])
|
||||
>>> return_sale.execute('return_')
|
||||
>>> returned_sale, = Sale.find([
|
||||
... ('state', '=', 'draft'),
|
||||
... ])
|
||||
>>> assertEqual(returned_sale.origin, sale_to_return)
|
||||
>>> sorted([x.quantity or 0 for x in returned_sale.lines])
|
||||
[-1.0, 0]
|
||||
|
||||
Create a sale to be invoiced on shipment partialy and check correctly linked
|
||||
to invoices::
|
||||
|
||||
>>> sale = Sale()
|
||||
>>> sale.party = customer
|
||||
>>> sale.payment_term = payment_term
|
||||
>>> sale.invoice_method = 'shipment'
|
||||
>>> line = sale.lines.new()
|
||||
>>> line.product = product
|
||||
>>> line.quantity = 10.0
|
||||
>>> sale.click('quote')
|
||||
>>> sale.click('confirm')
|
||||
>>> shipment, = sale.shipments
|
||||
>>> for move in shipment.inventory_moves:
|
||||
... move.quantity = 5.0
|
||||
>>> shipment.click('assign_try')
|
||||
>>> shipment.click('pick')
|
||||
>>> shipment.click('pack')
|
||||
>>> shipment.click('do')
|
||||
>>> sale.reload()
|
||||
>>> invoice, = sale.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 sale to be sent on invoice partially and check correctly linked to
|
||||
invoices::
|
||||
|
||||
>>> sale = Sale()
|
||||
>>> sale.party = customer
|
||||
>>> sale.payment_term = payment_term
|
||||
>>> sale.shipment_method = 'invoice'
|
||||
>>> line = sale.lines.new()
|
||||
>>> line.product = product
|
||||
>>> line.quantity = 10.0
|
||||
>>> sale.click('quote')
|
||||
>>> sale.click('confirm')
|
||||
>>> invoice, = sale.invoices
|
||||
>>> invoice_line, = invoice.lines
|
||||
>>> assertEqual(invoice_line.stock_moves, [])
|
||||
>>> invoice_line.quantity = 5.0
|
||||
>>> invoice.click('post')
|
||||
>>> pay = invoice.click('pay')
|
||||
>>> pay.form.payment_method = payment_method
|
||||
>>> pay.execute('choice')
|
||||
>>> invoice.reload()
|
||||
>>> invoice.state
|
||||
'paid'
|
||||
>>> sale.reload()
|
||||
>>> sale.invoice_state
|
||||
'partially paid'
|
||||
>>> invoice_line.reload()
|
||||
>>> stock_move, = invoice_line.stock_moves
|
||||
>>> stock_move.quantity
|
||||
5.0
|
||||
>>> stock_move.state
|
||||
'draft'
|
||||
|
||||
Deleting a line from a invoice should recreate it::
|
||||
|
||||
>>> sale = Sale()
|
||||
>>> sale.party = customer
|
||||
>>> line = sale.lines.new()
|
||||
>>> line.product = product
|
||||
>>> line.quantity = 10.0
|
||||
>>> sale.click('quote')
|
||||
>>> sale.click('confirm')
|
||||
>>> invoice, = sale.invoices
|
||||
>>> invoice_line, = invoice.lines
|
||||
>>> invoice.lines.remove(invoice_line)
|
||||
>>> invoice.click('post')
|
||||
>>> sale.reload()
|
||||
>>> new_invoice, = sale.invoices
|
||||
>>> new_invoice.number
|
||||
>>> len(new_invoice.lines)
|
||||
1
|
||||
31
modules/sale/tests/scenario_sale_default_methods.rst
Normal file
31
modules/sale/tests/scenario_sale_default_methods.rst
Normal file
@@ -0,0 +1,31 @@
|
||||
=============================
|
||||
Sale Default Methods Scenario
|
||||
=============================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> from proteus import Model
|
||||
>>> from trytond.modules.company.tests.tools import create_company
|
||||
>>> from trytond.tests.tools import activate_modules
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> config = activate_modules('sale', create_company)
|
||||
|
||||
Create a party and set their default methods::
|
||||
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> customer = Party(name='Customer')
|
||||
>>> customer.sale_shipment_method = 'invoice'
|
||||
>>> customer.sale_invoice_method = 'shipment'
|
||||
>>> customer.save()
|
||||
|
||||
Create a sale to to check default methods::
|
||||
|
||||
>>> Sale = Model.get('sale.sale')
|
||||
>>> sale = Sale()
|
||||
>>> sale.party = customer
|
||||
>>> sale.shipment_method
|
||||
'invoice'
|
||||
>>> sale.invoice_method
|
||||
'shipment'
|
||||
59
modules/sale/tests/scenario_sale_default_taxes.rst
Normal file
59
modules/sale/tests/scenario_sale_default_taxes.rst
Normal file
@@ -0,0 +1,59 @@
|
||||
=======================
|
||||
Sale 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('sale', create_company, create_chart)
|
||||
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> Sale = Model.get('sale.sale')
|
||||
>>> Tax = Model.get('account.tax')
|
||||
|
||||
Get accounts::
|
||||
|
||||
>>> accounts = get_accounts()
|
||||
|
||||
Create tax::
|
||||
|
||||
>>> tax = create_tax(Decimal('.10'))
|
||||
>>> tax.save()
|
||||
>>> accounts['revenue'].taxes.append(Tax(tax.id))
|
||||
>>> accounts['revenue'].save()
|
||||
|
||||
Create parties::
|
||||
|
||||
>>> customer = Party(name="Customer")
|
||||
>>> customer.save()
|
||||
|
||||
Create a sale without product::
|
||||
|
||||
>>> sale = Sale(party=customer)
|
||||
>>> line = sale.lines.new()
|
||||
>>> assertEqual(line.taxes, [tax])
|
||||
>>> line.quantity = 1
|
||||
>>> line.unit_price = Decimal('100.0000')
|
||||
>>> sale.click('quote')
|
||||
>>> sale.total_amount
|
||||
Decimal('110.00')
|
||||
>>> sale.click('confirm')
|
||||
>>> sale.state
|
||||
'processing'
|
||||
|
||||
Check invoice::
|
||||
|
||||
>>> invoice, = sale.invoices
|
||||
>>> invoice.total_amount
|
||||
Decimal('110.00')
|
||||
>>> line, = invoice.lines
|
||||
>>> assertEqual(line.account, accounts['revenue'])
|
||||
48
modules/sale/tests/scenario_sale_empty.rst
Normal file
48
modules/sale/tests/scenario_sale_empty.rst
Normal file
@@ -0,0 +1,48 @@
|
||||
===================
|
||||
Sale 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('sale', create_company, create_chart)
|
||||
|
||||
Create parties::
|
||||
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> customer = Party(name='Customer')
|
||||
>>> customer.save()
|
||||
|
||||
Create empty sale::
|
||||
|
||||
>>> Sale = Model.get('sale.sale')
|
||||
>>> sale = Sale()
|
||||
>>> sale.party = customer
|
||||
>>> sale.click('quote')
|
||||
>>> sale.state
|
||||
'quotation'
|
||||
>>> sale.untaxed_amount
|
||||
Decimal('0')
|
||||
>>> sale.tax_amount
|
||||
Decimal('0')
|
||||
>>> sale.total_amount
|
||||
Decimal('0')
|
||||
>>> sale.click('confirm')
|
||||
>>> sale.state
|
||||
'done'
|
||||
>>> sale.shipment_state
|
||||
'none'
|
||||
>>> len(sale.shipments)
|
||||
0
|
||||
>>> len(sale.shipment_returns)
|
||||
0
|
||||
>>> sale.invoice_state
|
||||
'none'
|
||||
>>> len(sale.invoices)
|
||||
0
|
||||
103
modules/sale/tests/scenario_sale_line_cancelled.rst
Normal file
103
modules/sale/tests/scenario_sale_line_cancelled.rst
Normal file
@@ -0,0 +1,103 @@
|
||||
============================
|
||||
Sale 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('sale', create_company, create_chart)
|
||||
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> ProductCategory = Model.get('product.category')
|
||||
>>> ProductTemplate = Model.get('product.template')
|
||||
>>> ProductUom = Model.get('product.uom')
|
||||
>>> Sale = Model.get('sale.sale')
|
||||
|
||||
Get accounts::
|
||||
|
||||
>>> accounts = get_accounts()
|
||||
|
||||
Create party::
|
||||
|
||||
>>> customer = Party(name="Customer")
|
||||
>>> customer.save()
|
||||
|
||||
Create product::
|
||||
|
||||
>>> account_category = ProductCategory(name="Account Category")
|
||||
>>> account_category.accounting = True
|
||||
>>> account_category.account_revenue = accounts['revenue']
|
||||
>>> account_category.save()
|
||||
|
||||
>>> template = ProductTemplate(name="Product")
|
||||
>>> template.default_uom, = ProductUom.find([('name', '=', "Unit")])
|
||||
>>> template.type = 'goods'
|
||||
>>> template.salable = True
|
||||
>>> template.list_price = Decimal('10.0000')
|
||||
>>> template.account_category = account_category
|
||||
>>> template.save()
|
||||
>>> product, = template.products
|
||||
|
||||
Sale product::
|
||||
|
||||
>>> sale = Sale(party=customer)
|
||||
>>> line = sale.lines.new()
|
||||
>>> line.product = product
|
||||
>>> line.quantity = 1
|
||||
>>> sale.click('quote')
|
||||
>>> sale.click('confirm')
|
||||
>>> sale.state
|
||||
'processing'
|
||||
>>> sale.shipment_state
|
||||
'waiting'
|
||||
>>> sale.invoice_state
|
||||
'pending'
|
||||
|
||||
Cancel shipment and invoice::
|
||||
|
||||
>>> shipment, = sale.shipments
|
||||
>>> shipment.click('cancel')
|
||||
>>> shipment.state
|
||||
'cancelled'
|
||||
|
||||
>>> invoice, = sale.invoices
|
||||
>>> invoice.click('cancel')
|
||||
>>> invoice.state
|
||||
'cancelled'
|
||||
|
||||
>>> sale.reload()
|
||||
>>> sale.state
|
||||
'processing'
|
||||
>>> sale.shipment_state
|
||||
'exception'
|
||||
>>> sale.invoice_state
|
||||
'exception'
|
||||
|
||||
Ignore exceptions::
|
||||
|
||||
>>> invoice_handle_exception = sale.click('handle_invoice_exception')
|
||||
>>> invoice_handle_exception.form.ignore_invoices.extend(
|
||||
... invoice_handle_exception.form.ignore_invoices.find())
|
||||
>>> invoice_handle_exception.execute('handle')
|
||||
|
||||
>>> sale.invoice_state
|
||||
'none'
|
||||
|
||||
>>> shipment_handle_exception = sale.click('handle_shipment_exception')
|
||||
>>> shipment_handle_exception.form.ignore_moves.extend(
|
||||
... shipment_handle_exception.form.ignore_moves.find())
|
||||
>>> shipment_handle_exception.execute('handle')
|
||||
|
||||
>>> sale.shipment_state
|
||||
'none'
|
||||
|
||||
>>> sale.state
|
||||
'done'
|
||||
@@ -0,0 +1,91 @@
|
||||
=======================================
|
||||
Sale Line Cancelled On 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('sale', create_company, create_chart)
|
||||
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> ProductCategory = Model.get('product.category')
|
||||
>>> ProductTemplate = Model.get('product.template')
|
||||
>>> ProductUom = Model.get('product.uom')
|
||||
>>> Sale = Model.get('sale.sale')
|
||||
|
||||
Get accounts::
|
||||
|
||||
>>> accounts = get_accounts()
|
||||
|
||||
Create party::
|
||||
|
||||
>>> customer = Party(name="Customer")
|
||||
>>> customer.save()
|
||||
|
||||
Create product::
|
||||
|
||||
>>> account_category = ProductCategory(name="Account Category")
|
||||
>>> account_category.accounting = True
|
||||
>>> account_category.account_revenue = accounts['revenue']
|
||||
>>> account_category.save()
|
||||
|
||||
>>> template = ProductTemplate(name="Product")
|
||||
>>> template.default_uom, = ProductUom.find([('name', '=', "Unit")])
|
||||
>>> template.type = 'goods'
|
||||
>>> template.salable = True
|
||||
>>> template.list_price = Decimal('10.0000')
|
||||
>>> template.account_category = account_category
|
||||
>>> template.save()
|
||||
>>> product, = template.products
|
||||
|
||||
Sale product::
|
||||
|
||||
>>> sale = Sale(party=customer)
|
||||
>>> sale.shipment_method = 'invoice'
|
||||
>>> line = sale.lines.new()
|
||||
>>> line.product = product
|
||||
>>> line.quantity = 1
|
||||
>>> sale.click('quote')
|
||||
>>> sale.click('confirm')
|
||||
>>> sale.state
|
||||
'processing'
|
||||
>>> sale.shipment_state
|
||||
'none'
|
||||
>>> sale.invoice_state
|
||||
'pending'
|
||||
|
||||
Cancel invoice::
|
||||
|
||||
>>> invoice, = sale.invoices
|
||||
>>> invoice.click('cancel')
|
||||
>>> invoice.state
|
||||
'cancelled'
|
||||
|
||||
>>> sale.reload()
|
||||
>>> sale.state
|
||||
'processing'
|
||||
>>> sale.shipment_state
|
||||
'none'
|
||||
>>> sale.invoice_state
|
||||
'exception'
|
||||
|
||||
Ignore exception::
|
||||
|
||||
>>> invoice_handle_exception = sale.click('handle_invoice_exception')
|
||||
>>> invoice_handle_exception.form.ignore_invoices.extend(
|
||||
... invoice_handle_exception.form.ignore_invoices.find())
|
||||
>>> invoice_handle_exception.execute('handle')
|
||||
|
||||
>>> sale.shipment_state
|
||||
'none'
|
||||
|
||||
>>> sale.state
|
||||
'done'
|
||||
@@ -0,0 +1,91 @@
|
||||
========================================
|
||||
Sale 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('sale', create_company, create_chart)
|
||||
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> ProductCategory = Model.get('product.category')
|
||||
>>> ProductTemplate = Model.get('product.template')
|
||||
>>> ProductUom = Model.get('product.uom')
|
||||
>>> Sale = Model.get('sale.sale')
|
||||
|
||||
Get accounts::
|
||||
|
||||
>>> accounts = get_accounts()
|
||||
|
||||
Create party::
|
||||
|
||||
>>> customer = Party(name="Customer")
|
||||
>>> customer.save()
|
||||
|
||||
Create product::
|
||||
|
||||
>>> account_category = ProductCategory(name="Account Category")
|
||||
>>> account_category.accounting = True
|
||||
>>> account_category.account_revenue = accounts['revenue']
|
||||
>>> account_category.save()
|
||||
|
||||
>>> template = ProductTemplate(name="Product")
|
||||
>>> template.default_uom, = ProductUom.find([('name', '=', "Unit")])
|
||||
>>> template.type = 'goods'
|
||||
>>> template.salable = True
|
||||
>>> template.list_price = Decimal('10.0000')
|
||||
>>> template.account_category = account_category
|
||||
>>> template.save()
|
||||
>>> product, = template.products
|
||||
|
||||
Sale product::
|
||||
|
||||
>>> sale = Sale(party=customer)
|
||||
>>> sale.invoice_method = 'shipment'
|
||||
>>> line = sale.lines.new()
|
||||
>>> line.product = product
|
||||
>>> line.quantity = 1
|
||||
>>> sale.click('quote')
|
||||
>>> sale.click('confirm')
|
||||
>>> sale.state
|
||||
'processing'
|
||||
>>> sale.shipment_state
|
||||
'waiting'
|
||||
>>> sale.invoice_state
|
||||
'none'
|
||||
|
||||
Cancel shipment::
|
||||
|
||||
>>> shipment, = sale.shipments
|
||||
>>> shipment.click('cancel')
|
||||
>>> shipment.state
|
||||
'cancelled'
|
||||
|
||||
>>> sale.reload()
|
||||
>>> sale.state
|
||||
'processing'
|
||||
>>> sale.shipment_state
|
||||
'exception'
|
||||
>>> sale.invoice_state
|
||||
'none'
|
||||
|
||||
Ignore exception::
|
||||
|
||||
>>> shipment_handle_exception = sale.click('handle_shipment_exception')
|
||||
>>> shipment_handle_exception.form.ignore_moves.extend(
|
||||
... shipment_handle_exception.form.ignore_moves.find())
|
||||
>>> shipment_handle_exception.execute('handle')
|
||||
|
||||
>>> sale.shipment_state
|
||||
'none'
|
||||
|
||||
>>> sale.state
|
||||
'done'
|
||||
90
modules/sale/tests/scenario_sale_manual_invoice.rst
Normal file
90
modules/sale/tests/scenario_sale_manual_invoice.rst
Normal file
@@ -0,0 +1,90 @@
|
||||
============================
|
||||
Sale 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('sale', create_company, create_chart)
|
||||
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> ProductCategory = Model.get('product.category')
|
||||
>>> ProductTemplate = Model.get('product.template')
|
||||
>>> ProductUom = Model.get('product.uom')
|
||||
>>> Sale = Model.get('sale.sale')
|
||||
|
||||
Get accounts::
|
||||
|
||||
>>> accounts = get_accounts()
|
||||
|
||||
Create party::
|
||||
|
||||
>>> customer = Party(name="Customer")
|
||||
>>> customer.save()
|
||||
|
||||
Create account category::
|
||||
|
||||
>>> account_category = ProductCategory(name="Account Category")
|
||||
>>> account_category.accounting = True
|
||||
>>> account_category.account_revenue = accounts['revenue']
|
||||
>>> account_category.save()
|
||||
|
||||
Create product::
|
||||
|
||||
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
|
||||
|
||||
>>> template = ProductTemplate()
|
||||
>>> template.name = 'product'
|
||||
>>> template.default_uom = unit
|
||||
>>> template.type = 'service'
|
||||
>>> template.salable = True
|
||||
>>> template.account_category = account_category
|
||||
>>> template.list_price = Decimal('10')
|
||||
>>> template.save()
|
||||
>>> product, = template.products
|
||||
|
||||
Sale with manual invoice method::
|
||||
|
||||
>>> sale = Sale()
|
||||
>>> sale.party = customer
|
||||
>>> sale.invoice_method = 'manual'
|
||||
>>> line = sale.lines.new()
|
||||
>>> line.product = product
|
||||
>>> line.quantity = 10
|
||||
>>> sale.click('quote')
|
||||
>>> sale.click('confirm')
|
||||
>>> sale.state
|
||||
'processing'
|
||||
>>> sale.invoice_state
|
||||
'none'
|
||||
>>> len(sale.invoices)
|
||||
0
|
||||
|
||||
Manually create an invoice::
|
||||
|
||||
>>> sale.click('manual_invoice')
|
||||
>>> sale.state
|
||||
'processing'
|
||||
>>> sale.invoice_state
|
||||
'pending'
|
||||
|
||||
Change quantity on invoice and create a new invoice::
|
||||
|
||||
>>> invoice, = sale.invoices
|
||||
>>> line, = invoice.lines
|
||||
>>> line.quantity = 5
|
||||
>>> invoice.save()
|
||||
|
||||
>>> len(sale.invoices)
|
||||
1
|
||||
>>> sale.click('manual_invoice')
|
||||
>>> len(sale.invoices)
|
||||
2
|
||||
76
modules/sale/tests/scenario_sale_manual_shipment.rst
Normal file
76
modules/sale/tests/scenario_sale_manual_shipment.rst
Normal file
@@ -0,0 +1,76 @@
|
||||
=============================
|
||||
Sale Manual Shipment Scenario
|
||||
=============================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> from decimal import Decimal
|
||||
|
||||
>>> from proteus import Model
|
||||
>>> from trytond.modules.company.tests.tools import create_company
|
||||
>>> from trytond.tests.tools import activate_modules
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> config = activate_modules('sale', create_company)
|
||||
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> ProductCategory = Model.get('product.category')
|
||||
>>> ProductTemplate = Model.get('product.template')
|
||||
>>> ProductUom = Model.get('product.uom')
|
||||
>>> Sale = Model.get('sale.sale')
|
||||
|
||||
Create party::
|
||||
|
||||
>>> customer = Party(name="Customer")
|
||||
>>> customer.save()
|
||||
|
||||
Create product::
|
||||
|
||||
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
|
||||
|
||||
>>> template = ProductTemplate()
|
||||
>>> template.name = 'product'
|
||||
>>> template.default_uom = unit
|
||||
>>> template.type = 'goods'
|
||||
>>> template.salable = True
|
||||
>>> template.list_price = Decimal('10')
|
||||
>>> template.save()
|
||||
>>> product, = template.products
|
||||
|
||||
Sale with manual shipment method::
|
||||
|
||||
>>> sale = Sale()
|
||||
>>> sale.party = customer
|
||||
>>> sale.shipment_method = 'manual'
|
||||
>>> sale.invoice_method = 'manual' # no need for accounting
|
||||
>>> line = sale.lines.new()
|
||||
>>> line.product = product
|
||||
>>> line.quantity = 10
|
||||
>>> sale.click('quote')
|
||||
>>> sale.click('confirm')
|
||||
>>> sale.state
|
||||
'processing'
|
||||
>>> len(sale.shipments)
|
||||
0
|
||||
|
||||
Manually create a shipment::
|
||||
|
||||
>>> sale.click('manual_shipment')
|
||||
>>> sale.state
|
||||
'processing'
|
||||
>>> sale.shipment_state
|
||||
'waiting'
|
||||
|
||||
Change quantity on shipment and create a new shipment::
|
||||
|
||||
>>> shipment, = sale.shipments
|
||||
>>> move, = shipment.outgoing_moves
|
||||
>>> move.quantity = 5
|
||||
>>> shipment.save()
|
||||
|
||||
>>> len(sale.shipments)
|
||||
1
|
||||
>>> sale.click('manual_shipment')
|
||||
>>> len(sale.shipments)
|
||||
2
|
||||
94
modules/sale/tests/scenario_sale_modify_header.rst
Normal file
94
modules/sale/tests/scenario_sale_modify_header.rst
Normal file
@@ -0,0 +1,94 @@
|
||||
===========================
|
||||
Sale 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('sale', create_company, create_chart)
|
||||
|
||||
Get accounts::
|
||||
|
||||
>>> accounts = get_accounts()
|
||||
>>> revenue = accounts['revenue']
|
||||
|
||||
Create tax and tax rule::
|
||||
|
||||
>>> tax = create_tax(Decimal('.10'))
|
||||
>>> tax.save()
|
||||
>>> other_tax = create_tax(Decimal('.05'))
|
||||
>>> other_tax.save()
|
||||
|
||||
>>> TaxRule = Model.get('account.tax.rule')
|
||||
>>> foreign = TaxRule(name='Foreign Customers')
|
||||
>>> foreign_tax = foreign.lines.new()
|
||||
>>> foreign_tax.origin_tax = tax
|
||||
>>> foreign_tax.tax = other_tax
|
||||
>>> foreign.save()
|
||||
|
||||
Create account categories::
|
||||
|
||||
>>> ProductCategory = Model.get('product.category')
|
||||
>>> account_category = ProductCategory(name="Account Category")
|
||||
>>> account_category.accounting = True
|
||||
>>> account_category.account_revenue = revenue
|
||||
>>> account_category.customer_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.salable = True
|
||||
>>> template.list_price = Decimal('10')
|
||||
>>> template.account_category = account_category
|
||||
>>> template.save()
|
||||
>>> product, = template.products
|
||||
|
||||
Create parties::
|
||||
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> customer = Party(name='Customer')
|
||||
>>> customer.save()
|
||||
>>> another = Party(name='Another Customer', customer_tax_rule=foreign)
|
||||
>>> another.save()
|
||||
|
||||
Create a sale with a line::
|
||||
|
||||
>>> Sale = Model.get('sale.sale')
|
||||
>>> sale = Sale()
|
||||
>>> sale.party = customer
|
||||
>>> sale_line = sale.lines.new()
|
||||
>>> sale_line.product = product
|
||||
>>> sale_line.quantity = 3
|
||||
>>> sale_line_comment = sale.lines.new(type='comment')
|
||||
>>> sale.save()
|
||||
>>> sale.untaxed_amount, sale.tax_amount, sale.total_amount
|
||||
(Decimal('30.00'), Decimal('3.00'), Decimal('33.00'))
|
||||
|
||||
Change the party::
|
||||
|
||||
>>> modify_header = sale.click('modify_header')
|
||||
>>> assertEqual(modify_header.form.party, customer)
|
||||
>>> modify_header.form.party = another
|
||||
>>> modify_header.execute('modify')
|
||||
|
||||
>>> sale.party.name
|
||||
'Another Customer'
|
||||
>>> sale.untaxed_amount, sale.tax_amount, sale.total_amount
|
||||
(Decimal('30.00'), Decimal('1.50'), Decimal('31.50'))
|
||||
57
modules/sale/tests/scenario_sale_quotation.rst
Normal file
57
modules/sale/tests/scenario_sale_quotation.rst
Normal file
@@ -0,0 +1,57 @@
|
||||
=======================
|
||||
Sale Quotation Scenario
|
||||
=======================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> import datetime as dt
|
||||
|
||||
>>> 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, assertEqual
|
||||
|
||||
>>> today = dt.date.today()
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> config = activate_modules('sale', create_company, create_chart)
|
||||
|
||||
>>> Cron = Model.get('ir.cron')
|
||||
>>> Sale = Model.get('sale.sale')
|
||||
>>> SaleConfiguration = Model.get('sale.configuration')
|
||||
>>> Party = Model.get('party.party')
|
||||
|
||||
Set quotation validity to 1 week::
|
||||
|
||||
>>> sale_configuration = SaleConfiguration(1)
|
||||
>>> sale_configuration.sale_quotation_validity = dt.timedelta(weeks=1)
|
||||
>>> sale_configuration.save()
|
||||
|
||||
Create customer::
|
||||
|
||||
>>> customer = Party(name="Customer")
|
||||
>>> customer.save()
|
||||
|
||||
Create a quotation::
|
||||
|
||||
>>> sale = Sale(party=customer)
|
||||
>>> sale.click('quote')
|
||||
>>> sale.state
|
||||
'quotation'
|
||||
>>> assertEqual(sale.quotation_date, today)
|
||||
>>> assertEqual(sale.quotation_validity, dt.timedelta(weeks=1))
|
||||
>>> assertEqual(sale.quotation_expire, today + dt.timedelta(weeks=1))
|
||||
|
||||
Expire quotation::
|
||||
|
||||
>>> sale.quotation_date = today - dt.timedelta(weeks=2)
|
||||
>>> sale.save()
|
||||
|
||||
>>> cron, = Cron.find(
|
||||
... [('method', '=', 'sale.sale|cancel_expired_quotation')], limit=1)
|
||||
>>> cron.click('run_once')
|
||||
|
||||
>>> sale.reload()
|
||||
>>> sale.state
|
||||
'cancelled'
|
||||
315
modules/sale/tests/scenario_sale_reporting.rst
Normal file
315
modules/sale/tests/scenario_sale_reporting.rst
Normal file
@@ -0,0 +1,315 @@
|
||||
=======================
|
||||
Sale 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('sale', create_company, create_chart)
|
||||
|
||||
Create fiscal year::
|
||||
|
||||
>>> fiscalyear = set_fiscalyear_invoice_sequences(create_fiscalyear())
|
||||
>>> fiscalyear.click('create_period')
|
||||
|
||||
Get accounts::
|
||||
|
||||
>>> accounts = get_accounts()
|
||||
>>> revenue = accounts['revenue']
|
||||
>>> expense = accounts['expense']
|
||||
|
||||
Create countries::
|
||||
|
||||
>>> Region = Model.get('country.region')
|
||||
>>> Country = Model.get('country.country')
|
||||
>>> Subdivision = Model.get('country.subdivision')
|
||||
>>> north_america, = Region.find([('code_numeric', '=', '021')])
|
||||
>>> country_us = Country(name="United States", region=north_america)
|
||||
>>> country_us.save()
|
||||
>>> california = Subdivision(
|
||||
... name="California", type='state', country=country_us)
|
||||
>>> california.save()
|
||||
>>> new_york = Subdivision(
|
||||
... name="New York", type='state', country=country_us)
|
||||
>>> new_york.save()
|
||||
|
||||
Create party categories::
|
||||
|
||||
>>> PartyCategory = Model.get('party.category')
|
||||
>>> party_category_root1 = PartyCategory(name="Root1")
|
||||
>>> party_category_root1.save()
|
||||
>>> party_category_child1 = PartyCategory(
|
||||
... name="Child1", parent=party_category_root1)
|
||||
>>> party_category_child1.save()
|
||||
>>> party_category_child2 = PartyCategory(
|
||||
... name="Child2", parent=party_category_root1)
|
||||
>>> party_category_child2.save()
|
||||
>>> party_category_root2 = PartyCategory(name="Root2")
|
||||
>>> party_category_root2.save()
|
||||
|
||||
Create parties::
|
||||
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> customer1 = Party(name='Customer1')
|
||||
>>> customer1.categories.append(PartyCategory(party_category_child1.id))
|
||||
>>> customer1.categories.append(PartyCategory(party_category_root2.id))
|
||||
>>> address, = customer1.addresses
|
||||
>>> address.country = country_us
|
||||
>>> address.subdivision = california
|
||||
>>> customer1.save()
|
||||
>>> customer2 = Party(name='Customer2')
|
||||
>>> customer2.categories.append(PartyCategory(party_category_child2.id))
|
||||
>>> address, = customer2.addresses
|
||||
>>> address.country = country_us
|
||||
>>> address.subdivision = new_york
|
||||
>>> customer2.save()
|
||||
|
||||
Create account categories::
|
||||
|
||||
>>> Category = Model.get('product.category')
|
||||
>>> account_category = Category(name="Account Category")
|
||||
>>> account_category.accounting = True
|
||||
>>> account_category.account_revenue = revenue
|
||||
>>> account_category.save()
|
||||
|
||||
Create products::
|
||||
|
||||
>>> ProductUom = Model.get('product.uom')
|
||||
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
|
||||
>>> ProductTemplate = Model.get('product.template')
|
||||
|
||||
>>> template1 = ProductTemplate()
|
||||
>>> template1.name = "Product1"
|
||||
>>> template1.default_uom = unit
|
||||
>>> template1.type = 'service'
|
||||
>>> template1.salable = True
|
||||
>>> template1.list_price = Decimal('10')
|
||||
>>> template1.account_category = account_category
|
||||
>>> template1.save()
|
||||
>>> product1, = template1.products
|
||||
|
||||
>>> template2, = template1.duplicate(default={'name': "Product2"})
|
||||
>>> template2.account_category = account_category
|
||||
>>> template2.save()
|
||||
>>> product2, = template2.products
|
||||
|
||||
>>> category_root1 = Category(name="Root1")
|
||||
>>> category_root1.save()
|
||||
>>> category_child1 = Category(name="Child1", parent=category_root1)
|
||||
>>> category_child1.save()
|
||||
>>> category_child2 = Category(name="Child2", parent=category_root1)
|
||||
>>> category_child2.save()
|
||||
>>> category_root2 = Category(name="Root2")
|
||||
>>> category_root2.save()
|
||||
|
||||
>>> template1.categories.append(Category(category_child1.id))
|
||||
>>> template1.categories.append(Category(category_root2.id))
|
||||
>>> template1.save()
|
||||
>>> template2.categories.append(Category(category_child2.id))
|
||||
>>> template2.save()
|
||||
|
||||
Create sales::
|
||||
|
||||
>>> Sale = Model.get('sale.sale')
|
||||
|
||||
>>> sale1 = Sale()
|
||||
>>> sale1.party = customer1
|
||||
>>> sale1.sale_date = fiscalyear.start_date
|
||||
>>> line = sale1.lines.new()
|
||||
>>> line.product = product1
|
||||
>>> line.quantity = 2
|
||||
>>> line = sale1.lines.new()
|
||||
>>> line.product = product2
|
||||
>>> line.quantity = 1
|
||||
>>> sale1.click('quote')
|
||||
>>> sale1.click('confirm')
|
||||
|
||||
>>> sale2 = Sale()
|
||||
>>> sale2.party = customer2
|
||||
>>> sale2.sale_date = fiscalyear.start_date + relativedelta(months=1)
|
||||
>>> line = sale2.lines.new()
|
||||
>>> line.product = product1
|
||||
>>> line.quantity = 1
|
||||
>>> sale2.click('quote')
|
||||
>>> sale2.click('confirm')
|
||||
|
||||
Check sale reporting per customer::
|
||||
|
||||
>>> Customer = Model.get('sale.reporting.customer')
|
||||
>>> CustomerTimeseries = Model.get('sale.reporting.customer.time_series')
|
||||
>>> context = dict(
|
||||
... from_date=fiscalyear.start_date,
|
||||
... to_date=fiscalyear.end_date,
|
||||
... period='month')
|
||||
>>> with config.set_context(context=context):
|
||||
... reports = Customer.find([])
|
||||
... time_series = CustomerTimeseries.find([])
|
||||
>>> len(reports)
|
||||
2
|
||||
>>> with config.set_context(context=context):
|
||||
... assertEqual({(r.customer.id, r.number, r.revenue) for r in reports},
|
||||
... {(customer1.id, 1, Decimal('30')),
|
||||
... (customer2.id, 1, Decimal('10'))})
|
||||
>>> len(time_series)
|
||||
2
|
||||
>>> with config.set_context(context=context):
|
||||
... assertEqual({(r.customer.id, r.date, r.number, r.revenue)
|
||||
... for r in time_series},
|
||||
... {(customer1.id, sale1.sale_date.replace(day=1), 1, Decimal('30')),
|
||||
... (customer2.id, sale2.sale_date.replace(day=1), 1, Decimal('10'))})
|
||||
|
||||
Check sale reporting per customer categories::
|
||||
|
||||
>>> CustomerCategory = Model.get('sale.reporting.customer.category')
|
||||
>>> CustomerCategoryTimeseries = Model.get(
|
||||
... 'sale.reporting.customer.category.time_series')
|
||||
>>> CustomerCategoryTree = Model.get('sale.reporting.customer.category.tree')
|
||||
>>> with config.set_context(context=context):
|
||||
... reports = CustomerCategory.find([])
|
||||
... time_series = CustomerCategoryTimeseries.find([])
|
||||
... tree = CustomerCategoryTree.find([])
|
||||
>>> len(reports)
|
||||
3
|
||||
>>> with config.set_context(context=context):
|
||||
... assertEqual({(r.category.id, r.number, r.revenue) for r in reports},
|
||||
... {(party_category_child1.id, 1, Decimal('30')),
|
||||
... (party_category_root2.id, 1, Decimal('30')),
|
||||
... (party_category_child2.id, 1, Decimal('10'))})
|
||||
>>> len(time_series)
|
||||
3
|
||||
>>> with config.set_context(context=context):
|
||||
... assertEqual({
|
||||
... (r.category.id, r.date, r.number, r.revenue)
|
||||
... for r in time_series},
|
||||
... {
|
||||
... (party_category_child1.id, sale1.sale_date.replace(day=1),
|
||||
... 1, Decimal('30')),
|
||||
... (party_category_root2.id, sale1.sale_date.replace(day=1),
|
||||
... 1, Decimal('30')),
|
||||
... (party_category_child2.id, sale2.sale_date.replace(day=1),
|
||||
... 1, Decimal('10'))})
|
||||
>>> len(tree)
|
||||
4
|
||||
>>> with config.set_context(context=context):
|
||||
... assertEqual({(r.name, r.revenue) for r in tree},
|
||||
... {('Root1', Decimal('40')),
|
||||
... ('Child1', Decimal('30')),
|
||||
... ('Child2', Decimal('10')),
|
||||
... ('Root2', Decimal('30'))})
|
||||
>>> child1, = CustomerCategoryTree.find([('rec_name', '=', 'Child1')])
|
||||
>>> child1.rec_name
|
||||
'Child1'
|
||||
|
||||
Check sale reporting per product::
|
||||
|
||||
>>> Product = Model.get('sale.reporting.product')
|
||||
>>> ProductTimeseries = Model.get('sale.reporting.product.time_series')
|
||||
>>> 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.revenue) for r in reports},
|
||||
... {(product1.id, 2, Decimal('30')),
|
||||
... (product2.id, 1, Decimal('10'))})
|
||||
>>> len(time_series)
|
||||
3
|
||||
>>> with config.set_context(context=context):
|
||||
... assertEqual({(r.product.id, r.date, r.number, r.revenue)
|
||||
... for r in time_series},
|
||||
... {(product1.id, sale1.sale_date.replace(day=1), 1, Decimal('20')),
|
||||
... (product2.id, sale1.sale_date.replace(day=1), 1, Decimal('10')),
|
||||
... (product1.id, sale2.sale_date.replace(day=1), 1, Decimal('10'))})
|
||||
|
||||
Check sale reporting per product categories::
|
||||
|
||||
>>> ProductCategory = Model.get('sale.reporting.product.category')
|
||||
>>> ProductCategoryTimeseries = Model.get(
|
||||
... 'sale.reporting.product.category.time_series')
|
||||
>>> ProductCategoryTree = Model.get('sale.reporting.product.category.tree')
|
||||
>>> with config.set_context(context=context):
|
||||
... reports = ProductCategory.find([])
|
||||
... time_series = ProductCategoryTimeseries.find([])
|
||||
... tree = ProductCategoryTree.find([])
|
||||
>>> len(reports)
|
||||
4
|
||||
>>> with config.set_context(context=context):
|
||||
... assertEqual({(r.category.id, r.number, r.revenue) for r in reports},
|
||||
... {(category_child1.id, 2, Decimal('30')),
|
||||
... (category_root2.id, 2, Decimal('30')),
|
||||
... (category_child2.id, 1, Decimal('10')),
|
||||
... (account_category.id, 2, Decimal('40'))})
|
||||
>>> len(time_series)
|
||||
7
|
||||
>>> with config.set_context(context=context):
|
||||
... assertEqual({(r.category.id, r.date, r.number, r.revenue)
|
||||
... for r in time_series},
|
||||
... {(category_child1.id, sale1.sale_date.replace(day=1), 1, Decimal('20')),
|
||||
... (category_root2.id, sale1.sale_date.replace(day=1), 1, Decimal('20')),
|
||||
... (category_child2.id, sale1.sale_date.replace(day=1), 1, Decimal('10')),
|
||||
... (category_child1.id, sale2.sale_date.replace(day=1), 1, Decimal('10')),
|
||||
... (category_root2.id, sale2.sale_date.replace(day=1), 1, Decimal('10')),
|
||||
... (account_category.id, sale1.sale_date.replace(day=1), 1, Decimal('30')),
|
||||
... (account_category.id, sale2.sale_date.replace(day=1), 1, Decimal('10'))})
|
||||
>>> len(tree)
|
||||
5
|
||||
>>> with config.set_context(context=context):
|
||||
... assertEqual({(r.name, r.revenue) for r in tree},
|
||||
... {('Root1', Decimal('40')),
|
||||
... ('Child1', Decimal('30')),
|
||||
... ('Child2', Decimal('10')),
|
||||
... ('Root2', Decimal('30')),
|
||||
... ('Account Category', Decimal('40'))})
|
||||
>>> child1, = ProductCategoryTree.find([('rec_name', '=', 'Child1')])
|
||||
>>> child1.rec_name
|
||||
'Child1'
|
||||
|
||||
Check sale reporting per countries::
|
||||
|
||||
>>> RegionTree = Model.get('sale.reporting.region.tree')
|
||||
>>> CountryTree = Model.get('sale.reporting.country.tree')
|
||||
>>> CountryTimeseries = Model.get('sale.reporting.country.time_series')
|
||||
>>> SubdivisionTimeseries = Model.get(
|
||||
... 'sale.reporting.country.subdivision.time_series')
|
||||
>>> with config.set_context(context=context):
|
||||
... region = RegionTree(north_america.id)
|
||||
... countries = CountryTree.find([])
|
||||
... country_time_series = CountryTimeseries.find([])
|
||||
... subdivision_time_series = SubdivisionTimeseries.find([])
|
||||
>>> region.revenue
|
||||
Decimal('40.00')
|
||||
>>> region.parent.revenue
|
||||
Decimal('40.00')
|
||||
>>> len(countries)
|
||||
3
|
||||
>>> with config.set_context(context=context):
|
||||
... sorted((c.region, c.number, c.revenue) for c in countries)
|
||||
[('California', 1, Decimal('30.00')), ('New York', 1, Decimal('10.00')), ('United States', 2, Decimal('40.00'))]
|
||||
>>> len(country_time_series)
|
||||
2
|
||||
>>> with config.set_context(context=context):
|
||||
... assertEqual({(r.country.id, r.date, r.number, r.revenue)
|
||||
... for r in country_time_series},
|
||||
... {(country_us.id, sale1.sale_date.replace(day=1), 1, Decimal('30')),
|
||||
... (country_us.id, sale2.sale_date.replace(day=1), 1, Decimal('10'))})
|
||||
>>> len(subdivision_time_series)
|
||||
2
|
||||
>>> with config.set_context(context=context):
|
||||
... assertEqual({(r.subdivision.id, r.date, r.number, r.revenue)
|
||||
... for r in subdivision_time_series},
|
||||
... {(california.id, sale1.sale_date.replace(day=1), 1, Decimal('30')),
|
||||
... (new_york.id, sale2.sale_date.replace(day=1), 1, Decimal('10'))})
|
||||
71
modules/sale/tests/test_module.py
Normal file
71
modules/sale/tests/test_module.py
Normal file
@@ -0,0 +1,71 @@
|
||||
# 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 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 SaleTestCase(
|
||||
PartyCompanyCheckEraseMixin, PartyCheckReplaceMixin, CompanyTestMixin,
|
||||
ModuleTestCase):
|
||||
'Test Sale module'
|
||||
module = 'sale'
|
||||
|
||||
@with_transaction()
|
||||
def test_sale_price(self):
|
||||
"Test sale price"
|
||||
pool = Pool()
|
||||
Account = pool.get('account.account')
|
||||
Template = pool.get('product.template')
|
||||
Product = pool.get('product.product')
|
||||
Uom = pool.get('product.uom')
|
||||
|
||||
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')])
|
||||
pound, = Uom.search([('name', '=', 'Pound')])
|
||||
|
||||
template, = Template.create([{
|
||||
'name': 'Product',
|
||||
'default_uom': g.id,
|
||||
'sale_uom': kg.id,
|
||||
'list_price': Decimal(5),
|
||||
'products': [('create', [{}])],
|
||||
}])
|
||||
product, = template.products
|
||||
|
||||
prices = Product.get_sale_price([product], quantity=100)
|
||||
self.assertEqual(prices, {product.id: Decimal(5000)})
|
||||
prices = Product.get_sale_price([product], quantity=1500)
|
||||
self.assertEqual(prices, {product.id: Decimal(5000)})
|
||||
|
||||
with Transaction().set_context(uom=pound.id):
|
||||
prices = Product.get_sale_price([product], quantity=0.5)
|
||||
self.assertEqual(prices, {product.id: Decimal('2267.9618')})
|
||||
prices = Product.get_sale_price([product], quantity=1.5)
|
||||
self.assertEqual(prices, {product.id: Decimal('2267.9618')})
|
||||
|
||||
|
||||
del ModuleTestCase
|
||||
8
modules/sale/tests/test_scenario.py
Normal file
8
modules/sale/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)
|
||||
87
modules/sale/tryton.cfg
Normal file
87
modules/sale/tryton.cfg
Normal file
@@ -0,0 +1,87 @@
|
||||
[tryton]
|
||||
version=7.8.1
|
||||
depends:
|
||||
account
|
||||
account_invoice
|
||||
account_invoice_stock
|
||||
account_product
|
||||
company
|
||||
country
|
||||
currency
|
||||
ir
|
||||
party
|
||||
product
|
||||
res
|
||||
stock
|
||||
xml:
|
||||
sale.xml
|
||||
configuration.xml
|
||||
sale_reporting.xml
|
||||
party.xml
|
||||
stock.xml
|
||||
product.xml
|
||||
invoice.xml
|
||||
message.xml
|
||||
|
||||
[register]
|
||||
model:
|
||||
ir.Cron
|
||||
party.Party
|
||||
party.PartyCustomerCurrency
|
||||
party.PartySaleMethod
|
||||
product.Configuration
|
||||
product.DefaultLeadTime
|
||||
product.Template
|
||||
product.ProductLeadTime
|
||||
product.Product
|
||||
product.SaleContext
|
||||
stock.Move
|
||||
stock.ShipmentOut
|
||||
stock.ShipmentOutReturn
|
||||
invoice.Invoice
|
||||
invoice.Line
|
||||
configuration.Configuration
|
||||
configuration.ConfigurationSequence
|
||||
configuration.ConfigurationSaleMethod
|
||||
configuration.ConfigurationQuotation
|
||||
sale.Sale
|
||||
sale.SaleIgnoredInvoice
|
||||
sale.SaleRecreatedInvoice
|
||||
sale.SaleLine
|
||||
sale.SaleLineTax
|
||||
sale.SaleLineIgnoredMove
|
||||
sale.SaleLineRecreatedMove
|
||||
sale.HandleShipmentExceptionAsk
|
||||
sale.HandleInvoiceExceptionAsk
|
||||
sale.ReturnSaleStart
|
||||
sale_reporting.Context
|
||||
sale_reporting.Main
|
||||
sale_reporting.MainTimeseries
|
||||
sale_reporting.Customer
|
||||
sale_reporting.CustomerTimeseries
|
||||
sale_reporting.CustomerCategory
|
||||
sale_reporting.CustomerCategoryTimeseries
|
||||
sale_reporting.CustomerCategoryTree
|
||||
sale_reporting.Product
|
||||
sale_reporting.ProductTimeseries
|
||||
sale_reporting.ProductCategory
|
||||
sale_reporting.ProductCategoryTimeseries
|
||||
sale_reporting.ProductCategoryTree
|
||||
sale_reporting.RegionTree
|
||||
sale_reporting.Country
|
||||
sale_reporting.CountryTimeseries
|
||||
sale_reporting.Subdivision
|
||||
sale_reporting.SubdivisionTimeseries
|
||||
sale_reporting.CountryTree
|
||||
wizard:
|
||||
party.Replace
|
||||
party.Erase
|
||||
sale.HandleShipmentException
|
||||
sale.HandleInvoiceException
|
||||
sale.ReturnSale
|
||||
sale.ModifyHeader
|
||||
sale.OpenProduct
|
||||
sale_reporting.OpenRegionTree
|
||||
sale_reporting.OpenCountryTree
|
||||
report:
|
||||
sale.SaleReport
|
||||
17
modules/sale/view/configuration_form.xml
Normal file
17
modules/sale/view/configuration_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>
|
||||
<label name="sale_sequence"/>
|
||||
<field name="sale_sequence"/>
|
||||
<newline />
|
||||
<label name="sale_invoice_method" />
|
||||
<field name="sale_invoice_method" />
|
||||
<label name="sale_shipment_method" />
|
||||
<field name="sale_shipment_method" />
|
||||
<newline/>
|
||||
<label name="sale_quotation_validity"/>
|
||||
<field name="sale_quotation_validity"/>
|
||||
<label name="sale_process_after"/>
|
||||
<field name="sale_process_after"/>
|
||||
</form>
|
||||
11
modules/sale/view/handle_invoice_exception_ask_form.xml
Normal file
11
modules/sale/view/handle_invoice_exception_ask_form.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. -->
|
||||
<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/sale/view/handle_shipment_exception_ask_form.xml
Normal file
10
modules/sale/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>
|
||||
8
modules/sale/view/modify_header_form.xml
Normal file
8
modules/sale/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/sale/view/move_list_shipment.xml
Normal file
13
modules/sale/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="sale_exception_state"/>
|
||||
<button name="cancel" multiple="1"/>
|
||||
<button name="draft" multiple="1"/>
|
||||
</tree>
|
||||
21
modules/sale/view/party_form.xml
Normal file
21
modules/sale/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-sale" name="sale.act_sale_relate" empty="hide"/>
|
||||
<link icon="tryton-sale" name="sale.act_sale_line_relate" empty="hide"/>
|
||||
</xpath>
|
||||
<xpath expr="/form/notebook" position="inside">
|
||||
<page id="sale" string="Sale">
|
||||
<label name="sale_invoice_method"/>
|
||||
<field name="sale_invoice_method"/>
|
||||
<label name="sale_shipment_method"/>
|
||||
<field name="sale_shipment_method"/>
|
||||
|
||||
<label name="customer_currency"/>
|
||||
<field name="customer_currency"/>
|
||||
<newline/>
|
||||
</page>
|
||||
</xpath>
|
||||
</data>
|
||||
9
modules/sale/view/product_configuration_form.xml
Normal file
9
modules/sale/view/product_configuration_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" position="inside">
|
||||
<label name="default_lead_time"/>
|
||||
<field name="default_lead_time"/>
|
||||
</xpath>
|
||||
</data>
|
||||
12
modules/sale/view/product_list_sale_line.xml
Normal file
12
modules/sale/view/product_list_sale_line.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. -->
|
||||
<tree>
|
||||
<field name="code" optional="0"/>
|
||||
<field name="name" expand="1"/>
|
||||
<field name="sale_price_uom"/>
|
||||
<field name="list_price_uom" optional="1"/>
|
||||
<field name="cost_price_uom" optional="1"/>
|
||||
<field name="quantity" symbol="default_uom" optional="0"/>
|
||||
<field name="forecast_quantity" symbol="default_uom" optional="1"/>
|
||||
</tree>
|
||||
19
modules/sale/view/product_sale_context_form.xml
Normal file
19
modules/sale/view/product_sale_context_form.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. -->
|
||||
<form>
|
||||
<group colspan="2" col="4" id="fields">
|
||||
<label name="company"/>
|
||||
<field name="company"/>
|
||||
<label name="currency"/>
|
||||
<field name="currency"/>
|
||||
<label name="sale_date"/>
|
||||
<field name="sale_date"/>
|
||||
<label name="quantity"/>
|
||||
<field name="quantity"/>
|
||||
<label name="customer"/>
|
||||
<field name="customer"/>
|
||||
</group>
|
||||
<field name="locations" colspan="2" yexpand="0"/>
|
||||
<field name="stock_date_end" invisible="1" colspan="4"/>
|
||||
</form>
|
||||
9
modules/sale/view/return_sale_start_form.xml
Normal file
9
modules/sale/view/return_sale_start_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. -->
|
||||
<form col="2">
|
||||
<image name="tryton-warning" xexpand="0" xfill="0"/>
|
||||
<label
|
||||
string="Are you sure to return these/this sale(s)?" id="return"
|
||||
yalign="0.5" xalign="0.0" xexpand="1"/>
|
||||
</form>
|
||||
111
modules/sale/view/sale_form.xml
Normal file
111
modules/sale/view/sale_form.xml
Normal file
@@ -0,0 +1,111 @@
|
||||
<?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="shipment_party"/>
|
||||
<field name="shipment_party"/>
|
||||
<label name="shipment_address"/>
|
||||
<field name="shipment_address" colspan="3"/>
|
||||
|
||||
<label name="description"/>
|
||||
<field name="description" colspan="3"/>
|
||||
<label name="reference"/>
|
||||
<field name="reference"/>
|
||||
|
||||
<notebook colspan="6">
|
||||
<page string="Sale" id="sale" col="6">
|
||||
<label name="sale_date"/>
|
||||
<field name="sale_date"/>
|
||||
<label name="payment_term"/>
|
||||
<field name="payment_term"/>
|
||||
<label name="currency"/>
|
||||
<field name="currency"/>
|
||||
|
||||
<label name="quotation_date"/>
|
||||
<field name="quotation_date"/>
|
||||
<label name="quotation_validity"/>
|
||||
<field name="quotation_validity"/>
|
||||
<label name="quotation_expire"/>
|
||||
<field name="quotation_expire"/>
|
||||
|
||||
<label name="warehouse"/>
|
||||
<field name="warehouse"/>
|
||||
<label name="shipping_date"/>
|
||||
<field name="shipping_date"/>
|
||||
|
||||
<field name="lines" colspan="6" view_ids="sale.sale_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="10" 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"/>
|
||||
<newline/>
|
||||
|
||||
<label name="tax_amount" xalign="1.0" xexpand="1" xfill="0"/>
|
||||
<field name="tax_amount" xalign="1.0" xexpand="0"/>
|
||||
<newline/>
|
||||
|
||||
<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="other">
|
||||
<label name="company"/>
|
||||
<field name="company"/>
|
||||
<label name="origin"/>
|
||||
<field name="origin"/>
|
||||
|
||||
<label name="invoice_method"/>
|
||||
<field name="invoice_method"/>
|
||||
<label name="shipment_method"/>
|
||||
<field name="shipment_method"/>
|
||||
|
||||
<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-out" name="sale.act_shipment_form"/>
|
||||
<link icon="tryton-account" name="sale.act_invoice_form"/>
|
||||
<link icon="tryton-shipment-in" name="sale.act_return_form"/>
|
||||
</group>
|
||||
<group col="-1" colspan="3" id="buttons">
|
||||
<button name="cancel" icon="tryton-cancel"/>
|
||||
<button name="modify_header" icon="tryton-launch"/>
|
||||
<button name="draft"/>
|
||||
<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-forward"/>
|
||||
<button name="process"/>
|
||||
<button name="manual_invoice" icon="tryton-forward"/>
|
||||
<button name="manual_shipment" icon="tryton-forward"/>
|
||||
</group>
|
||||
<field name="party_lang" invisible="1" colspan="6"/>
|
||||
</form>
|
||||
45
modules/sale/view/sale_line_form.xml
Normal file
45
modules/sale/view/sale_line_form.xml
Normal file
@@ -0,0 +1,45 @@
|
||||
<?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="sale"/>
|
||||
<field name="sale" 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="sale.product_view_list_sale_line"/>
|
||||
<newline/>
|
||||
<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 name="shipping_date"/>
|
||||
<field name="shipping_date"/>
|
||||
<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>
|
||||
18
modules/sale/view/sale_line_tree.xml
Normal file
18
modules/sale/view/sale_line_tree.xml
Normal file
@@ -0,0 +1,18 @@
|
||||
<?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="sale" expand="1"/>
|
||||
<field name="customer" expand="1" optional="0"/>
|
||||
<field name="sale_date" optional="1"/>
|
||||
<field name="type" optional="1"/>
|
||||
<field name="product" expand="1" optional="0"/>
|
||||
<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="sale_state"/>
|
||||
</tree>
|
||||
13
modules/sale/view/sale_line_tree_sequence.xml
Normal file
13
modules/sale/view/sale_line_tree_sequence.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 sequence="sequence">
|
||||
<field name="sale" expand="1"/>
|
||||
<field name="type" optional="1"/>
|
||||
<field name="product" expand="1" optional="0"/>
|
||||
<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/sale/view/sale_reporting_context_form.xml
Normal file
17
modules/sale/view/sale_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>
|
||||
<label name="from_date"/>
|
||||
<group id="dates" col="-1">
|
||||
<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>
|
||||
9
modules/sale/view/sale_reporting_country_tree.xml
Normal file
9
modules/sale/view/sale_reporting_country_tree.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. -->
|
||||
<tree keyword_open="1">
|
||||
<field name="region" expand="1"/>
|
||||
<field name="number"/>
|
||||
<field name="revenue"/>
|
||||
<field name="revenue_trend" expand="1"/>
|
||||
</tree>
|
||||
14
modules/sale/view/sale_reporting_customer_category_list.xml
Normal file
14
modules/sale/view/sale_reporting_customer_category_list.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. -->
|
||||
<data>
|
||||
<xpath expr="//field[@name='number']" position="before">
|
||||
<field name="category" expand="1"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='number']" position="replace">
|
||||
<field name="number"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='revenue']" position="replace">
|
||||
<field name="revenue"/>
|
||||
</xpath>
|
||||
</data>
|
||||
@@ -0,0 +1,7 @@
|
||||
<?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="name" expand="1"/>
|
||||
<field name="revenue"/>
|
||||
</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="customer"/>
|
||||
</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="customer"/>
|
||||
</xpath>
|
||||
</data>
|
||||
8
modules/sale/view/sale_reporting_customer_list.xml
Normal file
8
modules/sale/view/sale_reporting_customer_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. -->
|
||||
<data>
|
||||
<xpath expr="//field[@name='number']" position="before">
|
||||
<field name="customer" expand="1"/>
|
||||
</xpath>
|
||||
</data>
|
||||
11
modules/sale/view/sale_reporting_main_graph_number.xml
Normal file
11
modules/sale/view/sale_reporting_main_graph_number.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. -->
|
||||
<graph>
|
||||
<x>
|
||||
<field name="id"/>
|
||||
</x>
|
||||
<y>
|
||||
<field name="number"/>
|
||||
</y>
|
||||
</graph>
|
||||
11
modules/sale/view/sale_reporting_main_graph_revenue.xml
Normal file
11
modules/sale/view/sale_reporting_main_graph_revenue.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. -->
|
||||
<graph>
|
||||
<x>
|
||||
<field name="id"/>
|
||||
</x>
|
||||
<y>
|
||||
<field name="revenue"/>
|
||||
</y>
|
||||
</graph>
|
||||
8
modules/sale/view/sale_reporting_main_list.xml
Normal file
8
modules/sale/view/sale_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="revenue" sum="1"/>
|
||||
<field name="revenue_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="number"/>
|
||||
</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="revenue"/>
|
||||
</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="revenue" sum="1"/>
|
||||
</tree>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user