2014-12-02 12:56:03 +01:00
|
|
|
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
|
|
|
|
# this repository contains the full copyright notices and license terms.
|
2017-04-01 00:38:16 +02:00
|
|
|
from trytond import backend
|
2018-12-29 14:20:29 +01:00
|
|
|
from trytond.i18n import gettext
|
2017-04-01 00:38:16 +02:00
|
|
|
from trytond.model import ModelSQL, ValueMixin, fields
|
2018-12-29 14:20:29 +01:00
|
|
|
from trytond.model.exceptions import RequiredValidationError
|
2020-04-23 21:44:00 +02:00
|
|
|
from trytond.modules.product import round_price
|
2017-12-20 19:31:44 +01:00
|
|
|
from trytond.modules.purchase.purchase import (
|
|
|
|
|
get_shipments_returns, search_shipments_returns)
|
2021-12-11 17:58:44 +01:00
|
|
|
from trytond.pool import Pool, PoolMeta
|
|
|
|
|
from trytond.pyson import Eval
|
|
|
|
|
from trytond.tools.multivalue import migrate_property
|
2022-03-09 00:57:17 +01:00
|
|
|
from trytond.transaction import Transaction
|
2017-12-20 19:31:44 +01:00
|
|
|
|
2017-04-01 00:38:16 +02:00
|
|
|
purchase_drop_location = fields.Many2One(
|
|
|
|
|
'stock.location', "Purchase Drop Location", domain=[('type', '=', 'drop')])
|
2013-02-26 20:51:27 +01:00
|
|
|
|
|
|
|
|
|
2020-03-01 12:33:54 +01:00
|
|
|
class Request(metaclass=PoolMeta):
|
2013-02-26 20:51:27 +01:00
|
|
|
__name__ = 'purchase.request'
|
|
|
|
|
|
|
|
|
|
customer = fields.Many2One('party.party', 'Customer', readonly=True,
|
|
|
|
|
states={
|
|
|
|
|
'invisible': ~Eval('customer'),
|
2021-03-05 15:49:20 +01:00
|
|
|
},
|
|
|
|
|
context={
|
|
|
|
|
'company': Eval('company', -1),
|
|
|
|
|
},
|
2022-04-08 19:07:14 +02:00
|
|
|
depends={'company'})
|
2013-02-26 20:51:27 +01:00
|
|
|
delivery_address = fields.Many2One('party.address', 'Delivery Address',
|
|
|
|
|
domain=[('party', '=', Eval('customer'))],
|
|
|
|
|
states={
|
|
|
|
|
'invisible': ~Eval('customer'),
|
|
|
|
|
'readonly': Eval('state') != 'draft',
|
2022-04-08 19:07:14 +02:00
|
|
|
})
|
2013-02-26 20:51:27 +01:00
|
|
|
|
|
|
|
|
|
2020-03-01 12:33:54 +01:00
|
|
|
class Configuration(metaclass=PoolMeta):
|
2015-10-04 17:55:39 +02:00
|
|
|
__name__ = 'purchase.configuration'
|
|
|
|
|
|
2017-04-01 00:38:16 +02:00
|
|
|
purchase_drop_location = fields.MultiValue(purchase_drop_location)
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def default_purchase_drop_location(cls, **pattern):
|
|
|
|
|
return cls.multivalue_model(
|
|
|
|
|
'purchase_drop_location').default_purchase_drop_location()
|
|
|
|
|
|
|
|
|
|
|
2020-03-01 12:33:54 +01:00
|
|
|
class ConfigurationPurchaseDropLocation(ModelSQL, ValueMixin):
|
2017-04-01 00:38:16 +02:00
|
|
|
"Purchase Configuration Purchase Drop Location"
|
|
|
|
|
__name__ = 'purchase.configuration.purchase_drop_location'
|
|
|
|
|
purchase_drop_location = purchase_drop_location
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def __register__(cls, module_name):
|
2019-12-06 17:33:47 +01:00
|
|
|
exist = backend.TableHandler.table_exist(cls._table)
|
2017-04-01 00:38:16 +02:00
|
|
|
|
2020-03-01 12:33:54 +01:00
|
|
|
super().__register__(module_name)
|
2017-04-01 00:38:16 +02:00
|
|
|
|
|
|
|
|
if not exist:
|
|
|
|
|
cls._migrate_property([], [], [])
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def _migrate_property(cls, field_names, value_names, fields):
|
|
|
|
|
field_names.append('purchase_drop_location')
|
|
|
|
|
value_names.append('purchase_drop_location')
|
|
|
|
|
migrate_property(
|
|
|
|
|
'purchase.configuration', field_names, cls, value_names,
|
|
|
|
|
fields=fields)
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def default_purchase_drop_location(cls):
|
|
|
|
|
pool = Pool()
|
|
|
|
|
ModelData = pool.get('ir.model.data')
|
2017-04-01 10:43:14 +02:00
|
|
|
try:
|
|
|
|
|
return ModelData.get_id(
|
|
|
|
|
'sale_supply_drop_shipment', 'location_drop')
|
|
|
|
|
except KeyError:
|
|
|
|
|
return None
|
2015-10-04 17:55:39 +02:00
|
|
|
|
|
|
|
|
|
2018-06-26 19:11:55 +02:00
|
|
|
class Purchase(metaclass=PoolMeta):
|
2013-02-26 20:51:27 +01:00
|
|
|
__name__ = 'purchase.purchase'
|
|
|
|
|
|
|
|
|
|
customer = fields.Many2One('party.party', 'Customer', readonly=True,
|
|
|
|
|
states={
|
|
|
|
|
'invisible': ~Eval('customer'),
|
2021-03-05 15:49:20 +01:00
|
|
|
},
|
|
|
|
|
context={
|
|
|
|
|
'company': Eval('company', -1),
|
|
|
|
|
},
|
2022-04-08 19:07:14 +02:00
|
|
|
depends={'company'})
|
2013-02-26 20:51:27 +01:00
|
|
|
delivery_address = fields.Many2One('party.address', 'Delivery Address',
|
|
|
|
|
domain=[('party', '=', Eval('customer'))],
|
|
|
|
|
states={
|
|
|
|
|
'readonly': Eval('state') != 'draft',
|
|
|
|
|
'invisible': ~Eval('customer'),
|
2022-04-08 19:07:14 +02:00
|
|
|
})
|
2019-03-27 00:28:57 +01:00
|
|
|
drop_shipments = fields.Function(fields.Many2Many('stock.shipment.drop',
|
|
|
|
|
None, None, "Drop Shipments",
|
2013-02-26 20:51:27 +01:00
|
|
|
states={
|
|
|
|
|
'invisible': ~Eval('customer'),
|
2022-04-08 19:07:14 +02:00
|
|
|
}),
|
2017-12-20 19:31:44 +01:00
|
|
|
'get_drop_shipments', searcher='search_drop_shipments')
|
2015-10-04 17:55:39 +02:00
|
|
|
drop_location = fields.Many2One('stock.location', 'Drop Location',
|
|
|
|
|
domain=[('type', '=', 'drop')],
|
|
|
|
|
states={
|
|
|
|
|
'invisible': ~Eval('customer', False),
|
|
|
|
|
'required': Eval('customer', False),
|
2022-04-08 19:07:14 +02:00
|
|
|
})
|
2013-02-26 20:51:27 +01:00
|
|
|
|
2015-10-04 17:55:39 +02:00
|
|
|
@staticmethod
|
|
|
|
|
def default_drop_location():
|
|
|
|
|
pool = Pool()
|
|
|
|
|
PurchaseConfig = pool.get('purchase.configuration')
|
|
|
|
|
|
|
|
|
|
config = PurchaseConfig(1)
|
2017-08-08 17:18:38 +02:00
|
|
|
if config.purchase_drop_location:
|
|
|
|
|
return config.purchase_drop_location.id
|
2015-10-04 17:55:39 +02:00
|
|
|
|
2014-01-26 14:29:36 +01:00
|
|
|
@fields.depends('customer', 'delivery_address')
|
2013-02-26 20:51:27 +01:00
|
|
|
def on_change_customer(self):
|
2014-10-29 20:40:23 +01:00
|
|
|
self.delivery_address = None
|
2013-02-26 20:51:27 +01:00
|
|
|
if self.customer:
|
2014-10-29 20:40:23 +01:00
|
|
|
self.delivery_address = self.customer.address_get(type='delivery')
|
2013-02-26 20:51:27 +01:00
|
|
|
|
2017-05-22 18:12:24 +02:00
|
|
|
@property
|
|
|
|
|
def delivery_full_address(self):
|
|
|
|
|
address = super(Purchase, self).delivery_full_address
|
|
|
|
|
if self.customer and self.delivery_address:
|
|
|
|
|
address = self.delivery_address.full_address
|
|
|
|
|
return address
|
|
|
|
|
|
2017-12-20 19:31:44 +01:00
|
|
|
get_drop_shipments = get_shipments_returns('stock.shipment.drop')
|
|
|
|
|
search_drop_shipments = search_shipments_returns('stock.shipment.drop')
|
2013-02-26 20:51:27 +01:00
|
|
|
|
2014-01-09 18:42:28 +01:00
|
|
|
def check_for_quotation(self):
|
|
|
|
|
super(Purchase, self).check_for_quotation()
|
|
|
|
|
if self.customer and not self.delivery_address:
|
2018-12-29 14:20:29 +01:00
|
|
|
raise RequiredValidationError(
|
|
|
|
|
gettext('sale_supply_drop_shipment'
|
|
|
|
|
'.msg_delivery_address_required_quotation_purchase') % {
|
|
|
|
|
'purchase': self.rec_name,
|
|
|
|
|
})
|
2014-01-09 18:42:28 +01:00
|
|
|
|
2021-10-08 19:21:06 +02:00
|
|
|
@classmethod
|
|
|
|
|
def _process_shipment(cls, purchases):
|
2015-10-04 17:55:39 +02:00
|
|
|
pool = Pool()
|
|
|
|
|
DropShipment = pool.get('stock.shipment.drop')
|
|
|
|
|
|
2021-10-08 19:21:06 +02:00
|
|
|
drop_shipments = []
|
|
|
|
|
for purchase in purchases:
|
|
|
|
|
if purchase.customer:
|
|
|
|
|
moves = purchase.create_move('in')
|
|
|
|
|
if moves:
|
|
|
|
|
drop_shipment = purchase.create_drop_shipment()
|
|
|
|
|
drop_shipment.supplier_moves = moves
|
|
|
|
|
drop_shipments.append(drop_shipment)
|
|
|
|
|
DropShipment.save(drop_shipments)
|
|
|
|
|
DropShipment.wait(drop_shipments)
|
|
|
|
|
super()._process_shipment(purchases)
|
2015-10-04 17:55:39 +02:00
|
|
|
|
|
|
|
|
def create_drop_shipment(self):
|
|
|
|
|
pool = Pool()
|
|
|
|
|
DropShipment = pool.get('stock.shipment.drop')
|
|
|
|
|
|
|
|
|
|
return DropShipment(
|
|
|
|
|
company=self.company,
|
|
|
|
|
supplier=self.party,
|
|
|
|
|
contact_address=self.party.address_get(),
|
|
|
|
|
customer=self.customer,
|
|
|
|
|
delivery_address=self.delivery_address,
|
|
|
|
|
)
|
|
|
|
|
|
2013-02-26 20:51:27 +01:00
|
|
|
|
2020-03-01 12:33:54 +01:00
|
|
|
class Line(metaclass=PoolMeta):
|
2013-02-26 20:51:27 +01:00
|
|
|
__name__ = 'purchase.line'
|
|
|
|
|
|
|
|
|
|
def get_to_location(self, name):
|
2020-03-01 12:33:54 +01:00
|
|
|
result = super().get_to_location(name)
|
2014-01-09 18:42:28 +01:00
|
|
|
if self.purchase.customer:
|
2015-10-04 17:55:39 +02:00
|
|
|
return self.purchase.drop_location.id
|
2013-02-26 20:51:27 +01:00
|
|
|
return result
|
|
|
|
|
|
2015-12-31 17:04:20 +01:00
|
|
|
def get_move(self, move_type):
|
2018-04-13 18:49:19 +02:00
|
|
|
pool = Pool()
|
|
|
|
|
Currency = pool.get('currency.currency')
|
2020-03-01 12:33:54 +01:00
|
|
|
move = super().get_move(move_type)
|
2013-02-26 20:51:27 +01:00
|
|
|
if move and self.purchase.customer:
|
2018-04-13 18:49:19 +02:00
|
|
|
cost_price = Currency.compute(
|
|
|
|
|
move.currency, move.unit_price, move.company.currency)
|
2020-04-23 21:44:00 +02:00
|
|
|
move.cost_price = round_price(cost_price)
|
2013-02-26 20:51:27 +01:00
|
|
|
return move
|
|
|
|
|
|
|
|
|
|
|
2018-06-26 19:11:55 +02:00
|
|
|
class ProductSupplier(metaclass=PoolMeta):
|
2013-02-26 20:51:27 +01:00
|
|
|
__name__ = 'purchase.product_supplier'
|
|
|
|
|
|
|
|
|
|
drop_shipment = fields.Boolean('Drop Shipment',
|
|
|
|
|
states={
|
2017-03-08 10:46:46 +01:00
|
|
|
'invisible': ~Eval('drop_shipment_available', False),
|
2022-04-08 19:07:14 +02:00
|
|
|
})
|
2017-03-08 10:46:46 +01:00
|
|
|
drop_shipment_available = fields.Function(
|
|
|
|
|
fields.Boolean("Drop Shipment Available"),
|
|
|
|
|
'on_change_with_drop_shipment_available')
|
|
|
|
|
|
2020-05-22 11:49:35 +02:00
|
|
|
@fields.depends('product', 'template',
|
|
|
|
|
'_parent_product.type', '_parent_product.supply_on_sale',
|
|
|
|
|
'_parent_template.type', '_parent_template.supply_on_sale')
|
2017-03-08 10:46:46 +01:00
|
|
|
def on_change_with_drop_shipment_available(self, name=None):
|
2020-05-22 11:49:35 +02:00
|
|
|
product = self.product or self.template
|
|
|
|
|
if product and product.type in {'goods', 'assets'}:
|
|
|
|
|
return product.supply_on_sale
|
2013-02-26 20:51:27 +01:00
|
|
|
|
|
|
|
|
|
2020-03-01 12:33:54 +01:00
|
|
|
class RequestCreatePurchase(metaclass=PoolMeta):
|
2013-02-26 20:51:27 +01:00
|
|
|
__name__ = 'purchase.request.create_purchase'
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def _group_purchase_key(cls, requests, request):
|
2020-03-01 12:33:54 +01:00
|
|
|
result = super()._group_purchase_key(requests, request)
|
2013-02-26 20:51:27 +01:00
|
|
|
result += (
|
|
|
|
|
('customer', request.customer.id if request.customer else None),
|
|
|
|
|
('delivery_address', request.delivery_address.id
|
|
|
|
|
if request.delivery_address else None),
|
|
|
|
|
)
|
|
|
|
|
return result
|
2014-09-19 11:41:41 +02:00
|
|
|
|
|
|
|
|
|
2020-03-01 12:33:54 +01:00
|
|
|
class HandleShipmentException(metaclass=PoolMeta):
|
2014-09-19 11:41:41 +02:00
|
|
|
__name__ = 'purchase.handle.shipment.exception'
|
|
|
|
|
|
|
|
|
|
def transition_handle(self):
|
|
|
|
|
pool = Pool()
|
|
|
|
|
Sale = pool.get('sale.sale')
|
2015-10-04 17:55:39 +02:00
|
|
|
Move = pool.get('stock.move')
|
2022-03-09 00:57:17 +01:00
|
|
|
transaction = Transaction()
|
|
|
|
|
context = transaction.context
|
2014-09-19 11:41:41 +02:00
|
|
|
|
2020-03-01 12:33:54 +01:00
|
|
|
super().transition_handle()
|
2014-09-19 11:41:41 +02:00
|
|
|
|
2015-10-04 17:55:39 +02:00
|
|
|
sales = set()
|
|
|
|
|
moves = set()
|
|
|
|
|
to_recreate = set(self.ask.recreate_moves)
|
|
|
|
|
domain_moves = set(self.ask.domain_moves)
|
2014-09-19 11:41:41 +02:00
|
|
|
|
2020-07-07 23:56:10 +02:00
|
|
|
for line in self.record.lines:
|
2015-10-04 17:55:39 +02:00
|
|
|
if not set(line.moves) & domain_moves:
|
|
|
|
|
continue
|
|
|
|
|
if not any(m in to_recreate for m in line.moves):
|
2017-02-25 18:44:48 +01:00
|
|
|
for request in line.requests:
|
|
|
|
|
for sale_line in request.sale_lines:
|
|
|
|
|
moves.update({m for m in sale_line.moves
|
|
|
|
|
if (m.state != 'done'
|
|
|
|
|
and m.from_location.type == 'drop')})
|
|
|
|
|
sales.add(sale_line.sale)
|
2015-10-04 17:55:39 +02:00
|
|
|
|
|
|
|
|
if moves:
|
2018-07-27 09:27:11 +02:00
|
|
|
Move.cancel(moves)
|
2015-10-04 17:55:39 +02:00
|
|
|
if sales:
|
2022-03-09 00:57:17 +01:00
|
|
|
with transaction.set_context(
|
|
|
|
|
queue_batch=context.get('queue_batch', True)):
|
|
|
|
|
Sale.__queue__.process(sales)
|
2014-09-19 11:41:41 +02:00
|
|
|
return 'end'
|