def _get_carrier_context(self): Uom = Pool().get('product.uom') context = super(ShipmentOut, self)._get_carrier_context() if not self.carrier: return context if self.carrier.carrier_cost_method != 'weight': return context context = context.copy() weights = [] context['weights'] = weights lines = self.inventory_moves or [] keyfunc = partial(self._group_parcel_key, lines) lines = sorted(lines, key=keyfunc) for key, parcel in groupby(lines, key=keyfunc): weight = 0 for line in parcel: if (line.product and line.quantity and line.uom and line.product.weight): quantity = Uom.compute_qty(line.uom, line.quantity, line.product.default_uom, round=False) weight += Uom.compute_qty(line.product.weight_uom, line.product.weight * quantity, self.carrier.weight_uom, round=False) weights.append(weight) return context
def _get_carrier_context(self): Uom = Pool().get('product.uom') context = super(Sale, self)._get_carrier_context() if self.carrier.carrier_cost_method != 'weight': return context context = context.copy() weights = [] context['weights'] = weights lines = self.lines or [] keyfunc = partial(self._group_parcel_key, lines) lines = sorted(lines, key=keyfunc) for key, parcel in groupby(lines, key=keyfunc): weight = 0 for line in parcel: if (getattr(line, 'product', None) and getattr(line, 'quantity', None) and getattr(line, 'unit', None)): quantity = Uom.compute_qty(line.unit, line.quantity, line.product.default_uom, round=False) if line.product.weight: weight += Uom.compute_qty(line.product.weight_uom, line.product.weight * quantity, self.carrier.weight_uom, round=False) weights.append(weight) return context
def compute(self, party, product, unit_price, quantity, uom, pattern=None): ''' Compute price based on price list of party :param unit_price: a Decimal for the default unit price in the company's currency and default uom of the product :param quantity: the quantity of product :param uom: a instance of the product.uom :param pattern: a dictionary with price list field as key and match value as value :return: the computed unit price ''' Uom = Pool().get('product.uom') if pattern is None: pattern = {} pattern = pattern.copy() pattern['product'] = product and product.id or None pattern['quantity'] = Uom.compute_qty(uom, quantity, product.default_uom, round=False) for line in self.lines: if line.match(pattern): with Transaction().set_context( self._get_context_price_list_line(party, product, unit_price, quantity, uom)): return line.get_unit_price() return unit_price
def on_change_supplier_invoice_line(self): pool = Pool() Currency = pool.get('currency.currency') Unit = Pool().get('product.uom') if not self.supplier_invoice_line: self.quantity = None self.value = None self.start_date = self.default_start_date() return invoice_line = self.supplier_invoice_line invoice = invoice_line.invoice if invoice.company.currency != invoice.currency: with Transaction().set_context(date=invoice.currency_date): self.value = Currency.compute( invoice.currency, invoice_line.amount, invoice.company.currency) else: self.value = invoice_line.amount if invoice.invoice_date: self.purchase_date = invoice.invoice_date self.start_date = invoice.invoice_date if invoice_line.product.depreciation_duration: duration = relativedelta.relativedelta( months=int(invoice_line.product.depreciation_duration), days=-1) self.end_date = self.start_date + duration if not self.unit: self.quantity = invoice_line.quantity else: self.quantity = Unit.compute_qty(invoice_line.unit, invoice_line.quantity, self.unit)
def get_shipping_rate(self, carrier, carrier_service=None, silent=False): """ Call the rates service and get possible quotes for shipment for eligible mail classes """ Currency = Pool().get('currency.currency') UOM = Pool().get('product.uom') ModelData = Pool().get('ir.model.data') if carrier.carrier_cost_method != "endicia": return super(Sale, self).get_shipping_rate( carrier, carrier_service, silent ) from_address = self._get_ship_from_address() if self.shipment_address.country.code == "US": mailclass_type = "Domestic" else: mailclass_type = "International" uom_oz = UOM.search([('symbol', '=', 'oz')])[0] # Endicia only support 1 decimal place in weight weight_oz = "%.1f" % UOM.compute_qty( self.weight_uom, self.weight, uom_oz ) to_zip = self.shipment_address.zip if mailclass_type == 'Domestic': to_zip = to_zip and to_zip[:5] else: # International to_zip = to_zip and to_zip[:15] postage_rates_request = PostageRatesAPI( mailclass=mailclass_type, weightoz=weight_oz, from_postal_code=from_address.zip[:5], to_postal_code=to_zip, to_country_code=self.shipment_address.country.code, accountid=carrier.endicia_account_id, requesterid=carrier.endicia_requester_id, passphrase=carrier.endicia_passphrase, test=carrier.endicia_is_test, ) # Logging. logger.debug( 'Making Postage Rates Request for shipping rates of' 'Sale ID: {0} and Carrier ID: {1}' .format(self.id, carrier.id) ) logger.debug('--------POSTAGE RATES REQUEST--------') logger.debug(str(postage_rates_request.to_xml())) logger.debug('--------END REQUEST--------') try: response_xml = postage_rates_request.send_request() response = objectify_response(response_xml) except RequestError, e: self.raise_user_error(unicode(e))
def get_weight(self, weight_uom, silent=False): """ Returns weight as required for carriers :param weight_uom: Weight uom used by carriers :param silent: Raise error if not silent """ ProductUom = Pool().get('product.uom') if not self.product or self.quantity <= 0 or \ self.product.type == 'service': return 0 if not self.product.weight: if silent: return 0 self.raise_user_error( 'weight_required', error_args=(self.product.name,) ) # Find the quantity in the default uom of the product as the weight # is for per unit in that uom if self.unit != self.product.default_uom: quantity = ProductUom.compute_qty( self.unit, self.quantity, self.product.default_uom ) else: quantity = self.quantity weight = self.product.weight * quantity # Compare product weight uom with the weight uom used by carrier # and calculate weight if botth are not same if self.product.weight_uom.symbol != weight_uom.symbol: weight = ProductUom.compute_qty( self.product.weight_uom, weight, weight_uom, ) return weight
def _get_rate_request_xml(self, carrier, carrier_service): SaleConfiguration = Pool().get("sale.configuration") Uom = Pool().get("product.uom") config = SaleConfiguration(1) code = length = width = height = dimensions_symbol = None box_type = config.ups_box_type if box_type: code = box_type.code length = box_type.length height = box_type.height width = box_type.width dimensions_symbol = box_type.distance_unit and box_type.distance_unit.symbol.upper() package_type = RatingService.packaging_type(Code=code) package_weight = RatingService.package_weight_type( Weight="%.2f" % Uom.compute_qty(self.weight_uom, self.weight, carrier.ups_weight_uom), Code=carrier.ups_weight_uom_code, ) package_service_options = RatingService.package_service_options_type( RatingService.insured_value_type(MonetaryValue="0") ) args = [package_type, package_weight, package_service_options] # Only send dimensions if box type has all information if length and width and height and dimensions_symbol: package_dimensions = RatingService.dimensions_type( Code=dimensions_symbol, Length=str(length), Width=str(width), Height=str(height) ) args.append(package_dimensions) shipment_args = [RatingService.package_type(*args)] from_address = self._get_ship_from_address() shipment_args.extend( [ from_address.to_ups_shipper(carrier=carrier), # Shipper self.shipment_address.to_ups_to_address(), # Ship to from_address.to_ups_from_address(), # Ship from ] ) if carrier.ups_negotiated_rates: shipment_args.append(RatingService.rate_information_type(negotiated=True)) if carrier_service: # TODO: handle ups_saturday_delivery shipment_args.append(RatingService.service_type(Code=carrier_service.code)) request_option = E.RequestOption("Rate") else: request_option = E.RequestOption("Shop") return RatingService.rating_request_type(E.Shipment(*shipment_args), RequestOption=request_option)
def compute_factor(self, product, quantity, uom): """ Compute factor for an output product """ Uom = Pool().get("product.uom") for output in self.outputs: if output.product == product: if not output.quantity: return 0.0 quantity = Uom.compute_qty(uom, quantity, output.uom, round=False) return quantity / output.quantity
def on_change_asset(self): Uom = Pool().get('product.uom') if self.asset: quantity = self.asset.quantity if self.unit: quantity = Uom.compute_qty(self.asset.unit, quantity, self.unit) self.quantity = quantity else: self.quantity = quantity self.unit = self.unit
def compute_factor(self, product, quantity, uom): ''' Compute factor for an output product ''' Uom = Pool().get('product.uom') for output in self.outputs: if output.product == product: if not output.quantity: return 0.0 quantity = Uom.compute_qty(uom, quantity, output.uom, round=False) return quantity / output.quantity
def get_weight(self, name): """ Returns package weight if weight is not overriden otherwise returns overriden weight """ UOM = Pool().get('product.uom') if self.override_weight: return UOM.compute_qty( self.override_weight_uom, self.override_weight, self.weight_uom ) return self.get_computed_weight()
def get_weight(self, weight_uom, silent=False): """ Returns weight as required for carriers :param weight_uom: Weight uom used by carriers :param silent: Raise error if not silent """ ProductUom = Pool().get('product.uom') if not self.product or self.quantity <= 0 or \ self.product.type == 'service': return 0 if not self.product.weight: if silent: return 0 self.raise_user_error('weight_required', error_args=(self.product.name, )) # Find the quantity in the default uom of the product as the weight # is for per unit in that uom if self.unit != self.product.default_uom: quantity = ProductUom.compute_qty(self.unit, self.quantity, self.product.default_uom) else: quantity = self.quantity weight = self.product.weight * quantity # Compare product weight uom with the weight uom used by carrier # and calculate weight if botth are not same if self.product.weight_uom != weight_uom: weight = ProductUom.compute_qty( self.product.weight_uom, weight, weight_uom, ) return weight
def get_total_volume(self, _ids): """Compute the total volume sum""" if self.total_volume_uom is None: return 0.0 total = 0.0 for trunks in self.trunks: if trunks is None or trunks.total_cubing is None: continue total += trunks.get_total_cubing(None) Uom = Pool().get('product.uom') m3_id = Id('product', 'uom_cubic_meter').pyson() m3 = Uom(m3_id) return Uom.compute_qty(m3, total, self.total_volume_uom)
def get_weight_for_endicia(self): """ Returns weight as required for endicia. Upward rounded integral values in Oz """ ProductUom = Pool().get('product.uom') if self.product.type == 'service': return 0 if not self.product.weight: self.raise_user_error( 'weight_required', error_args=(self.product.name,) ) # Find the quantity in the default uom of the product as the weight # is for per unit in that uom if self.uom != self.product.default_uom: quantity = ProductUom.compute_qty( self.uom, self.quantity, self.product.default_uom ) else: quantity = self.quantity weight = float(self.product.weight) * quantity # Endicia by default uses oz for weight purposes if self.product.weight_uom.symbol != 'oz': ounce, = ProductUom.search([('symbol', '=', 'oz')]) weight = ProductUom.compute_qty( self.product.weight_uom, weight, ounce ) return math.ceil(weight)
def get_mean_volume(self, ids): """Return the mean volume in the selected unit""" if self.total_trunks_count == 0 or self.mean_volume_uom is None: return 0.0 total = 0.0 for trunks in self.trunks: if trunks is None or trunks.total_cubing is None: continue total += trunks.get_total_cubing(None) Uom = Pool().get('product.uom') m3_id = Id('product', 'uom_cubic_meter').pyson() m3 = Uom(m3_id) return Uom.compute_qty(m3, total / float(self.total_trunks_count), self.mean_volume_uom)
def get_monetary_value_for_ups(self): """ Returns monetary_value as required for ups """ ProductUom = Pool().get("product.uom") # Find the quantity in the default uom of the product as the weight # is for per unit in that uom if self.uom != self.product.default_uom: quantity = ProductUom.compute_qty(self.uom, self.quantity, self.product.default_uom) else: quantity = self.quantity return Decimal(self.product.list_price) * Decimal(quantity)
def _fill_line_from_kit_line(self, kit_line, line): ProductUom = Pool().get('product.uom') self.product = kit_line.product if kit_line.unit.category.id != line.unit.category.id: quantity = kit_line.quantity * line.quantity else: quantity = ProductUom.compute_qty( kit_line.unit, kit_line.quantity, line.unit ) * line.quantity self.quantity = quantity self.unit = kit_line.unit self.type = 'line' self.kit_parent_line = line
def _update_fifo_out_product_cost_price(self): """ Update the product cost price of the given product on the move. Update fifo_quantity on the concerned incomming moves. Return the cost price for outputing the given product and quantity. """ Uom = Pool().get("product.uom") total_qty = Uom.compute_qty(self.uom, self.quantity, self.product.default_uom, round=False) fifo_moves = self.product.template.get_fifo_move(total_qty) cost_price = Decimal("0.0") consumed_qty = 0.0 for move, move_qty in fifo_moves: consumed_qty += move_qty move_unit_price = Uom.compute_price(move.uom, move.unit_price, move.product.default_uom) cost_price += move_unit_price * Decimal(str(move_qty)) move.quantity = move_qty move._update_product_cost_price("out") move_qty = Uom.compute_qty(self.product.default_uom, move_qty, move.uom, round=False) # Use write as move instance quantity was modified to call # _update_product_cost_price self.write([self.__class__(move.id)], {"fifo_quantity": (move.fifo_quantity or 0.0) + move_qty}) if Decimal(str(consumed_qty)) != Decimal("0"): cost_price = cost_price / Decimal(str(consumed_qty)) if cost_price != Decimal("0"): digits = self.__class__.cost_price.digits return cost_price.quantize(Decimal(str(10.0 ** -digits[1]))) else: return self.product.cost_price
def get_ups_package_container_rate(self): """ Return UPS package container for a single package """ Uom = Pool().get('product.uom') shipment = self.shipment carrier = shipment.carrier if self.box_type: code = self.box_type.code length = self.box_type.length height = self.box_type.height width = self.box_type.width dimensions_symbol = self.box_type.distance_unit and \ self.box_type.distance_unit.symbol.upper() else: code = '02' length = self.length height = self.height width = self.width dimensions_symbol = self.distance_unit and \ self.distance_unit.symbol.upper() package_type = RatingService.packaging_type(Code=code) package_weight = RatingService.package_weight_type( Weight="%.2f" % Uom.compute_qty( self.weight_uom, self.weight, carrier.ups_weight_uom ), Code=carrier.ups_weight_uom_code, ) package_service_options = RatingService.package_service_options_type( RatingService.insured_value_type(MonetaryValue='0') ) args = [package_type, package_weight, package_service_options] # Only send dimensions if the box type is 'Customer Supplied Package' if code == '02' and length and width and height and dimensions_symbol: package_dimensions = RatingService.dimensions_type( Code=dimensions_symbol, Length=str(length), Width=str(width), Height=str(height) ) args.append(package_dimensions) package_container = RatingService.package_type(*args) return package_container
def create(cls, vlist): Uom = Pool().get('product.uom') for v in vlist: default_uom = cls.get_count_uom(v['product'], default_uom=True) if 'quantity' not in v: v['quantity'] = 0.0 if 'quantity_1' not in v: uom_1 = cls.get_count_uom(v['product']) if default_uom.id == uom_1.id: v['quantity_1'] = v['quantity'] else: v['quantity_1'] = Uom.compute_qty(default_uom, v['quantity'], uom_1) v['uom_1'] = uom_1.id return super(InventoryLine, cls).create(vlist)
def compute_quantites(sql_where): Uom = Pool().get('product.uom') query = move.select(move.id.as_('move_id'), where=sql_where, order_by=move.effective_date.desc) cursor.execute(*query) move_ids = [m[0] for m in cursor.fetchall()] moves = Move.browse(move_ids) total = sum([ Uom.compute_qty(m.uom, m.quantity, m.product.default_uom, True) for m in moves ]) moves = [DualRecord(m) for m in moves] return total, moves
def get_monetary_value_for_ups(self): """ Returns monetary_value as required for ups """ ProductUom = Pool().get('product.uom') # Find the quantity in the default uom of the product as the weight # is for per unit in that uom if self.uom != self.product.default_uom: quantity = ProductUom.compute_qty(self.uom, self.quantity, self.product.default_uom) else: quantity = self.quantity return Decimal(self.product.list_price) * Decimal(quantity)
def get_total_cubing(self, _name): """Compute the total cubing nd convert it to the select UOM""" total = 0.0 if self.trunks_count is None: return 0.0 for trunk in self.trunks_count: diameter = (trunk.diam_class.diameter_min + trunk.diam_class.diameter_max) / 2.0 total += self.get_volume(trunk.height_m, diameter) * trunk.count # Unit conversion Uom = Pool().get('product.uom') m3_id = Id('product', 'uom_cubic_meter').pyson() m3 = Uom(m3_id) # call compute_qty to round the number return Uom.compute_qty(m3, total, m3)
def get_weight(self, name=None): """ Returns sum of weight associated with each package or move line otherwise """ Uom = Pool().get('product.uom') if self.packages: return sum( map( lambda p: Uom.compute_qty(p.weight_uom, p.weight, self. weight_uom), self.packages)) return sum( map(lambda move: move.get_weight(self.weight_uom, silent=True), self.carrier_cost_moves))
def compute_factor(self, product, quantity, uom): ''' Compute factor for an output product ''' Uom = Pool().get('product.uom') output_quantity = 0 for output in self.outputs: if output.product == product: output_quantity += Uom.compute_qty(output.uom, output.quantity, uom, round=False) if output_quantity: return quantity / output_quantity else: return 0
def on_change_with_cost(self): Uom = Pool().get('product.uom') cost = Decimal(0) if not self.inputs: return cost for input_ in self.inputs: if (input_.product is None or input_.uom is None or input_.quantity is None): continue product = input_.product quantity = Uom.compute_qty(input_.uom, input_.quantity, product.default_uom) cost += Decimal(str(quantity)) * product.cost_price return cost
def on_change_asset(self): Uom = Pool().get('product.uom') if self.asset: quantity = self.asset.quantity if self.unit: quantity = Uom.compute_qty(self.asset.unit, quantity, self.unit) return { 'quantity': quantity, } else: return { 'quantity': quantity, 'unit': self.unit.id, 'unit.rec_name': self.unit.rec_name, } return {}
def get_weight(self, name=None): """ Returns sum of weight associated with each package or move line otherwise """ Uom = Pool().get('product.uom') if self.packages: return sum(map( lambda p: Uom.compute_qty( p.weight_uom, p.weight, self.weight_uom ), self.packages )) return sum(map( lambda move: move.get_weight(self.weight_uom, silent=True), self.carrier_cost_moves ))
def on_change_with_minimum_quantity(self, name=None): Uom = Pool().get('product.uom') if not self.product or not self.purchase.party: return product_suppliers = list( self.product.product_suppliers_used( **{'party': self.purchase.party.id})) if not product_suppliers: return product_supplier = product_suppliers[0] minimum_quantity = product_supplier.minimum_quantity uom_category = self.product.purchase_uom.category if (minimum_quantity and self.unit and self.unit in uom_category.uoms): return Uom.compute_qty(self.product.purchase_uom, minimum_quantity, self.unit) return minimum_quantity
def compute(self, party, product, unit_price, quantity, uom, pattern=None): 'Compute price based on price list of party' Uom = Pool().get('product.uom') if pattern is None: pattern = {} pattern = pattern.copy() pattern['product'] = product and product.id or None pattern['quantity'] = Uom.compute_qty(uom, quantity, product.default_uom, round=False) if product else quantity context = self.get_context_formula( party, product, unit_price, quantity, uom) for line in self.lines: if line.match(pattern): return line.get_unit_price(**context) return unit_price
def compute(self, party, product, unit_price, quantity, uom, pattern=None): 'Compute price based on price list of party' Uom = Pool().get('product.uom') if pattern is None: pattern = {} pattern = pattern.copy() pattern['product'] = product and product.id or None pattern['quantity'] = Uom.compute_qty( uom, quantity, product.default_uom, round=False) if product else quantity context = self.get_context_formula(party, product, unit_price, quantity, uom) for line in self.lines: if line.match(pattern): return line.get_unit_price(**context) return unit_price
def compute(self, product, unit_price, quantity, uom, pattern=None): 'Compute price based on price list of party' def get_unit_price(**context): 'Return unit price (as Decimal)' context.setdefault('functions', {})['Decimal'] = Decimal return simple_eval(decistmt(self.formula), **context) Uom = Pool().get('product.uom') if pattern is None: pattern = {} pattern = pattern.copy() pattern['product'] = product and product.id or None pattern['quantity'] = Uom.compute_qty(uom, quantity, product.default_uom, round=False) if product else quantity context = self.get_context_formula( product, unit_price, quantity, uom) return get_unit_price(**context)
def get_fedex_items_details(self, fedex_request): ''' Computes the details of the shipment items and passes to fedex request ''' ProductUom = Pool().get('product.uom') item = fedex_request.get_element_from_type( 'RequestedPackageLineItem' ) weight_uom, = ProductUom.search([('symbol', '=', 'lb')]) item.SequenceNumber = 1 item.Weight.Units = 'LB' item.Weight.Value = ProductUom.compute_qty( self.weight_uom, self.package_weight, weight_uom ) # From sale you cannot define packages per shipment, so single # package per shipment. item.GroupPackageCount = 1 fedex_request.RequestedShipment.PackageCount = 1 fedex_request.RequestedShipment.RequestedPackageLineItems = [item]
def on_change_supplier_invoice_line(self): pool = Pool() Currency = pool.get('currency.currency') Unit = Pool().get('product.uom') new_values = {} if not self.supplier_invoice_line: new_values['quantity'] = None new_values['value'] = None new_values['start_date'] = self.default_start_date() return new_values invoice_line = self.supplier_invoice_line invoice = invoice_line.invoice if invoice.company.currency != invoice.currency: with Transaction().set_context(date=invoice.currency_date): new_values['value'] = Currency.compute( invoice.currency, invoice_line.amount, invoice.company.currency) else: new_values['value'] = invoice_line.amount if invoice.invoice_date: new_values['purchase_date'] = invoice.invoice_date new_values['start_date'] = invoice.invoice_date if invoice_line.product.depreciation_duration: duration = relativedelta.relativedelta( months=int(invoice_line.product.depreciation_duration), days=-1) new_values['end_date'] = new_values['start_date'] + duration if not self.unit: new_values['quantity'] = invoice_line.quantity return new_values new_values['quantity'] = Unit.compute_qty(invoice_line.unit, invoice_line.quantity, self.unit) return new_values
def compute(self, party, product, unit_price, quantity, uom, pattern=None): 'Compute price based on price list of party' Uom = Pool().get('product.uom') def parents(categories): for category in categories: while category: yield category category = category.parent if pattern is None: pattern = {} pattern = pattern.copy() if product: pattern['categories'] = [ c.id for c in parents(product.categories_all) ] pattern['product'] = product.id pattern['quantity'] = Uom.compute_qty( uom, quantity, self.get_uom(product), round=False) if product else quantity context = self.get_context_formula(party, product, unit_price, quantity, uom, pattern=pattern) for line in self.lines: if line.match(pattern): unit_price = line.get_unit_price(**context) if isinstance(unit_price, Null): unit_price = None break return unit_price
def make_fedex_labels(self): """ Make labels for the given shipment :return: Tracking number as string """ Currency = Pool().get('currency.currency') Attachment = Pool().get('ir.attachment') Package = Pool().get('stock.package') Uom = Pool().get('product.uom') if self.state not in ('packed', 'done'): self.raise_user_error('invalid_state') if not self.carrier.carrier_cost_method == 'fedex': self.raise_user_error('wrong_carrier') if self.tracking_number: self.raise_user_error('tracking_number_already_present') fedex_credentials = self.carrier.get_fedex_credentials() ship_request = ProcessShipmentRequest(fedex_credentials) requested_shipment = ship_request.RequestedShipment requested_shipment.DropoffType = self.fedex_drop_off_type.value requested_shipment.ServiceType = self.fedex_service_type.value requested_shipment.PackagingType = self.fedex_packaging_type.value uom_pound, = Uom.search([('symbol', '=', 'lb')]) if len(self.packages) > 1: requested_shipment.TotalWeight.Units = 'LB' requested_shipment.TotalWeight.Value = Uom.compute_qty( self.weight_uom, self.weight, uom_pound ) # Shipper & Recipient requested_shipment.Shipper.AccountNumber = \ fedex_credentials.AccountNumber if not self.warehouse.address: self.raise_user_error('warehouse_address_required') self.warehouse.address.set_fedex_address(requested_shipment.Shipper) self.delivery_address.set_fedex_address(requested_shipment.Recipient) # Shipping Charges Payment shipping_charges = requested_shipment.ShippingChargesPayment shipping_charges.PaymentType = 'SENDER' shipping_charges.Payor.ResponsibleParty = requested_shipment.Shipper # Express Freight Detail fright_detail = requested_shipment.ExpressFreightDetail fright_detail.PackingListEnclosed = 1 # XXX fright_detail.ShippersLoadAndCount = 2 # XXX fright_detail.BookingConfirmationNumber = 'Ref-%s' % self.reference if self.is_international_shipping: # Customs Clearance Detail self.get_fedex_customs_details(ship_request) # Label Specification # Maybe make them as configurable items in later versions requested_shipment.LabelSpecification.LabelFormatType = 'COMMON2D' requested_shipment.LabelSpecification.ImageType = 'PNG' requested_shipment.LabelSpecification.LabelStockType = 'PAPER_4X6' requested_shipment.RateRequestTypes = ['ACCOUNT'] master_tracking_number = None for index, package in enumerate(self.packages, start=1): item = ship_request.get_element_from_type( 'RequestedPackageLineItem' ) item.SequenceNumber = index # TODO: some country needs item.ItemDescription item.Weight.Units = 'LB' item.Weight.Value = Uom.compute_qty( package.weight_uom, package.weight, uom_pound ) ship_request.RequestedShipment.RequestedPackageLineItems = [item] ship_request.RequestedShipment.PackageCount = len(self.packages) if master_tracking_number is not None: tracking_id = ship_request.get_element_from_type( 'TrackingId' ) tracking_id.TrackingNumber = master_tracking_number ship_request.RequestedShipment.MasterTrackingId = tracking_id try: response = ship_request.send_request(str(self.id)) except RequestError, error: self.raise_user_error('error_label', error_args=(error,)) package_details = response.CompletedShipmentDetail.CompletedPackageDetails # noqa tracking_number = package_details[0].TrackingIds[0].TrackingNumber if self.packages.index(package) == 0: master_tracking_number = tracking_number Package.write([package], { 'tracking_number': tracking_number, }) for id, image in enumerate(package_details[0].Label.Parts): Attachment.create([{ 'name': "%s_%s_Fedex.png" % (tracking_number, id), 'type': 'data', 'data': buffer(base64.decodestring(image.Image)), 'resource': '%s,%s' % (self.__name__, self.id) }])
def get_height_m(self, _name): """Convert the height to meters""" Uom = Pool().get('product.uom') m_id = Id('product', 'uom_meter').pyson() m = Uom(m_id) return Uom.compute_qty(self.height_uom, self.height, m)
def _get_internal_quantity(quantity, uom, product): Uom = Pool().get('product.uom') internal_quantity = Uom.compute_qty(uom, quantity, product.default_uom, round=True) return internal_quantity
def make_fedex_labels(self): """ Make labels for the given shipment :return: Tracking number as string """ Currency = Pool().get('currency.currency') Attachment = Pool().get('ir.attachment') Package = Pool().get('stock.package') Uom = Pool().get('product.uom') if self.state not in ('packed', 'done'): self.raise_user_error('invalid_state') if not self.carrier.carrier_cost_method == 'fedex': self.raise_user_error('wrong_carrier') if self.tracking_number: self.raise_user_error('tracking_number_already_present') fedex_credentials = self.carrier.get_fedex_credentials() ship_request = ProcessShipmentRequest(fedex_credentials) requested_shipment = ship_request.RequestedShipment requested_shipment.DropoffType = self.fedex_drop_off_type.value requested_shipment.ServiceType = self.fedex_service_type.value requested_shipment.PackagingType = self.fedex_packaging_type.value uom_pound, = Uom.search([('symbol', '=', 'lb')]) if len(self.packages) > 1: requested_shipment.TotalWeight.Units = 'LB' requested_shipment.TotalWeight.Value = Uom.compute_qty( self.weight_uom, self.weight, uom_pound ) # Shipper & Recipient requested_shipment.Shipper.AccountNumber = \ fedex_credentials.AccountNumber from_address = self._get_ship_from_address() from_address.set_fedex_address(requested_shipment.Shipper) self.delivery_address.set_fedex_address(requested_shipment.Recipient) # Shipping Charges Payment shipping_charges = requested_shipment.ShippingChargesPayment shipping_charges.PaymentType = 'SENDER' shipping_charges.Payor.ResponsibleParty = requested_shipment.Shipper # Express Freight Detail fright_detail = requested_shipment.ExpressFreightDetail fright_detail.PackingListEnclosed = 1 # XXX fright_detail.ShippersLoadAndCount = 2 # XXX fright_detail.BookingConfirmationNumber = 'Ref-%s' % self.reference if self.is_international_shipping: # Customs Clearance Detail self.get_fedex_customs_details(ship_request) # Label Specification # Maybe make them as configurable items in later versions requested_shipment.LabelSpecification.LabelFormatType = 'COMMON2D' requested_shipment.LabelSpecification.ImageType = 'PNG' requested_shipment.LabelSpecification.LabelStockType = 'PAPER_4X6' requested_shipment.RateRequestTypes = ['ACCOUNT'] master_tracking_number = None for index, package in enumerate(self.packages, start=1): item = ship_request.get_element_from_type( 'RequestedPackageLineItem' ) item.SequenceNumber = index # TODO: some country needs item.ItemDescription item.Weight.Units = 'LB' item.Weight.Value = Uom.compute_qty( package.weight_uom, package.weight, uom_pound ) ship_request.RequestedShipment.RequestedPackageLineItems = [item] ship_request.RequestedShipment.PackageCount = len(self.packages) if master_tracking_number is not None: tracking_id = ship_request.get_element_from_type( 'TrackingId' ) tracking_id.TrackingNumber = master_tracking_number ship_request.RequestedShipment.MasterTrackingId = tracking_id try: response = ship_request.send_request(str(self.id)) except RequestError, error: self.raise_user_error('error_label', error_args=(error,)) package_details = response.CompletedShipmentDetail.CompletedPackageDetails # noqa tracking_number = package_details[0].TrackingIds[0].TrackingNumber if self.packages.index(package) == 0: master_tracking_number = tracking_number Package.write([package], { 'tracking_number': tracking_number, }) for id, image in enumerate(package_details[0].Label.Parts): Attachment.create([{ 'name': "%s_%s_Fedex.png" % (tracking_number, id), 'type': 'data', 'data': buffer(base64.decodestring(image.Image)), 'resource': '%s,%s' % (self.__name__, self.id) }])
def mass_balance_report_data(self, requested_product, direction, lot=None): Uom = Pool().get('product.uom') digits = self.uom and self.uom.digits or 2 quantity = 0.0 for move in getattr( self, 'outputs' if direction == 'backward' else 'inputs'): if move.state == 'cancelled': continue if move.product == requested_product: # skip moves that same product but different lot if lot and lot != move.lot: continue quantity += Uom.compute_qty(move.uom, move.quantity, move.product.default_uom, False) moves = {} for move in getattr( self, 'inputs' if direction == 'backward' else 'outputs'): if move.state == 'cancelled': continue product = move.product mqty = Uom.compute_qty(move.uom, move.quantity, move.product.default_uom, False) if moves.get(product): moves[product] += mqty else: moves[product] = mqty res = {} for product, qty in moves.items(): item = res.setdefault(product, {}) item.setdefault('balance_quantity', 0.0) item.setdefault('balance_consumption', 0.0) item.setdefault('balance_plan_consumption', 0.0) item.setdefault('balance_difference', 0.0) item.setdefault('productions', []) if direction == 'backward': balance_quantity = quantity balance_consumption = qty balance_plan_consumption = balance_difference = balance_difference_percent = 0.0 if self.bom: bom = self.bom for bm in bom.inputs: if bm.product == product: default_uom = self.product.default_uom bqty = Uom.compute_qty(bm.uom, bm.quantity, bm.product.default_uom, False) factor = bom.compute_factor( self.product, bqty, default_uom) balance_plan_consumption = default_uom.ceil( self.quantity * factor) break balance_difference = round(qty - balance_plan_consumption, digits) if balance_consumption and balance_plan_consumption: balance_difference_percent = ( (balance_consumption - balance_plan_consumption) / balance_plan_consumption) * 100 item['balance_quantity'] = balance_quantity item['balance_consumption'] += balance_consumption item['balance_plan_consumption'] += balance_plan_consumption item['balance_difference'] += balance_difference item['balance_quantity_uom'] = requested_product.default_uom item['balance_consumption_uom'] = product.default_uom item['balance_plan_consumption_uom'] = product.default_uom item['balance_difference_uom'] = product.default_uom else: balance_quantity = qty balance_consumption = quantity balance_plan_consumption = balance_difference = balance_difference_percent = 0.0 if self.bom: bom = self.bom for bm in bom.inputs: if bm.product == requested_product: default_uom = self.product.default_uom bqty = Uom.compute_qty(bm.uom, bm.quantity, bm.product.default_uom, False) factor = bom.compute_factor( self.product, bqty, default_uom) balance_plan_consumption = default_uom.ceil( self.quantity * factor) break balance_difference = round( quantity - balance_plan_consumption, digits) if balance_consumption and balance_plan_consumption: balance_difference_percent = ( (balance_consumption - balance_plan_consumption) / balance_plan_consumption) * 100 item['balance_quantity'] = balance_quantity item['balance_consumption'] += balance_consumption item['balance_plan_consumption'] += balance_plan_consumption item['balance_difference'] += balance_difference item['balance_quantity_uom'] = product.default_uom item['balance_consumption_uom'] = requested_product.default_uom item[ 'balance_plan_consumption_uom'] = requested_product.default_uom item['balance_difference_uom'] = requested_product.default_uom vals = { 'id': self.id, 'name': self.rec_name, 'product': self.product, 'uom': self.uom, 'default_uom': product.default_uom, 'balance_quantity': balance_quantity, 'balance_consumption': balance_consumption, 'balance_plan_consumption': balance_plan_consumption, 'balance_difference': balance_difference, 'balance_difference_percent': balance_difference_percent, 'balance_quantity_uom': item['balance_quantity_uom'], 'balance_consumption_uom': item['balance_consumption_uom'], 'balance_plan_consumption_uom': item['balance_plan_consumption_uom'], 'balance_difference_uom': item['balance_difference_uom'], } item['productions'].append(vals) return res
def generate_shipping_labels(self, **kwargs): """ Make labels for the given shipment :return: Tracking number as string """ Attachment = Pool().get('ir.attachment') Tracking = Pool().get('shipment.tracking') Uom = Pool().get('product.uom') if self.carrier_cost_method != 'endicia': return super(ShipmentOut, self).generate_shipping_labels(**kwargs) label_request = LabelRequest( Test=self.carrier.endicia_is_test and 'YES' or 'NO', LabelType=( 'International' in self.carrier_service.code ) and 'International' or 'Default', # TODO: Probably the following have to be configurable ImageFormat="PNG", LabelSize="6x4", ImageResolution="203", ImageRotation="Rotate270", ) try: package, = self.packages except ValueError: self.raise_user_error( "There should be exactly one package to generate USPS label" "\n Multi Piece shipment is not supported yet" ) oz, = Uom.search([('symbol', '=', 'oz')]) # Endicia only support 1 decimal place in weight weight_oz = "%.1f" % Uom.compute_qty( package.weight_uom, package.weight, oz ) shipping_label_request = ShippingLabelAPI( label_request=label_request, weight_oz=weight_oz, partner_customer_id=self.delivery_address.id, partner_transaction_id=self.id, mail_class=self.carrier_service.code, accountid=self.carrier.endicia_account_id, requesterid=self.carrier.endicia_requester_id, passphrase=self.carrier.endicia_passphrase, test=self.carrier.endicia_is_test, ) shipping_label_request.mailpieceshape = package.box_type and \ package.box_type.code # Dimensions required for priority mail class and # all values must be in inches to_uom, = Uom.search([('symbol', '=', 'in')]) from_uom, = Uom.search([('symbol', '=', package.distance_unit.symbol)]) if (package.length and package.width and package.height): length, width, height = package.length, package.width, package.height if from_uom != to_uom: length = "%.1f" % Uom.compute_qty( from_uom, package.length, to_uom ) width = "%.1f" % Uom.compute_qty( from_uom, package.width, to_uom ) height = "%.1f" % Uom.compute_qty( from_uom, package.height, to_uom ) shipping_label_request.mailpiecedimensions = { 'Length': length, 'Width': width, 'Height': height } from_address = self._get_ship_from_address() shipping_label_request.add_data( from_address.address_to_endicia_from_address().data ) shipping_label_request.add_data( self.delivery_address.address_to_endicia_to_address().data ) shipping_label_request.add_data({ 'LabelSubtype': self.endicia_label_subtype, 'IncludePostage': self.endicia_include_postage and 'TRUE' or 'FALSE', }) if self.endicia_label_subtype != 'None': # Integrated form type needs to be sent for international shipments shipping_label_request.add_data({ 'IntegratedFormType': self.endicia_integrated_form_type, }) if self.delivery_address.country.code != 'US': self._update_endicia_item_details(shipping_label_request) # Logging. logger.debug( 'Making Shipping Label Request for' 'Shipment ID: {0} and Carrier ID: {1}' .format(self.id, self.carrier.id) ) logger.debug('--------SHIPPING LABEL REQUEST--------') logger.debug(str(shipping_label_request.to_xml())) logger.debug('--------END REQUEST--------') try: response = shipping_label_request.send_request() except RequestError, error: self.raise_user_error('error_label', error_args=(error.message,))