diff --git a/__init__.py b/__init__.py
index 22b1b7d..f862838 100644
--- a/__init__.py
+++ b/__init__.py
@@ -1,11 +1,13 @@
# The COPYRIGHT file at the top level of this repository contains
# the full copyright notices and license terms.
from trytond.pool import Pool
-from .sale_cost import SaleCost
+from . import purchase
+from . import sale_cost
def register():
Pool.register(
- SaleCost,
+ sale_cost.SaleCost,
+ sale_cost.CostType,
+ purchase.Line,
module='sale_cost_apply_purchase', type_='model')
-
diff --git a/locale/es.po b/locale/es.po
index 02afa11..40d86a0 100644
--- a/locale/es.po
+++ b/locale/es.po
@@ -5,3 +5,11 @@ msgstr "Content-Type: text/plain; charset=utf-8\n"
msgctxt "field:sale.cost,purchase:"
msgid "Purchase"
msgstr "Compra"
+
+msgctxt "field:sale.cost,purchase_lines:"
+msgid "Purchase lines"
+msgstr "LĂneas de compra"
+
+msgctxt "selection:sale.cost.type,apply_method:"
+msgid "Purchase"
+msgstr "Compra"
\ No newline at end of file
diff --git a/purchase.py b/purchase.py
new file mode 100644
index 0000000..93332e6
--- /dev/null
+++ b/purchase.py
@@ -0,0 +1,13 @@
+# The COPYRIGHT file at the top level of this repository contains the full
+# copyright notices and license terms.
+from trytond.pool import PoolMeta
+
+
+class Line(metaclass=PoolMeta):
+ __name__ = 'purchase.line'
+
+ @classmethod
+ def _get_origin(cls):
+ result = super()._get_origin()
+ result.append('sale.cost')
+ return result
diff --git a/sale_cost.py b/sale_cost.py
index 3a316c8..c54c41d 100644
--- a/sale_cost.py
+++ b/sale_cost.py
@@ -1,16 +1,29 @@
# The COPYRIGHT file at the top level of this repository contains the full
# copyright notices and license terms.
+from decimal import Decimal
from trytond.pool import PoolMeta, Pool
from trytond.model import fields
from trytond.pyson import Eval, Not, Bool, Or
+from trytond.transaction import Transaction
+from trytond.exceptions import UserError
+from trytond.i18n import gettext
+from sql.operators import Concat
+from functools import partial
+from itertools import groupby
class SaleCost(metaclass=PoolMeta):
__name__ = 'sale.cost'
- purchase = fields.Many2One('purchase.purchase', 'Purchase', readonly=True,
+ purchase = fields.Function(
+ fields.Many2One('purchase.purchase', 'Purchase', readonly=True,
states={
- 'invisible': Eval('apply_method', '') != 'invoice_in'
+ 'invisible': Eval('apply_method', '') != 'purchase'
+ }, depends=['apply_method']), 'get_purchase')
+ purchase_lines = fields.One2Many('purchase.line', 'origin',
+ 'Purchase lines',
+ states={
+ 'invisible': Eval('apply_method', '') != 'purchase'
}, depends=['apply_method'])
@classmethod
@@ -26,67 +39,117 @@ class SaleCost(metaclass=PoolMeta):
invisible_condition,
Not(Bool(Eval('purchase'))))
cls._buttons['unapply']['depends'].append('purchase')
+ cls.apply_method.selection.append(('purchase', 'Purchase'))
+ cls.invoice_party.states['invisible'] &= (
+ Eval('apply_method') != 'purchase')
@classmethod
- def create_invoice_lines(cls, apply_method, costs):
+ def __register__(cls, module_name):
+ table = cls.__table_handler__(module_name)
+ Purchase_lines = Pool().get('purchase.line')
+ cursor = Transaction().connection.cursor()
+ sql_table = cls.__table__()
+ purchase_line = Purchase_lines.__table__()
+
+ purchase_exists = table.column_exist('purchase')
+
+ super(SaleCost, cls).__register__(module_name)
+
+ if purchase_exists:
+ cursor.execute(*purchase_line.join(sql_table,
+ condition=purchase_line.purchase == sql_table.purchase
+ ).select(
+ purchase_line.id,
+ sql_table.id,
+ where=(purchase_line.type == 'line'))
+ )
+
+ for pline_id, cost_id in cursor.fetchall():
+ cursor.execute(*purchase_line.update(
+ columns=[purchase_line.origin],
+ values=[Concat(cls.__name__ + ',', cost_id)],
+ where=purchase_line.id == pline_id
+ ))
+
+ table.drop_column('purchase')
+
+ def get_purchase(self, name=None):
+ if self.purchase_lines:
+ return self.purchase_lines[0].purchase.id
+
+ @classmethod
+ def _apply_method(cls, apply_method, costs):
Purchase = Pool().get('purchase.purchase')
- if apply_method != 'invoice_in':
- return super().create_invoice_lines(apply_method, costs)
+ if apply_method != 'purchase':
+ return super()._apply_method(apply_method, costs)
- lines = []
- for cost in costs:
- lines.extend(cost._get_purchase_lines(apply_method))
- if lines:
- Purchase.quote(list(set([l.purchase for l in lines])))
+ cost_keyfunc = partial(cls._get_purchase_keygroup)
+ sorted_cost_lines = sorted(costs, key=cost_keyfunc)
+ purchases = []
+ for key, grouped_lines in groupby(sorted_cost_lines, key=cost_keyfunc):
+ purchase = Purchase(**dict(key))
+ purchase.on_change_party()
+ lines = []
+
+ group_lines = list(grouped_lines)
+ for cost in group_lines:
+ lines.append(cost._get_purchase_line(purchase))
+ purchase.lines = lines
+ purchases.append(purchase)
+ Purchase.save(purchases)
+ Purchase.quote(purchases)
return []
- def _get_purchase_lines(self, apply_method):
+ def _get_purchase_line(self, purchase):
pool = Pool()
PurchaseLine = pool.get('purchase.line')
- Purchase = pool.get('purchase.purchase')
-
- if not self.amount or self.purchase:
- return []
- purchase = Purchase(
- party=self.invoice_party,
- company=self.document.company,
- purchase_date=self.sale.sale_date)
- purchase.on_change_party()
- self.purchase = purchase
line = PurchaseLine()
+ line.purchase = purchase
+ line.type = 'line'
line.product = self.type_.product
- line.quantity = 1
- line.unit = self.type_.product.default_uom
line.on_change_product()
+ line.quantity = self.quantity
line.on_change_quantity()
- line.unit_price = self.amount
- taxes = []
- for tax in line.product.supplier_taxes_used:
- if self.invoice_party.supplier_tax_rule:
- pattern = self._get_tax_rule_pattern()
- tax_ids = self.invoice_party.supplier_tax_rule.apply(
- tax, pattern)
- if tax_ids:
- taxes.extend(tax_ids)
- continue
- taxes.append(tax.id)
- line.taxes = taxes
- purchase.lines = [line]
- self.save()
- return list(purchase.lines)
+ digits = PurchaseLine.unit_price.digits[1]
+ line.unit_price = (self.amount / Decimal(str(self.quantity))
+ ).quantize(Decimal(10) ** -Decimal(digits))
+ line.amount = line.on_change_with_amount()
+ line.origin = self
+ return line
+
+ @classmethod
+ def _get_purchase_keygroup(cls, cost):
+ if not cost.invoice_party:
+ raise UserError(gettext('document_cost_apply_invoice.'
+ 'msg_document_cost_apply_invoice_invoice_no_party',
+ type_=cost.type_.rec_name,
+ cost=cost.document.rec_name))
+ return [
+ ('company', cost.document.company),
+ ('party', cost.invoice_party),
+ ('purchase_date', cost.sale.sale_date)]
@classmethod
def _unapply_method(cls, apply_method, costs):
pool = Pool()
Purchase = pool.get('purchase.purchase')
- if apply_method != 'invoice_in':
+ if apply_method != 'purchase':
super(SaleCost, cls)._unapply_method(apply_method, costs)
purchases = Purchase.search([
('id', 'in', [c.purchase.id for c in costs if c.purchase])])
if purchases:
Purchase.delete(purchases)
+
+
+class CostType(metaclass=PoolMeta):
+ __name__ = 'sale.cost.type'
+
+ @classmethod
+ def __setup__(cls):
+ super(CostType, cls).__setup__()
+ cls.apply_method.selection.append(('purchase', 'Purchase'))
diff --git a/setup.py b/setup.py
index 1415e5c..78baf40 100644
--- a/setup.py
+++ b/setup.py
@@ -9,7 +9,8 @@ import io
from configparser import ConfigParser
MODULE2PREFIX = {
- 'sale_cost_apply_invoice': 'datalife'
+ 'sale_cost_apply_invoice': 'datalife',
+ 'purchase_origin': 'datalife'
}
@@ -53,6 +54,19 @@ dependency_links = {
'trytond-sale_cost_apply_invoice@%(branch)s'
'#egg=datalife_sale_cost_apply_invoice' % {
'branch': branch,
+ },
+ 'purchase_origin':
+ 'git+https://gitlab.com/datalifeit/'
+ 'trytond-purchase_origin@%(branch)s'
+ '#egg=datalife_purchase_origin' % {
+ 'branch': branch,
+ },
+ 'sale_processing2confirmed':
+ 'git+https://gitlab.com/datalifeit/'
+ 'trytond-sale_processing2confirmed@%(branch)s'
+ '#egg=nantic_sale_processing2confirmed-%(series)s' % {
+ 'branch': branch,
+ 'series': series
}
}
@@ -69,7 +83,10 @@ for dep in info.get('depends', []):
requires.append(get_require_version('trytond'))
-tests_require = [get_require_version('proteus')]
+tests_require = [
+ get_require_version('proteus'),
+ get_require_version('nantic_sale_processing2confirmed'),
+]
dependency_links = list(dependency_links.values())
if minor_version % 2:
diff --git a/tests/scenario_sale_cost_apply_purchase.rst b/tests/scenario_sale_cost_apply_purchase.rst
index 5d796b9..21d3325 100644
--- a/tests/scenario_sale_cost_apply_purchase.rst
+++ b/tests/scenario_sale_cost_apply_purchase.rst
@@ -20,7 +20,7 @@ Imports::
Install sale_cost_apply_purchase::
- >>> config = activate_modules('sale_cost_apply_purchase')
+ >>> config = activate_modules(['sale_cost_apply_purchase', 'sale_processing2confirmed'])
Create company::
@@ -165,6 +165,36 @@ Sale 2 products::
>>> sale.untaxed_amount, sale.tax_amount, sale.total_amount
(Decimal('29.00'), Decimal('2.90'), Decimal('31.90'))
+Create cost type purchase::
+
+ >>> type_invoice.apply_method = 'purchase'
+ >>> type_invoice.product = service
+ >>> type_invoice.save()
+
+Check purchase lines::
+
+ >>> SaleCost = Model.get('sale.cost')
+ >>> sale_cost = SaleCost()
+ >>> sale_cost.invoice_party = supplier
+ >>> sale_cost.document = sale
+ >>> sale_cost.type_ = type_invoice
+ >>> sale_cost.template = template
+ >>> sale_cost.save()
+ >>> sale_cost.click('apply')
+ >>> len([sale_cost.purchase])
+ 1
+ >>> PurchaseLine = Model.get('purchase.line')
+ >>> line, = PurchaseLine.find([()])
+ >>> line.origin == sale_cost
+ True
+ >>> sale_cost.click('unapply')
+ >>> sale_cost.reload()
+ >>> lines = PurchaseLine.find([()])
+ >>> len(lines)
+ 0
+ >>> sale_cost.click('delete')
+
+
Check cost applying::
>>> invoice_cost, = sale.costs
@@ -180,7 +210,7 @@ Check cost applying::
True
>>> line.unit_price == invoice_cost.amount
True
- >>> sale.click('quote')
+ >>> sale.click('unprocess')
>>> lines = PurchaseLine.find([()])
>>> len(lines)
0
\ No newline at end of file
diff --git a/tryton.cfg b/tryton.cfg
index 080d9f8..d5ecb2b 100644
--- a/tryton.cfg
+++ b/tryton.cfg
@@ -5,5 +5,6 @@ depends:
res
sale_cost_apply_invoice
purchase
+ purchase_origin
xml:
- sale_cost.xml
+ sale_cost.xml
\ No newline at end of file
diff --git a/view/cost_form.xml b/view/cost_form.xml
index 7db8922..e13af20 100644
--- a/view/cost_form.xml
+++ b/view/cost_form.xml
@@ -6,4 +6,9 @@
+
+
+
+
+
\ No newline at end of file