parent
7c1e37e325
commit
6cfb63fb3d
|
@ -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')
|
||||
|
||||
|
|
|
@ -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"
|
|
@ -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
|
141
sale_cost.py
141
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'))
|
||||
|
|
21
setup.py
21
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:
|
||||
|
|
|
@ -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
|
|
@ -5,5 +5,6 @@ depends:
|
|||
res
|
||||
sale_cost_apply_invoice
|
||||
purchase
|
||||
purchase_origin
|
||||
xml:
|
||||
sale_cost.xml
|
||||
sale_cost.xml
|
|
@ -6,4 +6,9 @@
|
|||
<label name="purchase"/>
|
||||
<field name="purchase"/>
|
||||
</xpath>
|
||||
<xpath expr="/form/notebook" position="inside">
|
||||
<page name="purchase_lines">
|
||||
<field name="purchase_lines"/>
|
||||
</page>
|
||||
</xpath>
|
||||
</data>
|
Loading…
Reference in New Issue