# 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 decimal import Decimal from trytond.i18n import gettext from trytond.pool import Pool, PoolMeta from .exceptions import BadRequest from .web import split_name class Sale(metaclass=PoolMeta): __name__ = 'sale.sale' @property def vsf_id(self): if self.web_shop: if self.web_shop.guest_party == self.party: return self.web_id else: return self.id def get_vsf_user_order_history(self): firstname, lastname = split_name(self.party.name) return { 'entity_id': self.id, 'increment_id': self.number, 'created_at': self.create_date.isoformat(), 'customer_firstname': firstname, 'customer_lastname': lastname, 'grand_total': float(self.total_amount), 'status': self.state_string, 'items': [ line.get_vsf_user_order_history() for line in self.lines], } @classmethod def search_vsf(cls, cart_id, shop, user=None): domain = [ ('web_shop', '=', shop.id), ('state', '=', 'draft'), ] # cart_id may be either id and web_id cart_id_domain = ['OR'] domain.append(cart_id_domain) try: sale_id = int(cart_id) except ValueError: pass else: if user: party = user.party cart_id_domain.append([ ('id', '=', sale_id), ('party', '=', party.id), ]) cart_id_domain.append(('web_id', '=', cart_id)) try: sale, = cls.search(domain, limit=1) except ValueError: raise BadRequest() return sale @property def vsf_subtotal(self): return self.untaxed_amount def set_vsf(self, data, user=None): if user and self.web_shop and self.party == self.web_shop.guest_party: self.party = user.party self.on_change_party() if user and 'addressInformation' in data: addresses = data['addressInformation'] address_data = addresses.get('shippingAddress') if address_data: address = user.set_vsf_address(address_data, self.party) address.save() if address.party != self.party: self.shipment_party = address.party self.shipment_address = address address_data = addresses.get('billingAddress') if address_data: if address_data != addresses.get('shippingAddress'): address = user.set_vsf_address(address_data, self.party) address.save() if address.party != self.party: self.invoice_party = address.party self.invoice_address = address def get_vsf(self): return { 'grand_total': float(self.total_amount), 'items': [line.get_vsf() for line in self.lines if line.product], 'total_segments': [{ 'code': 'subtotal', 'title': gettext('web_shop_vue_storefront.msg_subtotal'), 'value': float(self.vsf_subtotal), }, { 'code': 'tax', 'title': gettext('web_shop_vue_storefront.msg_tax'), 'value': float(self.tax_amount), }, { 'code': 'grand_total', 'title': gettext( 'web_shop_vue_storefront.msg_grand_total'), 'value': float(self.total_amount), }], } class SaleShipmentCost(metaclass=PoolMeta): __name__ = 'sale.sale' def get_vsf(self): pool = Pool() Tax = pool.get('account.tax') item = super().get_vsf() if self.carrier: cost = self.compute_shipment_cost(self.carrier) if cost is not None: cost_line = self.get_shipment_cost_line(self.carrier, cost) taxes = Tax.compute(cost_line.taxes, cost, 1, self.tax_date) cost += sum(t['amount'] for t in taxes) cost = float(self.currency.round(cost)) item['grand_total'] += cost item['total_segments'].insert(1, { 'code': 'shipping', 'title': gettext( 'web_shop_vue_storefront.msg_shipping'), 'value': cost, }) for segment in item['total_segments']: if segment['code'] == 'grand_total': segment['value'] += cost return item def set_vsf_shipping_methods(self, data): pool = Pool() Country = pool.get('country.country') Address = pool.get('party.address') try: country, = Country.search([ ('code', '=', data['address']['country_id']), ]) except ValueError: raise BadRequest() if not self.shipment_address: self.shipment_address = Address() self.shipment_address.country = country def set_vsf(self, data, user): pool = Pool() Carrier = pool.get('carrier') super().set_vsf(data, user) if 'addressInformation' in data: if data['addressInformation']['shipping_carrier_code']: try: carrier_id = int( data['addressInformation']['shipping_carrier_code']) except ValueError: raise BadRequest() try: carrier, = Carrier.search([ ('id', '=', carrier_id), ], limit=1) except ValueError: raise BadRequest() else: carrier = None # Use id to get proper context self.carrier = carrier.id if carrier else None if not self.carrier: self.shipment_cost_method = None class Line(metaclass=PoolMeta): __name__ = 'sale.line' @property def vsf_tax_amount(self): return sum((t.amount for t in self._get_taxes().values()), Decimal(0)) def get_vsf_user_order_history(self): amount_incl_tax = self.amount + self.vsf_tax_amount digits = self.__class__.unit_price.digits price_incl_tax = ( amount_incl_tax / Decimal(str(self.quantity)) ).quantize(Decimal(1) / 10 ** digits[1]) return { 'name': self.product.name if self.product else '', 'sku': self.product.vsf_sku if self.product else '', 'price_incl_tax': float(price_incl_tax), 'qty_ordered': self.quantity, 'row_total_incl_tax': float(amount_incl_tax), } def get_vsf(self): assert self.product amount_incl_tax = self.amount + self.vsf_tax_amount digits = self.__class__.unit_price.digits price_incl_tax = ( amount_incl_tax / Decimal(str(self.quantity)) ).quantize(Decimal(1) / 10 ** digits[1]) return { 'item_id': self.id, 'sku': self.product.vsf_sku, 'qty': self.quantity, 'name': self.product.name, 'price': float(price_incl_tax), 'product_type': self.product.vsf_type_id, 'quote_id': self.sale.id, 'product_option': { 'extension_attributes': { }, }, } def set_vsf(self, data): pool = Pool() Product = pool.get('product.product') try: self.product, = Product.search([ ('vsf_sku', '=', data['sku']), ], limit=1) except ValueError: raise BadRequest() self.quantity = data['qty'] self.on_change_product() class LineAttribute(metaclass=PoolMeta): __name__ = 'sale.line' def get_vsf_cart(self): assert self.sale.web_shop item = super().get_vsf_cart() if self.product.attributes_set: product_option = item['product_option'] extension_attributes = product_option['extension_attributes'] extension_attributes['configurable_item_options'] = options = [] for attribute in self.product.attributes_set.attributes: if attribute not in self.sale.web_shop.attributes: continue options.append({ 'option_id': attribute.vsf_identifier.id, 'option_value': self.attributes.get(attribute.name), }) return item