diff --git a/__init__.py b/__init__.py
index 74db69d..1a52ad9 100644
--- a/__init__.py
+++ b/__init__.py
@@ -4,6 +4,7 @@
from trytond.pool import Pool
from . import contract
from . import invoice
+from . import history
from . import party
from . import plot
from . import product
@@ -20,6 +21,8 @@ def register():
contract.AgronomicsContract,
contract.AgronomicsContractLine,
invoice.InvoiceLine,
+ history.WineAgingHistory,
+ history.ProductWineAgingHistory,
party.Party,
plot.Enclosure,
plot.Crop,
diff --git a/history.py b/history.py
new file mode 100644
index 0000000..ddf2de8
--- /dev/null
+++ b/history.py
@@ -0,0 +1,72 @@
+# The COPYRIGHT file at the top level of this repository contains
+# the full copyright notices and license terms.
+from sql import Literal, Null
+from sql.aggregate import Min, Sum
+from sql.functions import CurrentTimestamp
+from trytond.pool import Pool
+from trytond.model import ModelSQL, ModelView, fields
+from trytond.pyson import Eval
+from trytond.transaction import Transaction
+
+
+class WineAgingHistory(ModelSQL, ModelView):
+ 'Wine Aging History'
+ __name__ = 'wine.wine_aging.history'
+ production = fields.Many2One('production', "Production",
+ required=True, readonly=True)
+ location = fields.Many2One('stock.location', "Location", required=True,
+ readonly=True)
+ product = fields.Many2One('product.product', "Product", required=True,
+ readonly=True)
+ material = fields.Many2One('stock.location.material', "Material",
+ readonly=True)
+ date_start = fields.Date("Date Start", required=True, readonly=True)
+ date_end = fields.Date("Date End", readonly=True,
+ domain=[
+ ['OR',
+ ('date_end', '=', None),
+ ('date_end', '>=', Eval('date_start')),
+ ],
+ ],
+ depends=['date_start'])
+ duration = fields.Integer("Duration")
+
+ @classmethod
+ def delete(cls, records):
+ pass
+
+
+class ProductWineAgingHistory(ModelSQL, ModelView):
+ "Product Wine Aging History"
+ __name__ = 'product.wine.wine_aging.history'
+
+ product = fields.Many2One('product.product', "Product")
+ material = fields.Many2One('stock.location.material', "Material")
+ duration = fields.Integer("Duration")
+
+ @classmethod
+ def table_query(cls):
+ pool = Pool()
+ WineAgingHistory = pool.get('wine.wine_aging.history')
+
+ wine_aging_history = WineAgingHistory.__table__()
+
+ product_id = Transaction().context.get('product')
+ sql_where = None
+ if product_id:
+ sql_where = (wine_aging_history.product == product_id)
+
+ query = wine_aging_history.select(
+ (Min(wine_aging_history.id * 2)).as_('id'),
+ Literal(0).as_('create_uid'),
+ CurrentTimestamp().as_('create_date'),
+ cls.write_uid.sql_cast(Literal(Null)).as_('write_uid'),
+ cls.write_date.sql_cast(Literal(Null)).as_('write_date'),
+ wine_aging_history.product.as_('product'),
+ wine_aging_history.material.as_('material'),
+ Sum(wine_aging_history.date_end - wine_aging_history.date_start).as_('duration'),
+ group_by=[wine_aging_history.product, wine_aging_history.material])
+ if sql_where:
+ query.where = sql_where
+
+ return query
diff --git a/history.xml b/history.xml
new file mode 100644
index 0000000..61122d8
--- /dev/null
+++ b/history.xml
@@ -0,0 +1,100 @@
+
+
+
+
+
+
+ wine.wine_aging.history
+ form
+ wine_aging_history_form
+
+
+ wine.wine_aging.history
+ tree
+ wine_aging_history_list
+
+
+
+ Wine Aging History
+ wine.wine_aging.history
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ product.wine.wine_aging.history
+ form
+ product_wine_aging_history_form
+
+
+ product.wine.wine_aging.history
+ tree
+ product_wine_aging_history_list
+
+
+
+ Wine Aging History
+ product.wine.wine_aging.history
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/location.py b/location.py
index 62ea0e4..2a61ed6 100644
--- a/location.py
+++ b/location.py
@@ -23,7 +23,7 @@ class Location(metaclass=PoolMeta):
'invisible': Eval('type') != 'storage',
},
depends=['type'])
- material = fields.Selection('get_materials', "Material",
+ material = fields.Many2One('stock.location.material', "Material",
states=tank_states, depends=tank_depends)
uom = fields.Many2One('product.uom', 'Uom',
states=tank_states, depends=tank_depends)
@@ -34,15 +34,6 @@ class Location(metaclass=PoolMeta):
digits=(16, Eval('unit_digits', 2)),
states=tank_states, depends=['unit_digits', 'tank'])
- @classmethod
- def get_materials(field_name):
- pool = Pool()
- LocationMaterial = pool.get('stock.location.material')
- materials = [(None, "")]
- for material in LocationMaterial.search([]):
- materials.append((material.name, material.name))
- return materials
-
@fields.depends('uom')
def on_change_with_unit_digits(self, name=None):
if self.uom:
diff --git a/message.xml b/message.xml
index 2a70203..46a2cf7 100644
--- a/message.xml
+++ b/message.xml
@@ -42,6 +42,9 @@ this repository contains the full copyright notices and license terms. -->
The contract "%(contract)s" cant be activated because the parcel "%(parcel)s" is in another active contract.
+
+ Production "%(production)s" has more than input mark transfer wine aging. Production will be done but no history will be created.
+
The weighing center "%(center)s" dont have a to location configured.
diff --git a/move.py b/move.py
index 693e168..cb967bc 100644
--- a/move.py
+++ b/move.py
@@ -25,6 +25,7 @@ class Move(metaclass=PoolMeta):
with Transaction().set_context(context):
location_quantity = sum(Product.products_by_location(
[move.to_location.id]).values())
- if (location_quantity > move.to_location.max_capacity):
+ if (move.to_location.max_capacity
+ and (location_quantity > move.to_location.max_capacity)):
raise UserError(gettext(
'agronomics.msg_move_amount_exceed'))
diff --git a/product.py b/product.py
index 8d4d85e..bb6bf7f 100644
--- a/product.py
+++ b/product.py
@@ -2,6 +2,9 @@
# this repository contains the full copyright notices and license terms.
from decimal import Decimal
from datetime import datetime
+from sql.operators import (Less, Greater, LessEqual,
+ GreaterEqual, Equal, NotEqual)
+from sql import Null
from trytond.model import ModelSQL, ModelView, fields
from trytond.pool import Pool, PoolMeta
from trytond.pyson import Eval
@@ -116,17 +119,30 @@ class Product(WineMixin, metaclass=PoolMeta):
quality_tests = fields.One2Many('quality.test', 'document', 'Quality Tests')
quality_samples = fields.Many2Many('product.product-quality.sample',
'product', 'sample', 'Quality Samples')
+ wine_aging = fields.One2Many('product.wine.wine_aging.history', 'product',
+ "Wine Aging", readonly=True,
+ context={
+ 'product': Eval('id'),
+ }, depends=['id'])
+ wine_history_material = fields.Function(fields.Text("History Material"),
+ 'get_wine_history', searcher='search_wine_history')
+ wine_history_duration = fields.Function(fields.Text("History Duration"),
+ 'get_wine_history', searcher='search_wine_history')
@classmethod
def deactivate_no_stock_variants_cron(cls):
pool = Pool()
Location = pool.get('stock.location')
ProductConfiguration = pool.get('product.configuration')
+ WineAgingHistory = pool.get('wine.wine_aging.history')
+ Date = pool.get('ir.date')
+
+ today = Date.today()
config = ProductConfiguration(1)
locations = Location.search(['type', '=', 'warehouse'])
locations = [location.id for location in locations]
with Transaction().set_context(locations=locations, with_childs=True):
- if config.variant_deactivation_time:
+ if config.variant_deactivation_time is not None:
products = cls.search(
[
('quantity', '=', 0),
@@ -134,9 +150,14 @@ class Product(WineMixin, metaclass=PoolMeta):
('create_date', '<',
(datetime.now() - config.variant_deactivation_time))
])
- for product in products:
- product.active = False
- cls.save(products)
+ if products:
+ cls.write(products, {'active': False})
+ histories = WineAgingHistory.search([
+ ('product', 'in', products),
+ ('date_end', '=', None),
+ ])
+ if histories:
+ WineAgingHistory.write(histories, {'date_end': today})
@classmethod
def validate(cls, products):
@@ -158,6 +179,52 @@ class Product(WineMixin, metaclass=PoolMeta):
/ 100).quantize(
Decimal(str(10 ** -self.__class__.alcohol_volume.digits[1])))
+ def get_wine_history(self, name):
+ # not implemented
+ pass
+
+ @classmethod
+ def search_wine_history(cls, name, clause):
+ pool = Pool()
+ WineAgingHistory = pool.get('wine.wine_aging.history')
+ Material = pool.get('stock.location.material')
+
+ wineaginghistory = WineAgingHistory.__table__()
+ material = Material.__table__()
+
+ Operator = fields.SQL_OPERATORS[clause[1]]
+ value = clause[2]
+
+ join1 = wineaginghistory.join(material,
+ condition=wineaginghistory.material == material.id)
+ query = join1.select(wineaginghistory.product)
+
+ if name == 'wine_history_material':
+ query.where = (Operator(material.name, clause[2])
+ & (wineaginghistory.material != Null)
+ & (wineaginghistory.duration != Null))
+ elif name == 'wine_history_duration':
+ operator = clause[1]
+ try:
+ value = int(value)
+ except ValueError:
+ value = None
+ if value:
+ if operator == '=':
+ query.where = Equal(wineaginghistory.duration, value)
+ elif operator == '!=':
+ query.where = NotEqual(wineaginghistory.duration, value)
+ elif operator == '>':
+ query.where = Greater(wineaginghistory.duration, value)
+ elif operator == '>=':
+ query.where = GreaterEqual(wineaginghistory.duration, value)
+ elif operator == '<':
+ query.where = Less(wineaginghistory.duration, value)
+ elif operator == '<=':
+ query.where = LessEqual(wineaginghistory.duration, value)
+
+ return [('id', 'in', query)]
+
class Cron(metaclass=PoolMeta):
__name__ = 'ir.cron'
diff --git a/production.py b/production.py
index b631a7e..d4a90a2 100644
--- a/production.py
+++ b/production.py
@@ -4,7 +4,7 @@ from decimal import Decimal
from trytond.model import ModelSQL, ModelView, fields
from trytond.pool import PoolMeta, Pool
from trytond.pyson import Eval, Bool, If
-from trytond.exceptions import UserError
+from trytond.exceptions import UserWarning, UserError
from trytond.i18n import gettext
from trytond.transaction import Transaction
from trytond.wizard import Wizard, StateView, StateAction, Button
@@ -45,6 +45,7 @@ class ProductionTemplate(ModelSQL, ModelView):
cost_distribution_templates = fields.One2Many(
'production.cost_price.distribution.template',
'production_template', "Cost Distribution Templates")
+ transfer_wine_aging = fields.Boolean("Transfer Wine Aging")
inputs_products = fields.Function(fields.One2Many('product.product', None,
'Products'), 'get_products', searcher='search_input_products')
@@ -474,13 +475,60 @@ class Production(metaclass=PoolMeta):
product.varieties = varieties.values()
return product
+ def create_wine_aged_history(self, input, outputs):
+ pool = Pool()
+ WineAgingHistory = pool.get('wine.wine_aging.history')
+
+ effective_date = input.production_input.effective_date
+ histories = WineAgingHistory.search([
+ ('product', '=', input.product),
+ ('date_end', '=', None),
+ ])
+ if histories:
+ to_write = []
+ for history in histories:
+ to_write.extend(([history], {
+ 'date_end': effective_date,
+ 'duration': (effective_date - history.date_start).days,
+ }))
+ WineAgingHistory.write(*to_write)
+
+ new_histories = []
+ for output in outputs:
+ new_histories += WineAgingHistory.create([{
+ 'production': output.production_output,
+ 'location': output.to_location,
+ 'material': output.to_location.material,
+ 'product': output.product,
+ 'date_start': effective_date,
+ 'date_end': None
+ }])
+ if histories:
+ new_histories += WineAgingHistory.copy(histories, {
+ 'production': output.production_output,
+ 'product': output.product,
+ })
+ return new_histories
+
@classmethod
def done(cls, productions):
- Move = Pool().get('stock.move')
+ pool = Pool()
+ Move = pool.get('stock.move')
+ Warning = pool.get('res.user.warning')
+
+ for production in productions:
+ if production.production_template.transfer_wine_aging:
+ if len(production.inputs) > 1:
+ warning_name = 'transfer_wine_aging_input_%s' % production.id
+ if Warning.check(warning_name):
+ raise UserWarning(warning_name,
+ gettext('agronomics.msg_transfer_wine_aging_inputs',
+ production=production.rec_name))
+
moves = []
for production in productions:
for distrib in production.output_distribution:
- if distrib.distribution_state == 'draft':
+ if distrib.distribution_state == 'draft' and distrib.location:
product = production.create_variant(distrib.product,
production.production_template.pass_feature)
product = production.pass_feature(product)
@@ -498,10 +546,17 @@ class Production(metaclass=PoolMeta):
Move.save(moves)
super().done(productions)
+
for production in productions:
for output in production.outputs:
production.copy_quality(output.product)
production.copy_quality_samples(output.product)
+ if production.production_template.transfer_wine_aging:
+ inputs = production.inputs
+ if len(inputs) == 1:
+ input, = inputs
+ outputs = production.outputs
+ production.create_wine_aged_history(input, outputs)
@classmethod
def set_cost(cls, productions):
diff --git a/tryton.cfg b/tryton.cfg
index bba8ee9..5584f52 100644
--- a/tryton.cfg
+++ b/tryton.cfg
@@ -25,3 +25,4 @@ xml:
quality.xml
contract.xml
invoice.xml
+ history.xml
diff --git a/view/product_form.xml b/view/product_form.xml
index a4653e5..116a19e 100644
--- a/view/product_form.xml
+++ b/view/product_form.xml
@@ -182,5 +182,8 @@
+
+
+
diff --git a/view/product_list.xml b/view/product_list.xml
index 97e60c7..31000ae 100644
--- a/view/product_list.xml
+++ b/view/product_list.xml
@@ -124,5 +124,8 @@
+
+
+
diff --git a/view/product_wine_aging_history_form.xml b/view/product_wine_aging_history_form.xml
new file mode 100644
index 0000000..77c849b
--- /dev/null
+++ b/view/product_wine_aging_history_form.xml
@@ -0,0 +1,8 @@
+
diff --git a/view/product_wine_aging_history_list.xml b/view/product_wine_aging_history_list.xml
new file mode 100644
index 0000000..d586d43
--- /dev/null
+++ b/view/product_wine_aging_history_list.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/view/production_template_form.xml b/view/production_template_form.xml
index d6c5272..6e87075 100644
--- a/view/production_template_form.xml
+++ b/view/production_template_form.xml
@@ -23,6 +23,9 @@ this repository contains the full copyright notices and license terms. -->
+
+
+
diff --git a/view/template_form.xml b/view/template_form.xml
index 0653697..37b0bce 100644
--- a/view/template_form.xml
+++ b/view/template_form.xml
@@ -14,5 +14,4 @@ this repository contains the full copyright notices and license terms. -->
-
diff --git a/view/wine_aging_history_form.xml b/view/wine_aging_history_form.xml
new file mode 100644
index 0000000..ec382fa
--- /dev/null
+++ b/view/wine_aging_history_form.xml
@@ -0,0 +1,16 @@
+
diff --git a/view/wine_aging_history_list.xml b/view/wine_aging_history_list.xml
new file mode 100644
index 0000000..3aa9075
--- /dev/null
+++ b/view/wine_aging_history_list.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+