Files
2025-12-26 13:11:43 +00:00

173 lines
6.1 KiB
Python
Executable File

# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from collections import defaultdict
import shopify
from shopify.resources.fulfillment import FulfillmentV2
from trytond.i18n import gettext, lazy_gettext
from trytond.model import ModelSQL, ModelView, Unique, fields
from trytond.pool import Pool, PoolMeta
from .common import IdentifierMixin
from .exceptions import ShopifyError
class ShipmentOut(metaclass=PoolMeta):
__name__ = 'stock.shipment.out'
shopify_identifiers = fields.One2Many(
'stock.shipment.shopify_identifier', 'shipment',
lazy_gettext('web_shop_shopify.msg_shopify_identifiers'))
def get_shopify(self, sale):
if self.state != 'done':
return
shopify_id = self.get_shopify_identifier(sale)
if shopify_id:
# Fulfillment can not be modified
return
else:
fulfillment = FulfillmentV2()
for shop_warehouse in sale.web_shop.shopify_warehouses:
if shop_warehouse.warehouse == self.warehouse:
location_id = int(shop_warehouse.shopify_id)
break
else:
location_id = None
fulfillment_orders = shopify.FulfillmentOrders.find(
order_id=sale.shopify_identifier)
line_items = defaultdict(list)
for move in self.outgoing_moves:
if move.sale == sale:
for order_id, line_item in move.get_shopify(
fulfillment_orders, location_id):
line_items[order_id].append(line_item)
if not line_items:
return
fulfillment.line_items_by_fulfillment_order = [{
'fulfillment_order_id': order_id,
'fulfillment_order_line_items': line_items,
}
for order_id, line_items in line_items.items()]
return fulfillment
def get_shopify_identifier(self, sale):
for record in self.shopify_identifiers:
if record.sale == sale:
return record.shopify_identifier
def set_shopify_identifier(self, sale, identifier=None):
pool = Pool()
Identifier = pool.get('stock.shipment.shopify_identifier')
for record in self.shopify_identifiers:
if record.sale == sale:
if not identifier:
Identifier.delete([record])
return
else:
if record.shopify_identifier != identifier:
record.shopify_identifier = identifier
record.save()
return record
if identifier:
record = Identifier(shipment=self, sale=sale)
record.shopify_identifier = identifier
record.save()
return record
@classmethod
def search_shopify_identifier(cls, sale, identifier):
records = cls.search([
('shopify_identifiers', 'where', [
('sale', '=', sale.id),
('shopify_identifier', '=', identifier),
]),
])
if records:
record, = records
return record
@classmethod
def copy(cls, records, default=None):
if default is None:
default = {}
else:
default = default.copy()
default.setdefault('shopify_identifiers')
return super().copy(records, default=default)
class ShipmentShopifyIdentifier(IdentifierMixin, ModelSQL, ModelView):
"Shopify Shipment Identifier"
__name__ = 'stock.shipment.shopify_identifier'
shipment = fields.Reference("Shipment", [
('stock.shipment.out', "Customer Shipment"),
], required=True)
sale = fields.Many2One('sale.sale', "Sale", required=True)
@classmethod
def __setup__(cls):
super().__setup__()
cls.shopify_identifier_signed.states = {
'required': True,
}
t = cls.__table__()
cls._sql_constraints += [
('shipment_sale_unique',
Unique(t, t.shipment, t.sale, t.shopify_identifier_signed),
'web_shop_shopify.msg_identifier_shipment_sale_unique'),
]
class ShipmentOut_PackageShipping(metaclass=PoolMeta):
__name__ = 'stock.shipment.out'
def get_shopify(self, sale):
fulfillment = super().get_shopify(sale)
if fulfillment and self.packages:
tracking_info = []
for package in self.packages:
tracking_info.append({
'number': package.shipping_reference,
'url': package.shipping_tracking_url,
})
fulfillment.tracking_info = tracking_info
return fulfillment
class Move(metaclass=PoolMeta):
__name__ = 'stock.move'
def get_shopify(self, fulfillment_orders, location_id):
pool = Pool()
SaleLine = pool.get('sale.line')
Uom = pool.get('product.uom')
if not isinstance(self.origin, SaleLine):
return
identifier = self.origin.shopify_identifier
quantity = int(Uom.compute_qty(
self.unit, self.quantity, self.origin.unit))
for fulfillment_order in fulfillment_orders:
if fulfillment_order.assigned_location_id != location_id:
continue
for line_item in fulfillment_order.line_items:
if line_item.line_item_id == identifier:
qty = min(quantity, line_item.fulfillable_quantity)
qty = quantity
yield fulfillment_order.id, {
'id': line_item.id,
'quantity': qty,
}
quantity -= qty
if quantity <= 0:
return
else:
raise ShopifyError(gettext(
'web_shop_shopify.msg_fulfillment_order_line_not_found',
quantity=quantity,
move=self.rec_name,
))