diff --git a/__init__.py b/__init__.py
index e4641a2..888d1a1 100644
--- a/__init__.py
+++ b/__init__.py
@@ -1,6 +1,6 @@
from trytond.pool import Pool
from . import (address, diary, party, product, purchase, sale,
- equipment, configuration_equipment, maintenance, subscription, exceptions)
+ equipment, configuration_equipment, maintenance, move, subscription, exceptions)
def register():
Pool.register(
@@ -12,6 +12,7 @@ def register():
purchase.Purchase,
purchase.Line,
sale.Sale,
+ sale.SaleLine,
sale.CreateSubscriptionStart,
equipment.OpticalEquipment,
equipment.EquipmentMaintenance,
@@ -19,11 +20,11 @@ def register():
maintenance.Maintenance,
maintenance.MaintenanceActivity,
maintenance.MaintenanceLine,
+ move.Move,
+ move.ShipmentOut,
subscription.Subscription,
subscription.SubscriptionEquipment,
- #subscription.CreateContractInvoiceStart,
module='optical_equipment', type_='model')
Pool.register(
sale.CreateSubscription,
- #subscription.CreateContractInvoice,
module='optical_equipment', type_='wizard')
diff --git a/equipment.py b/equipment.py
index bf88cfb..bcab925 100644
--- a/equipment.py
+++ b/equipment.py
@@ -104,6 +104,10 @@ class OpticalEquipment(DeactivableMixin, Workflow, ModelSQL, ModelView):
purchase_origin = fields.Reference("Purchase Origin", selection='get_origin',select=True,
states={'readonly': True})
+ sale_destination = fields.Reference("Sale Destination", selection='get_destination',select=True,
+ states={'readonly': True})
+
+
del _states_serial, _states, _depends
@classmethod
@@ -129,6 +133,24 @@ class OpticalEquipment(DeactivableMixin, Workflow, ModelSQL, ModelView):
return [(None, '')] + [(m, get_name(m)) for m in models]
+ @classmethod
+ def _get_destination(cls):
+ 'Return list of Model names for origin Reference'
+ pool = Pool()
+ Sale = pool.get('sale.line')
+
+ return [Sale.__name__]
+
+ @classmethod
+ def get_destination(cls):
+ Model = Pool().get('ir.model')
+ get_name = Model.get_name
+ models = cls._get_destination()
+
+ return [(None, '')] + [(m, get_name(m)) for m in models]
+
+
+
@classmethod
def __setup__(cls):
@@ -142,7 +164,7 @@ class OpticalEquipment(DeactivableMixin, Workflow, ModelSQL, ModelView):
# 'draft': {
# 'invisible': Eval('state') == 'draft'},
'registred': {
- 'invisible': Eval('state').in_(['registred', 'contrated'])}}
+ 'invisible': Eval('state').in_(['registred', 'uncontrated', 'contrated'])}}
)
@classmethod
diff --git a/move.py b/move.py
new file mode 100644
index 0000000..cc003df
--- /dev/null
+++ b/move.py
@@ -0,0 +1,53 @@
+from trytond.model import fields
+from trytond.pool import Pool, PoolMeta
+
+class Move(metaclass=PoolMeta):
+ "Stock Move"
+ __name__ = "stock.move"
+
+ serial = fields.Char('Serial')
+
+
+
+class ShipmentOut(metaclass=PoolMeta):
+ "Customer Shipment"
+ __name__ = 'stock.shipment.out'
+
+ def _get_inventory_move(self, move):
+ 'Return inventory move for the outgoing move if necessary'
+ pool = Pool()
+ Move = pool.get('stock.move')
+ Uom = pool.get('product.uom')
+ quantity = move.quantity
+
+ for inventory_move in self.inventory_moves:
+ if (inventory_move.origin == move
+ and inventory_move.state != 'cancelled'):
+ quantity -= Uom.compute_qty(
+ inventory_move.uom, inventory_move.quantity, move.uom)
+ quantity = move.uom.round(quantity)
+
+ if quantity <= 0:
+ return
+
+ inventory_move = Move(
+ from_location=self.warehouse_storage,
+ to_location=move.from_location,
+ product=move.product,
+ serial=move.serial,
+ uom=move.uom,
+ quantity=quantity,
+ shipment=self,
+ planned_date=move.planned_date,
+ company=move.company,
+ origin=move,
+ state='staging' if move.state == 'staging' else 'draft',
+ )
+
+ if inventory_move.on_change_with_unit_price_required():
+
+ inventory_move.unit_price = move.unit_price
+
+ inventory_move.currency = move.currency
+
+ return inventory_move
diff --git a/move.xml b/move.xml
new file mode 100644
index 0000000..3b220a6
--- /dev/null
+++ b/move.xml
@@ -0,0 +1,14 @@
+
+
+
+
+ stock.move
+
+ move_list_shipment
+
+
+ stock.move
+
+ move_form
+
+
diff --git a/sale.py b/sale.py
index 76b714a..d4f24f5 100644
--- a/sale.py
+++ b/sale.py
@@ -1,7 +1,7 @@
from trytond.pool import Pool, PoolMeta
from trytond.model import ModelView, ModelSQL, fields
from trytond.modules.currency.fields import Monetary
-from trytond.pyson import Eval, Bool, If
+from trytond.pyson import Eval, Bool, If, Get
from decimal import Decimal
from trytond.modules.product import price_digits
from trytond.transaction import Transaction
@@ -18,6 +18,7 @@ class Sale(metaclass=PoolMeta):
'Sale'
__name__ = 'sale.sale'
+
@classmethod
@ModelView.button
@Workflow.transition('confirmed')
@@ -39,13 +40,24 @@ class Sale(metaclass=PoolMeta):
equipment.propietary=sale.party.id
equipment.propietary_address=sale.shipment_address.id
equipment.state="uncontrated"
+ equipment.sale_destination = line
+ equipment.maintenance_frequency = "6" if sale.party.client_type == "ips" else "12"
equipment.save()
with Transaction().set_context(
queue_name='sale',
queue_scheduled_at=config.sale_process_after):
cls.__queue__.process(sales)
-
+
+ # @classmethod
+ # def get_equipments_in_lines(self, sales, equipments):
+ # #raise UserError(str(equipments))
+ # equipments = []
+ # for line in sales[0].lines:
+ # if line.product_equipment:
+ # equipments.append(line.equipment.id)
+
+ # return equipments
class SaleLine(metaclass=PoolMeta):
'SaleLine'
@@ -56,9 +68,21 @@ class SaleLine(metaclass=PoolMeta):
domain=[('state', '=', 'registred'),
('product','=', Eval('product'))],
states={'invisible': If(~Eval('product_equipment'), True)},)
+ equipment_serial = fields.Char('Serial',states={'readonly': True,
+ 'invisible': If(~Eval('product_equipment'), True)},
+ depends=['product_equipment'])
address_equipment = fields.Many2One('party.address', "Direccion")
unit_digits = fields.Function(fields.Integer('Unit Digits'),
'on_change_with_unit_digits')
+
+ @fields.depends('product_equipment','equipment')
+ def get_serial_equipment(self):
+ if self.product_equipment:
+ raise UserError(str(self.equipment.serial))
+ return self.equipment.serial
+ else:
+ raise UserError(str(self.equipment.serial))
+ return None
def on_change_with_unit_digits(self, name=None):
if self.unit:
@@ -70,6 +94,7 @@ class SaleLine(metaclass=PoolMeta):
if self.equipment:
self.product = self.equipment.product.id
self.address_equipment = self.sale.shipment_address.id
+ self.equipment_serial = self.equipment.serial
self.on_change_product()
else:
self.address_equipment = None
@@ -78,6 +103,7 @@ class SaleLine(metaclass=PoolMeta):
self.quantity = None
self.unit_price = None
self.amount = None
+ self.equipment_serial = None
self.on_change_product()
@fields.depends('product_equipment', methods=['on_change_equipment'])
@@ -98,45 +124,105 @@ class SaleLine(metaclass=PoolMeta):
self.unit = None
return
- party = None
+ else:
+ party = None
- if self.sale and self.sale.party:
- self.product_equipment = False
- party = self.sale.party
+ if self.sale and self.sale.party:
+ self.product_equipment = False
+ party = self.sale.party
+
+ # Set taxes before unit_price to have taxes in context of sale price
+ taxes = []
+ pattern = self._get_tax_rule_pattern()
+ for tax in self.product.customer_taxes_used:
+ if party and party.customer_tax_rule:
+ tax_ids = party.customer_tax_rule.apply(tax, pattern)
+ if tax_ids:
+ taxes.extend(tax_ids)
+ continue
+ taxes.append(tax.id)
- # Set taxes before unit_price to have taxes in context of sale price
- taxes = []
- pattern = self._get_tax_rule_pattern()
- for tax in self.product.customer_taxes_used:
if party and party.customer_tax_rule:
- tax_ids = party.customer_tax_rule.apply(tax, pattern)
+ tax_ids = party.customer_tax_rule.apply(None, pattern)
if tax_ids:
taxes.extend(tax_ids)
- continue
- taxes.append(tax.id)
+ self.taxes = taxes
- if party and party.customer_tax_rule:
- tax_ids = party.customer_tax_rule.apply(None, pattern)
- if tax_ids:
- taxes.extend(tax_ids)
- self.taxes = taxes
+ category = self.product.sale_uom.category
+ if not self.unit or self.unit.category != category:
+ self.unit = self.product.sale_uom
+ self.unit_digits = self.product.sale_uom.digits
- category = self.product.sale_uom.category
- if not self.unit or self.unit.category != category:
- self.unit = self.product.sale_uom
- self.unit_digits = self.product.sale_uom.digits
+ with Transaction().set_context(self._get_context_sale_price()):
+ self.unit_price = Product.get_sale_price([self.product],
+ self.quantity or 0)[self.product.id]
- with Transaction().set_context(self._get_context_sale_price()):
- self.unit_price = Product.get_sale_price([self.product],
- self.quantity or 0)[self.product.id]
+ if self.unit_price:
+ self.unit_price = self.unit_price.quantize(
+ Decimal(1) / 10 ** self.__class__.unit_price.digits[1])
- if self.unit_price:
- self.unit_price = self.unit_price.quantize(
- Decimal(1) / 10 ** self.__class__.unit_price.digits[1])
+ self.type = 'line'
+ self.amount = self.on_change_with_amount()
- self.type = 'line'
- self.amount = self.on_change_with_amount()
- self.product_equipment = True
+ if self.product.equipment:
+ self.product_equipment = True
+
+
+ def get_move(self, shipment_type):
+
+ '''
+ Return moves for the sale line according to shipment_type
+ '''
+
+ pool = Pool()
+ Move = pool.get('stock.move')
+
+ if self.type != 'line':
+ return
+
+ if not self.product:
+ return
+
+ if self.product.type not in Move.get_product_types():
+ return
+
+ if (shipment_type == 'out') != (self.quantity >= 0):
+ return
+
+
+ quantity = (self._get_move_quantity(shipment_type)
+ - self._get_shipped_quantity(shipment_type))
+
+ quantity = self.unit.round(quantity)
+
+ if quantity <= 0:
+ return
+
+ if not self.sale.party.customer_location:
+ raise PartyLocationError(
+ gettext('sale.msg_sale_customer_location_required',
+ sale=self.sale.rec_name,
+ party=self.sale.party.rec_name))
+
+ move = Move()
+ move.quantity = quantity
+ move.uom = self.unit
+ move.product = self.product
+ move.from_location = self.from_location
+ move.to_location = self.to_location
+ move.state = 'draft'
+ move.company = self.sale.company
+ move.serial = self.equipment_serial
+
+ if move.on_change_with_unit_price_required():
+ move.unit_price = self.unit_price
+ move.currency = self.sale.currency
+
+ move.planned_date = self.planned_shipping_date
+ move.invoice_lines = self._get_move_invoice_lines(shipment_type)
+ move.origin = self
+
+ return move
@classmethod
@ModelView.button
diff --git a/sale.xml b/sale.xml
index 9266f01..f24d340 100644
--- a/sale.xml
+++ b/sale.xml
@@ -6,6 +6,16 @@
sale_line_form
+
+ sale.line
+
+ sale_line_tree
+
+
+ sale.line
+
+ sale_line_tree_sequence
+
sale.create.subscription.start
form
diff --git a/tryton.cfg b/tryton.cfg
index 299a347..d4a5581 100644
--- a/tryton.cfg
+++ b/tryton.cfg
@@ -23,6 +23,7 @@ xml:
purchase.xml
uom.xml
maintenance.xml
+ move.xml
subscription.xml
message.xml
\ No newline at end of file
diff --git a/view/move_form.xml b/view/move_form.xml
new file mode 100644
index 0000000..d470067
--- /dev/null
+++ b/view/move_form.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
diff --git a/view/move_list_shipment.xml b/view/move_list_shipment.xml
new file mode 100644
index 0000000..d189157
--- /dev/null
+++ b/view/move_list_shipment.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
diff --git a/view/optical_equipment_form.xml b/view/optical_equipment_form.xml
index 42e4c87..294d42f 100644
--- a/view/optical_equipment_form.xml
+++ b/view/optical_equipment_form.xml
@@ -59,7 +59,11 @@
+
+
+
+
diff --git a/view/sale_line_form.xml b/view/sale_line_form.xml
index 834c189..6876941 100644
--- a/view/sale_line_form.xml
+++ b/view/sale_line_form.xml
@@ -10,7 +10,12 @@
expr="/form/notebook/page[@id='general']/label[@name='product']" position="before">
+
+
+
+
diff --git a/view/sale_line_tree.xml b/view/sale_line_tree.xml
new file mode 100644
index 0000000..e9e1cd2
--- /dev/null
+++ b/view/sale_line_tree.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/view/sale_line_tree_sequence.xml b/view/sale_line_tree_sequence.xml
new file mode 100644
index 0000000..7aeacd9
--- /dev/null
+++ b/view/sale_line_tree_sequence.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+