Merge branch 'imp/11.0/delivery_hibou__sig_req' into '11.0'

WIP: imp/11.0/delivery_hibou__sig_req into 11.0

See merge request hibou-io/hibou-odoo/suite!1163
This commit is contained in:
Jared Kipe
2022-02-15 20:31:02 +00:00
77 changed files with 10585 additions and 4045 deletions

View File

@@ -1 +1,3 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
from . import models from . import models

View File

@@ -1,9 +1,9 @@
{ {
'name': 'Hibou Fedex Shipping', 'name': 'Hibou Fedex Shipping',
'version': '11.0.1.0.0', 'version': '11.0.1.1.0',
'category': 'Stock', 'category': 'Stock',
'author': "Hibou Corp.", 'author': "Hibou Corp.",
'license': 'AGPL-3', 'license': 'OPL-1',
'website': 'https://hibou.io/', 'website': 'https://hibou.io/',
'depends': [ 'depends': [
'delivery_fedex', 'delivery_fedex',

View File

@@ -1,2 +1,4 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
from . import delivery_fedex from . import delivery_fedex
from . import stock from . import stock

View File

@@ -1,3 +1,5 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
import logging import logging
import time import time
from odoo import fields, models, tools, _ from odoo import fields, models, tools, _
@@ -17,6 +19,10 @@ class DeliveryFedex(models.Model):
('FEDEX_EXPRESS_SAVER', 'FEDEX_EXPRESS_SAVER'), ('FEDEX_EXPRESS_SAVER', 'FEDEX_EXPRESS_SAVER'),
]) ])
def _fedex_convert_weight(self, weight, unit):
# dummy converter
return weight
def _get_fedex_is_third_party(self, order=None, picking=None): def _get_fedex_is_third_party(self, order=None, picking=None):
third_party_account = self.get_third_party_account(order=order, picking=picking) third_party_account = self.get_third_party_account(order=order, picking=picking)
if third_party_account: if third_party_account:
@@ -36,6 +42,8 @@ class DeliveryFedex(models.Model):
if not third_party_account.delivery_type == 'fedex': if not third_party_account.delivery_type == 'fedex':
raise ValidationError('Non-FedEx Shipping Account indicated during FedEx shipment.') raise ValidationError('Non-FedEx Shipping Account indicated during FedEx shipment.')
return third_party_account.name return third_party_account.name
if picking and picking.picking_type_id.warehouse_id.fedex_account_number:
return picking.picking_type_id.warehouse_id.fedex_account_number
return self.fedex_account_number return self.fedex_account_number
def _get_fedex_account_number(self, order=None, picking=None): def _get_fedex_account_number(self, order=None, picking=None):
@@ -112,6 +120,8 @@ class DeliveryFedex(models.Model):
date_planned = None date_planned = None
if self.env.context.get('date_planned'): if self.env.context.get('date_planned'):
date_planned = self.env.context.get('date_planned') date_planned = self.env.context.get('date_planned')
if date_planned and isinstance(date_planned, str):
date_planned = fields.Datetime.from_string(date_planned)
# Authentication stuff # Authentication stuff
@@ -205,6 +215,15 @@ class DeliveryFedex(models.Model):
srm = FedexRequest(self.log_xml, request_type="shipping", prod_environment=self.prod_environment) srm = FedexRequest(self.log_xml, request_type="shipping", prod_environment=self.prod_environment)
superself = self.sudo() superself = self.sudo()
picking_packages = picking.package_ids
package_carriers = picking_packages.mapped('carrier_id')
if package_carriers:
# only ship ours
picking_packages = picking_packages.filtered(lambda p: p.carrier_id == self and not p.carrier_tracking_ref)
if package_carriers and not picking_packages:
continue
shipper_company = superself.get_shipper_company(picking=picking) shipper_company = superself.get_shipper_company(picking=picking)
shipper_warehouse = superself.get_shipper_warehouse(picking=picking) shipper_warehouse = superself.get_shipper_warehouse(picking=picking)
recipient = superself.get_recipient(picking=picking) recipient = superself.get_recipient(picking=picking)
@@ -213,7 +232,6 @@ class DeliveryFedex(models.Model):
payment_acc_number = superself._get_fedex_payment_account_number(picking=picking) payment_acc_number = superself._get_fedex_payment_account_number(picking=picking)
order_name = superself.get_order_name(picking=picking) order_name = superself.get_order_name(picking=picking)
attn = superself.get_attn(picking=picking) attn = superself.get_attn(picking=picking)
insurance_value = superself.get_insurance_value(picking=picking)
residential = self._get_fedex_recipient_is_residential(recipient) residential = self._get_fedex_recipient_is_residential(recipient)
srm.web_authentication_detail(superself.fedex_developer_key, superself.fedex_developer_password) srm.web_authentication_detail(superself.fedex_developer_key, superself.fedex_developer_password)
@@ -222,7 +240,7 @@ class DeliveryFedex(models.Model):
# Not the actual reference. Using `shipment_name` during `add_package` calls. # Not the actual reference. Using `shipment_name` during `add_package` calls.
srm.transaction_detail(picking.id) srm.transaction_detail(picking.id)
package_type = picking.package_ids and picking.package_ids[0].packaging_id.shipper_package_code or self.fedex_default_packaging_id.shipper_package_code package_type = picking_packages and picking_packages[0].packaging_id.shipper_package_code or self.fedex_default_packaging_id.shipper_package_code
srm.shipment_request(self.fedex_droppoff_type, self.fedex_service_type, package_type, self.fedex_weight_unit, self.fedex_saturday_delivery) srm.shipment_request(self.fedex_droppoff_type, self.fedex_service_type, package_type, self.fedex_weight_unit, self.fedex_saturday_delivery)
srm.set_currency(_convert_curr_iso_fdx(picking.company_id.currency_id.name)) srm.set_currency(_convert_curr_iso_fdx(picking.company_id.currency_id.name))
srm.set_shipper(shipper_company, shipper_warehouse) srm.set_shipper(shipper_company, shipper_warehouse)
@@ -258,7 +276,7 @@ class DeliveryFedex(models.Model):
srm.customs_value(_convert_curr_iso_fdx(commodity_currency.name), total_commodities_amount, "NON_DOCUMENTS") srm.customs_value(_convert_curr_iso_fdx(commodity_currency.name), total_commodities_amount, "NON_DOCUMENTS")
srm.duties_payment(picking.picking_type_id.warehouse_id.partner_id.country_id.code, superself.fedex_account_number) srm.duties_payment(picking.picking_type_id.warehouse_id.partner_id.country_id.code, superself.fedex_account_number)
package_count = len(picking.package_ids) or 1 package_count = len(picking_packages) or 1
# TODO RIM master: factorize the following crap # TODO RIM master: factorize the following crap
@@ -280,13 +298,24 @@ class DeliveryFedex(models.Model):
package_labels = [] package_labels = []
carrier_tracking_ref = "" carrier_tracking_ref = ""
for sequence, package in enumerate(picking.package_ids, start=1): for sequence, package in enumerate(picking_packages, start=1):
package_weight = self._fedex_convert_weight(package.shipping_weight, self.fedex_weight_unit)
package_weight = _convert_weight(package.shipping_weight, self.fedex_weight_unit) packaging = package.packaging_id
packaging_code = packaging.shipper_package_code if (packaging.package_carrier_type == 'fedex' and packaging.shipper_package_code) else self.fedex_default_packaging_id.shipper_package_code
# Hibou Delivery # Hibou Delivery
# Add more details to package. # Add more details to package.
srm.add_package(package_weight, sequence_number=sequence, ref=('%s-%d' % (order_name, sequence)), insurance=insurance_value) srm.add_package(
package_weight,
# package_code=packaging_code,
# package_height=packaging.height,
# package_width=packaging.width,
# package_length=packaging.length,
sequence_number=sequence,
ref=('%s-%d' % (order_name, sequence)),
insurance=superself.get_insurance_value(picking=picking, package=package),
signature_required=superself.get_signature_required(picking=picking, package=package)
)
srm.set_master_package(net_weight, package_count, master_tracking_id=master_tracking_id) srm.set_master_package(net_weight, package_count, master_tracking_id=master_tracking_id)
request = srm.process_shipment() request = srm.process_shipment()
package_name = package.name or sequence package_name = package.name or sequence
@@ -350,9 +379,18 @@ class DeliveryFedex(models.Model):
# One package # # One package #
############### ###############
elif package_count == 1: elif package_count == 1:
# Hibou Delivery packaging = picking_packages[:1].packaging_id or self.fedex_default_packaging_id
# Add more details to package. packaging_code = packaging.shipper_package_code if packaging.package_carrier_type == 'fedex' else self.fedex_default_packaging_id.shipper_package_code
srm.add_package(net_weight, ref=order_name, insurance=insurance_value) srm.add_package(
net_weight,
# package_code=packaging_code,
# package_height=packaging.height,
# package_width=packaging.width,
# package_length=packaging.length,
ref=order_name,
insurance=superself.get_insurance_value(picking=picking, package=picking_packages[:1]),
signature_required=superself.get_signature_required(picking=picking, package=picking_packages[:1])
)
srm.set_master_package(net_weight, 1) srm.set_master_package(net_weight, 1)
# Ask the shipping to fedex # Ask the shipping to fedex
@@ -394,3 +432,265 @@ class DeliveryFedex(models.Model):
raise UserError(_('No packages for this picking')) raise UserError(_('No packages for this picking'))
return res return res
def fedex_rate_shipment_multi(self, order=None, picking=None, packages=None):
if not packages:
return self._fedex_rate_shipment_multi_package(order=order, picking=picking)
else:
rates = []
for package in packages:
rates += self._fedex_rate_shipment_multi_package(order=order, picking=picking, package=package)
return rates
def _fedex_rate_shipment_multi_package(self, order=None, picking=None, package=None):
if order:
max_weight = self._fedex_convert_weight(self.fedex_default_packaging_id.max_weight, self.fedex_weight_unit)
is_india = order.partner_shipping_id.country_id.code == 'IN' and order.company_id.partner_id.country_id.code == 'IN'
est_weight_value = sum([(line.product_id.weight * line.product_uom_qty) for line in order.order_line]) or 0.0
weight_value = self._fedex_convert_weight(est_weight_value, self.fedex_weight_unit)
order_currency = order.currency_id
elif not package:
is_india = picking.partner_id.country_id.code == 'IN' and picking.company_id.partner_id.country_id.code == 'IN'
est_weight_value = sum([(line.product_id.weight * (line.qty_done or line.product_uom_qty)) for line in picking.move_line_ids]) or 0.0
weight_value = self._fedex_convert_weight(est_weight_value, self.fedex_weight_unit)
order_currency = picking.sale_id.currency_id if picking.sale_id else picking.company_id.currency_id
else:
is_india = picking.partner_id.country_id.code == 'IN' and picking.company_id.partner_id.country_id.code == 'IN'
order_currency = picking.sale_id.currency_id if picking.sale_id else picking.company_id.currency_id
est_weight_value = package.shipping_weight or package.weight
weight_value = self._fedex_convert_weight(est_weight_value, self.fedex_weight_unit)
price = 0.0
# Some users may want to ship very lightweight items; in order to give them a rating, we round the
# converted weight of the shipping to the smallest value accepted by FedEx: 0.01 kg or lb.
# (in the case where the weight is actually 0.0 because weights are not set, don't do this)
if weight_value > 0.0:
weight_value = max(weight_value, 0.01)
superself = self.sudo()
# Hibou Delivery methods for collecting details in an overridable way
shipper_company = superself.get_shipper_company(order=order, picking=picking)
shipper_warehouse = superself.get_shipper_warehouse(order=order, picking=picking)
recipient = superself.get_recipient(order=order, picking=picking)
acc_number = superself._get_fedex_account_number(order=order, picking=picking)
meter_number = superself._get_fedex_meter_number(order=order, picking=picking)
order_name = superself.get_order_name(order=order, picking=picking)
insurance_value = superself.get_insurance_value(order=order, picking=picking, package=package)
signature_required = superself.get_signature_required(order=order, picking=picking, package=package)
residential = self._get_fedex_recipient_is_residential(recipient)
date_planned = fields.Datetime.now()
if self.env.context.get('date_planned'):
date_planned = self.env.context.get('date_planned')
if date_planned and isinstance(date_planned, str):
date_planned = fields.Datetime.from_string(date_planned)
# Authentication stuff
srm = FedexRequest(self.log_xml, request_type="rating", prod_environment=self.prod_environment)
srm.web_authentication_detail(superself.fedex_developer_key, superself.fedex_developer_password)
srm.client_detail(acc_number, meter_number)
# Build basic rating request and set addresses
srm.transaction_detail(order_name)
# TODO what shipment requests can we make?
# TODO package and main weights?
# TODO need package & weight count to pass in
srm.shipment_request(
self.fedex_droppoff_type,
None, # because we want all of the rates back...
self.fedex_default_packaging_id.shipper_package_code,
self.fedex_weight_unit,
self.fedex_saturday_delivery,
ship_timestamp=date_planned,
)
pkg = self.fedex_default_packaging_id
srm.set_currency(_convert_curr_iso_fdx(order_currency.name))
srm.set_shipper(shipper_company, shipper_warehouse)
srm.set_recipient(recipient, residential=residential)
if order and max_weight and weight_value > max_weight:
total_package = int(weight_value / max_weight)
last_package_weight = weight_value % max_weight
for sequence in range(1, total_package + 1):
srm.add_package(
max_weight,
# package_code=pkg.shipper_package_code,
# package_height=pkg.height,
# package_width=pkg.width,
# package_length=pkg.length,
sequence_number=sequence,
mode='rating',
)
if last_package_weight:
total_package = total_package + 1
srm.add_package(
last_package_weight,
# package_code=pkg.shipper_package_code,
# package_height=pkg.height,
# package_width=pkg.width,
# package_length=pkg.length,
sequence_number=total_package,
mode='rating',
)
srm.set_master_package(weight_value, total_package)
elif order:
srm.add_package(
weight_value,
# package_code=pkg.shipper_package_code,
# package_height=pkg.height,
# package_width=pkg.width,
# package_length=pkg.length,
mode='rating',
)
srm.set_master_package(weight_value, 1)
else:
if package:
package_weight = self._fedex_convert_weight(package.shipping_weight or package.weight, self.fedex_weight_unit)
packaging = package.packaging_id
package_code = package.packaging_id.shipper_package_code if packaging.package_carrier_type == 'fedex' else self.fedex_default_packaging_id.shipper_package_code
srm.add_package(
package_weight,
mode='rating',
# package_code=package_code,
# package_height=packaging.height,
# package_width=packaging.width,
# package_length=packaging.length,
sequence_number=1,
ref=('%s-%d' % (order_name, 1)),
insurance=insurance_value,
signature_required=signature_required
)
else:
# deliver all together...
package_weight = self._fedex_convert_weight(picking.shipping_weight or picking.weight, self.fedex_weight_unit)
packaging = self.fedex_default_packaging_id
srm.add_package(
package_weight,
mode='rating',
# package_code=packaging.shipper_package_code,
# package_height=packaging.height,
# package_width=packaging.width,
# package_length=packaging.length,
sequence_number=1,
# po_number=po_number,
# dept_number=dept_number,
ref=('%s-%d' % (order_name, 1)),
insurance=insurance_value,
signature_required=signature_required
)
# Commodities for customs declaration (international shipping)
# TODO Intestigate rating if needed...
# if self.fedex_service_type in ['INTERNATIONAL_ECONOMY', 'INTERNATIONAL_PRIORITY'] or is_india:
# total_commodities_amount = 0.0
# commodity_country_of_manufacture = order.warehouse_id.partner_id.country_id.code
#
# for line in order.order_line.filtered(lambda l: l.product_id.type in ['product', 'consu']):
# commodity_amount = line.price_total / line.product_uom_qty
# total_commodities_amount += (commodity_amount * line.product_uom_qty)
# commodity_description = line.product_id.name
# commodity_number_of_piece = '1'
# commodity_weight_units = self.fedex_weight_unit
# commodity_weight_value = self._fedex_convert_weight(line.product_id.weight * line.product_uom_qty,
# self.fedex_weight_unit)
# commodity_quantity = line.product_uom_qty
# commodity_quantity_units = 'EA'
# # DO NOT FORWARD PORT AFTER 12.0
# if getattr(line.product_id, 'hs_code', False):
# commodity_harmonized_code = line.product_id.hs_code or ''
# else:
# commodity_harmonized_code = ''
# srm._commodities(_convert_curr_iso_fdx(order_currency.name), commodity_amount,
# commodity_number_of_piece, commodity_weight_units, commodity_weight_value,
# commodity_description, commodity_country_of_manufacture, commodity_quantity,
# commodity_quantity_units, commodity_harmonized_code)
# srm.customs_value(_convert_curr_iso_fdx(order_currency.name), total_commodities_amount, "NON_DOCUMENTS")
# srm.duties_payment(order.warehouse_id.partner_id.country_id.code, superself.fedex_account_number)
request_list = srm.rate(date_planned=date_planned, multi=True)
result = []
for request in request_list:
price = 0.0
warnings = request.get('warnings_message')
if warnings:
_logger.info(warnings)
if not request.get('errors_message'):
if _convert_curr_iso_fdx(order_currency.name) in request['price']:
price = request['price'][_convert_curr_iso_fdx(order_currency.name)]
else:
_logger.info("Preferred currency has not been found in FedEx response")
company_currency = order.company_id.currency_id
if _convert_curr_iso_fdx(company_currency.name) in request['price']:
amount = request['price'][_convert_curr_iso_fdx(company_currency.name)]
price = company_currency._convert(amount, order_currency, order.company_id,
order.date_order or fields.Date.today())
else:
amount = request['price']['USD']
price = company_currency._convert(amount, order_currency, order.company_id,
order.date_order or fields.Date.today())
else:
result.append({'carrier': self,
'success': False,
'price': 0.0,
'error_message': _('Error:\n%s') % request['errors_message'],
'warning_message': False,
'service_code': request['service_code'],
})
service_code = request['service_code']
carrier = self.fedex_find_delivery_carrier_for_service(service_code)
if carrier:
date_delivered = request.get('date_delivered', False)
result.append({'carrier': carrier,
'package': package or self.env['stock.quant.package'].browse(),
'success': True,
'price': price,
'error_message': False,
'transit_days': request.get('transit_days', False),
'date_delivered': date_delivered,
'date_planned': date_planned,
'warning_message': _('Warning:\n%s') % warnings if warnings else False,
'service_code': request['service_code'],
})
return result
def fedex_find_delivery_carrier_for_service(self, service_code):
if self.fedex_service_type == service_code:
return self
# arbitrary decision, lets find the same account number
carrier = self.search([('fedex_account_number', '=', self.fedex_account_number),
('fedex_service_type', '=', service_code)
], limit=1)
return carrier
def fedex_cancel_shipment(self, picking):
request = FedexRequest(self.log_xml, request_type="shipping", prod_environment=self.prod_environment)
superself = self.sudo()
request.web_authentication_detail(superself.fedex_developer_key, superself.fedex_developer_password)
acc_number = superself._get_fedex_account_number(picking=picking)
meter_number = superself._get_fedex_meter_number(picking=picking)
request.client_detail(acc_number, meter_number)
request.transaction_detail(picking.id)
master_tracking_id = picking.carrier_tracking_ref.split(',')[0]
request.set_deletion_details(master_tracking_id)
result = request.delete_shipment()
warnings = result.get('warnings_message')
if warnings:
_logger.info(warnings)
if result.get('delete_success') and not result.get('errors_message'):
picking.message_post(body=_(u'Shipment N° %s has been cancelled' % master_tracking_id))
picking.write({'carrier_tracking_ref': '',
'carrier_price': 0.0})
else:
raise UserError(result['errors_message'])

View File

@@ -1,9 +1,13 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
import suds import suds
from odoo.addons.delivery_fedex.models import fedex_request from odoo.addons.delivery_fedex.models import fedex_request
from datetime import datetime
from copy import deepcopy
from pprint import pformat from pprint import pformat
import logging import logging
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
# logging.getLogger('suds.client').setLevel(logging.DEBUG)
STATECODE_REQUIRED_COUNTRIES = fedex_request.STATECODE_REQUIRED_COUNTRIES STATECODE_REQUIRED_COUNTRIES = fedex_request.STATECODE_REQUIRED_COUNTRIES
@@ -31,6 +35,7 @@ class FedexRequest(fedex_request.FedexRequest):
_service_transit_days = { _service_transit_days = {
'FEDEX_2_DAY': 2, 'FEDEX_2_DAY': 2,
'FEDEX_2_DAY_AM': 2, 'FEDEX_2_DAY_AM': 2,
'FEDEX_3_DAY_FREIGHT': 3,
'FIRST_OVERNIGHT': 1, 'FIRST_OVERNIGHT': 1,
'PRIORITY_OVERNIGHT': 1, 'PRIORITY_OVERNIGHT': 1,
'STANDARD_OVERNIGHT': 1, 'STANDARD_OVERNIGHT': 1,
@@ -76,7 +81,7 @@ class FedexRequest(fedex_request.FedexRequest):
self.RequestedShipment.Recipient.Contact = Contact self.RequestedShipment.Recipient.Contact = Contact
self.RequestedShipment.Recipient.Address = Address self.RequestedShipment.Recipient.Address = Address
def add_package(self, weight_value, sequence_number=False, mode='shipping', ref=False, insurance=False): def add_package(self, weight_value, sequence_number=False, mode='shipping', ref=False, insurance=False, signature_required=False):
""" """
Adds ref type of object to include. Adds ref type of object to include.
:param weight_value: default :param weight_value: default
@@ -84,6 +89,7 @@ class FedexRequest(fedex_request.FedexRequest):
:param mode: default :param mode: default
:param ref: NEW add CUSTOMER_REFERENCE object :param ref: NEW add CUSTOMER_REFERENCE object
:param insurance: NEW add Insurance amount :param insurance: NEW add Insurance amount
:param signature_required: NEW add signature required
:return: :return:
""" """
package = self.client.factory.create('RequestedPackageLineItem') package = self.client.factory.create('RequestedPackageLineItem')
@@ -104,6 +110,12 @@ class FedexRequest(fedex_request.FedexRequest):
insured.Currency = 'USD' insured.Currency = 'USD'
package.InsuredValue = insured package.InsuredValue = insured
special_service = self.client.factory.create("PackageSpecialServicesRequested")
signature_detail = self.client.factory.create("SignatureOptionDetail")
signature_detail.OptionType = 'DIRECT' if signature_required else 'NO_SIGNATURE_REQUIRED'
special_service.SignatureOptionDetail = signature_detail
package.SpecialServicesRequested = special_service
package.PhysicalPackaging = 'BOX' package.PhysicalPackaging = 'BOX'
package.Weight = package_weight package.Weight = package_weight
if mode == 'rating': if mode == 'rating':
@@ -130,20 +142,44 @@ class FedexRequest(fedex_request.FedexRequest):
Payor.ResponsibleParty.AccountNumber = shipping_charges_payment_account Payor.ResponsibleParty.AccountNumber = shipping_charges_payment_account
self.RequestedShipment.ShippingChargesPayment.Payor = Payor self.RequestedShipment.ShippingChargesPayment.Payor = Payor
def shipment_request(self, dropoff_type, service_type, packaging_type, overall_weight_unit, saturday_delivery, ship_timestamp=None):
self.RequestedShipment = self.client.factory.create('RequestedShipment')
self.RequestedShipment.ShipTimestamp = ship_timestamp or datetime.now()
self.RequestedShipment.DropoffType = dropoff_type
self.RequestedShipment.ServiceType = service_type
self.RequestedShipment.PackagingType = packaging_type
# Resuest estimation of duties and taxes for international shipping
if service_type in ['INTERNATIONAL_ECONOMY', 'INTERNATIONAL_PRIORITY']:
self.RequestedShipment.EdtRequestType = 'ALL'
else:
self.RequestedShipment.EdtRequestType = 'NONE'
self.RequestedShipment.PackageCount = 0
self.RequestedShipment.TotalWeight.Units = overall_weight_unit
self.RequestedShipment.TotalWeight.Value = 0
self.listCommodities = []
if saturday_delivery:
timestamp_day = self.RequestedShipment.ShipTimestamp.strftime("%A")
if (service_type == 'FEDEX_2_DAY' and timestamp_day == 'Thursday') or (service_type in ['PRIORITY_OVERNIGHT', 'FIRST_OVERNIGHT', 'INTERNATIONAL_PRIORITY'] and timestamp_day == 'Friday'):
SpecialServiceTypes = self.client.factory.create('ShipmentSpecialServiceType')
self.RequestedShipment.SpecialServicesRequested.SpecialServiceTypes = [SpecialServiceTypes.SATURDAY_DELIVERY]
# Rating stuff # Rating stuff
def rate(self, date_planned=None): def rate(self, date_planned=None, multi=False):
""" """
Response will contain 'transit_days' key with number of days. Response will contain 'transit_days' key with number of days.
:param date_planned: Planned Outgoing shipment. Used to have FedEx tell us how long it will take for the package to arrive. :param date_planned: Planned Outgoing shipment. Used to have FedEx tell us how long it will take for the package to arrive.
:return: :return:
""" """
if multi:
multi_result = []
if date_planned:
self.RequestedShipment.ShipTimestamp = date_planned
formatted_response = {'price': {}} formatted_response = {'price': {}}
del self.ClientDetail.Region del self.ClientDetail.Region
if self.hasCommodities:
if date_planned: self.RequestedShipment.CustomsClearanceDetail.Commodities = self.listCommodities
# though Fedex sends BACK timestamps like `2020-01-01 00:00:00` they EXPECT `2020-01-01T00:00:00`
self.RequestedShipment.ShipTimestamp = date_planned.replace(' ', 'T')
try: try:
self.response = self.client.service.getRates(WebAuthenticationDetail=self.WebAuthenticationDetail, self.response = self.client.service.getRates(WebAuthenticationDetail=self.WebAuthenticationDetail,
@@ -152,7 +188,12 @@ class FedexRequest(fedex_request.FedexRequest):
Version=self.VersionId, Version=self.VersionId,
RequestedShipment=self.RequestedShipment, RequestedShipment=self.RequestedShipment,
ReturnTransitAndCommit=True) # New ReturnTransitAndCommit for CommitDetails in response ReturnTransitAndCommit=True) # New ReturnTransitAndCommit for CommitDetails in response
if (self.response.HighestSeverity != 'ERROR' and self.response.HighestSeverity != 'FAILURE'): if (self.response.HighestSeverity != 'ERROR' and self.response.HighestSeverity != 'FAILURE'):
if not getattr(self.response, "RateReplyDetails", False):
raise Exception("No rating found")
if not multi:
for rating in self.response.RateReplyDetails[0].RatedShipmentDetails: for rating in self.response.RateReplyDetails[0].RatedShipmentDetails:
formatted_response['price'][rating.ShipmentRateDetail.TotalNetFedExCharge.Currency] = rating.ShipmentRateDetail.TotalNetFedExCharge.Amount formatted_response['price'][rating.ShipmentRateDetail.TotalNetFedExCharge.Currency] = rating.ShipmentRateDetail.TotalNetFedExCharge.Amount
if len(self.response.RateReplyDetails[0].RatedShipmentDetails) == 1: if len(self.response.RateReplyDetails[0].RatedShipmentDetails) == 1:
@@ -162,13 +203,42 @@ class FedexRequest(fedex_request.FedexRequest):
# Hibou Delivery Planning # Hibou Delivery Planning
if hasattr(self.response.RateReplyDetails[0], 'DeliveryTimestamp') and self.response.RateReplyDetails[0].DeliveryTimestamp: if hasattr(self.response.RateReplyDetails[0], 'DeliveryTimestamp') and self.response.RateReplyDetails[0].DeliveryTimestamp:
formatted_response['date_delivered'] = self.response.RateReplyDetails[0].DeliveryTimestamp formatted_response['date_delivered'] = self.response.RateReplyDetails[0].DeliveryTimestamp
elif hasattr(self.response.RateReplyDetails[0].CommitDetails[0], 'CommitTimestamp'): if hasattr(self.response.RateReplyDetails[0].CommitDetails[0], 'TransitTime'):
formatted_response['date_delivered'] = self.response.RateReplyDetails[0].CommitDetails[0].CommitTimestamp
formatted_response['transit_days'] = self._service_transit_days.get(self.response.RateReplyDetails[0].CommitDetails[0].ServiceType, 0)
elif hasattr(self.response.RateReplyDetails[0].CommitDetails[0], 'TransitTime'):
transit_days = self.response.RateReplyDetails[0].CommitDetails[0].TransitTime transit_days = self.response.RateReplyDetails[0].CommitDetails[0].TransitTime
transit_days = self._transit_days.get(transit_days, 0) transit_days = self._transit_days.get(transit_days, 0)
formatted_response['transit_days'] = transit_days formatted_response['transit_days'] = transit_days
elif hasattr(self.response.RateReplyDetails[0], 'CommitDetails') and hasattr(self.response.RateReplyDetails[0].CommitDetails[0], 'CommitTimestamp'):
formatted_response['date_delivered'] = self.response.RateReplyDetails[0].CommitDetails[0].CommitTimestamp
formatted_response['transit_days'] = self._service_transit_days.get(self.response.RateReplyDetails[0].CommitDetails[0].ServiceType, 0)
elif hasattr(self.response.RateReplyDetails[0], 'CommitDetails') and hasattr(self.response.RateReplyDetails[0].CommitDetails[0], 'TransitTime'):
transit_days = self.response.RateReplyDetails[0].CommitDetails[0].TransitTime
transit_days = self._transit_days.get(transit_days, 0)
formatted_response['transit_days'] = transit_days
else:
for rate_reply_detail in self.response.RateReplyDetails:
res = deepcopy(formatted_response)
res['service_code'] = rate_reply_detail.ServiceType
for rating in rate_reply_detail.RatedShipmentDetails:
res['price'][rating.ShipmentRateDetail.TotalNetFedExCharge.Currency] = rating.ShipmentRateDetail.TotalNetFedExCharge.Amount
if len(rate_reply_detail.RatedShipmentDetails) == 1:
if 'CurrencyExchangeRate' in rate_reply_detail.RatedShipmentDetails[0].ShipmentRateDetail:
res['price'][rate_reply_detail.RatedShipmentDetails[0].ShipmentRateDetail.CurrencyExchangeRate.FromCurrency] = rate_reply_detail.RatedShipmentDetails[0].ShipmentRateDetail.TotalNetFedExCharge.Amount / rate_reply_detail.RatedShipmentDetails[0].ShipmentRateDetail.CurrencyExchangeRate.Rate
# Hibou Delivery Planning
if hasattr(rate_reply_detail, 'DeliveryTimestamp') and rate_reply_detail.DeliveryTimestamp:
res['date_delivered'] = rate_reply_detail.DeliveryTimestamp
res['transit_days'] = self._service_transit_days.get(rate_reply_detail.ServiceType, 0)
if not res['transit_days'] and hasattr(rate_reply_detail.CommitDetails[0], 'TransitTime'):
transit_days = rate_reply_detail.CommitDetails[0].TransitTime
transit_days = self._transit_days.get(transit_days, 0)
res['transit_days'] = transit_days
elif hasattr(rate_reply_detail, 'CommitDetails') and hasattr(rate_reply_detail.CommitDetails[0], 'CommitTimestamp'):
res['date_delivered'] = rate_reply_detail.CommitDetails[0].CommitTimestamp
res['transit_days'] = self._service_transit_days.get(rate_reply_detail.ServiceType, 0)
elif hasattr(rate_reply_detail, 'CommitDetails') and hasattr(rate_reply_detail.CommitDetails[0], 'TransitTime'):
transit_days = rate_reply_detail.CommitDetails[0].TransitTime
transit_days = self._transit_days.get(transit_days, 0)
res['transit_days'] = transit_days
multi_result.append(res)
else: else:
errors_message = '\n'.join([("%s: %s" % (n.Code, n.Message)) for n in self.response.Notifications if (n.Severity == 'ERROR' or n.Severity == 'FAILURE')]) errors_message = '\n'.join([("%s: %s" % (n.Code, n.Message)) for n in self.response.Notifications if (n.Severity == 'ERROR' or n.Severity == 'FAILURE')])
@@ -182,5 +252,9 @@ class FedexRequest(fedex_request.FedexRequest):
formatted_response['errors_message'] = fault formatted_response['errors_message'] = fault
except IOError: except IOError:
formatted_response['errors_message'] = "Fedex Server Not Found" formatted_response['errors_message'] = "Fedex Server Not Found"
except Exception as e:
formatted_response['errors_message'] = e.args[0]
if multi:
return multi_result
return formatted_response return formatted_response

View File

@@ -1,3 +1,5 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
from odoo import api, fields, models from odoo import api, fields, models

View File

@@ -1 +1,3 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
from . import models from . import models

View File

@@ -1,10 +1,10 @@
{ {
'name': 'Golden State Overnight (gso.com) Shipping', 'name': 'Golden State Overnight (gso.com) Shipping',
'summary': 'Send your shippings through gso.com and track them online.', 'summary': 'Send your shippings through gso.com and track them online.',
'version': '11.0.1.0.0', 'version': '11.0.1.1.0',
'author': "Hibou Corp.", 'author': "Hibou Corp.",
'category': 'Warehouse', 'category': 'Warehouse',
'license': 'AGPL-3', 'license': 'OPL-1',
'images': [], 'images': [],
'website': "https://hibou.io", 'website': "https://hibou.io",
'description': """ 'description': """

View File

@@ -1 +1,3 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
from . import delivery_gso from . import delivery_gso

View File

@@ -1,3 +1,5 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
import pytz import pytz
from math import ceil from math import ceil
from requests import HTTPError from requests import HTTPError
@@ -160,7 +162,7 @@ class ProviderGSO(models.Model):
company = self.get_shipper_company(picking=picking) company = self.get_shipper_company(picking=picking)
from_ = self.get_shipper_warehouse(picking=picking) from_ = self.get_shipper_warehouse(picking=picking)
to = self.get_recipient(picking=picking) to = self.get_recipient(picking=picking)
address_type = 'B' if bool(to.company or to.parent_id.company) else 'R' address_type = 'B' if bool(to.is_company or to.parent_id.is_company) else 'R'
request_body = { request_body = {
'AccountNumber': sudoself.gso_account_number, 'AccountNumber': sudoself.gso_account_number,
@@ -175,22 +177,34 @@ class ProviderGSO(models.Model):
request_body['Shipment'].update(self._gso_make_shipper_address(from_, company)) request_body['Shipment'].update(self._gso_make_shipper_address(from_, company))
request_body['Shipment'].update(self._gso_make_ship_address(to)) request_body['Shipment'].update(self._gso_make_ship_address(to))
# Automatic insurance at $100.0
insurance_value = sudoself.get_insurance_value(picking=picking)
if insurance_value:
request_body['Shipment']['SignatureCode'] = 'SIG_REQD'
if insurance_value > 100.0:
# Documentation says to set DeclaredValue ONLY if over $100.00
request_body['Shipment']['DeclaredValue'] = insurance_value
cost = 0.0 cost = 0.0
labels = { labels = {
'thermal': [], 'thermal': [],
'paper': [], 'paper': [],
} }
if picking.package_ids: picking_packages = picking.package_ids
package_carriers = picking_packages.mapped('carrier_id')
if package_carriers:
# only ship ours
picking_packages = picking_packages.filtered(lambda p: p.carrier_id == self and not p.carrier_tracking_ref)
if picking_packages:
# Every package will be a transaction # Every package will be a transaction
for package in picking.package_ids: for package in picking_packages:
# Use Sale Order Number or fall back to Picking
shipment_ref = (picking.sale_id.name if picking.sale_id else picking.name) + '-' + package.name
insurance_value = sudoself.get_insurance_value(picking=picking, package=package)
if insurance_value > 100.0:
# Documentation says to set DeclaredValue ONLY if over $100.00
request_body['Shipment']['DeclaredValue'] = insurance_value
elif 'DeclaredValue' in request_body['Shipment']:
del request_body['Shipment']['DeclaredValue']
if sudoself.get_signature_required(picking=picking, package=package):
request_body['Shipment']['SignatureCode'] = 'SIG_REQD'
else:
request_body['Shipment']['SignatureCode'] = 'SIG_NOT_REQD'
request_body['Shipment']['Weight'] = self._gso_convert_weight(package.shipping_weight) request_body['Shipment']['Weight'] = self._gso_convert_weight(package.shipping_weight)
request_body['Shipment'].update(self._gso_get_package_dimensions(package)) request_body['Shipment'].update(self._gso_get_package_dimensions(package))
request_body['Shipment']['ShipmentReference'] = package.name request_body['Shipment']['ShipmentReference'] = package.name
@@ -207,10 +221,12 @@ class ProviderGSO(models.Model):
cost += response['ShipmentCharges']['TotalCharge'] cost += response['ShipmentCharges']['TotalCharge']
except HTTPError as e: except HTTPError as e:
raise ValidationError(e) raise ValidationError(e)
else: elif not package_carriers:
# ship the whole picking
shipment_ref = picking.sale_id.name if picking.sale_id else picking.name
request_body['Shipment']['Weight'] = self._gso_convert_weight(picking.shipping_weight) request_body['Shipment']['Weight'] = self._gso_convert_weight(picking.shipping_weight)
request_body['Shipment'].update(self._gso_get_package_dimensions()) request_body['Shipment'].update(self._gso_get_package_dimensions())
request_body['Shipment']['ShipmentReference'] = picking.name request_body['Shipment']['ShipmentReference'] = shipment_ref
request_body['Shipment']['TrackingNumber'] = self._gso_create_tracking_number(picking.name) request_body['Shipment']['TrackingNumber'] = self._gso_create_tracking_number(picking.name)
try: try:
response = service.post_shipment(request_body) response = service.post_shipment(request_body)
@@ -224,6 +240,8 @@ class ProviderGSO(models.Model):
cost += response['ShipmentCharges']['TotalCharge'] cost += response['ShipmentCharges']['TotalCharge']
except HTTPError as e: except HTTPError as e:
raise ValidationError(e) raise ValidationError(e)
else:
continue
# Handle results # Handle results
trackings = [l[0] for l in labels['thermal']] + [l(0) for l in labels['paper']] trackings = [l[0] for l in labels['thermal']] + [l(0) for l in labels['paper']]
@@ -251,7 +269,7 @@ class ProviderGSO(models.Model):
} }
for tracking in picking.carrier_tracking_ref.split(','): for tracking in picking.carrier_tracking_ref.split(','):
request_body['TrackingNumber'] = tracking request_body['TrackingNumber'] = tracking
_ = service.delete_shipment(request_body) cancel_res = service.delete_shipment(request_body)
except HTTPError as e: except HTTPError as e:
raise ValidationError(e) raise ValidationError(e)
picking.message_post(body=(_('Shipment N° %s has been cancelled') % (picking.carrier_tracking_ref, ))) picking.message_post(body=(_('Shipment N° %s has been cancelled') % (picking.carrier_tracking_ref, )))
@@ -262,7 +280,7 @@ class ProviderGSO(models.Model):
service = sudoself._get_gso_service() service = sudoself._get_gso_service()
from_ = sudoself.get_shipper_warehouse(order=order) from_ = sudoself.get_shipper_warehouse(order=order)
to = sudoself.get_recipient(order=order) to = sudoself.get_recipient(order=order)
address_type = 'B' if bool(to.company or to.parent_id.company) else 'R' address_type = 'B' if bool(to.is_company or to.parent_id.is_company) else 'R'
est_weight_value = self._gso_convert_weight( est_weight_value = self._gso_convert_weight(
sum([(line.product_id.weight * line.product_uom_qty) for line in order.order_line]) or 0.0) sum([(line.product_id.weight * line.product_uom_qty) for line in order.order_line]) or 0.0)
@@ -319,3 +337,106 @@ class ProviderGSO(models.Model):
for _ in pickings: for _ in pickings:
res.append('https://www.gso.com/Tracking') res.append('https://www.gso.com/Tracking')
return res return res
def gso_rate_shipment_multi(self, order=None, picking=None, packages=None):
if not packages:
return self._gso_rate_shipment_multi_package(order=order, picking=picking)
else:
rates = []
for package in packages:
rates += self._gso_rate_shipment_multi_package(order=order, picking=picking, package=package)
return rates
def _gso_rate_shipment_multi_package(self, order=None, picking=None, package=None):
sudoself = self.sudo()
try:
service = sudoself._get_gso_service()
except HTTPError as e:
# _logger.error(e)
return [{
'success': False,
'price': 0.0,
'error_message': _('GSO web service returned an error. ' + str(e)),
'warning_message': False,
}]
from_ = sudoself.get_shipper_warehouse(order=order, picking=picking)
to = sudoself.get_recipient(order=order, picking=picking)
address_type = 'B' if bool(to.is_company or to.parent_id.is_company) else 'R'
package_dimensions = self._gso_get_package_dimensions(package=package)
date_planned = fields.Datetime.now()
if self.env.context.get('date_planned'):
date_planned = self.env.context.get('date_planned')
ship_date_utc = fields.Datetime.from_string(date_planned if date_planned else fields.Datetime.now())
ship_date_utc = ship_date_utc.replace(tzinfo=pytz.utc)
ship_date_gso = ship_date_utc.astimezone(pytz.timezone(GSO_TZ))
ship_date_gso = fields.Datetime.to_string(ship_date_gso)
if order:
est_weight_value = self._gso_convert_weight(
sum([(line.product_id.weight * line.product_uom_qty) for line in order.order_line]) or 0.0)
elif not package:
est_weight_value = self._gso_convert_weight(picking.shipping_weight)
else:
est_weight_value = self._gso_convert_weight(package.shipping_weight or package.weight)
request_body = {
'AccountNumber': sudoself.gso_account_number,
'OriginZip': from_.zip,
'DestinationZip': to.zip,
'ShipDate': ship_date_gso,
'PackageDimension': package_dimensions,
'PackageWeight': est_weight_value,
'DeliveryAddressType': address_type,
}
try:
result = service.get_rates_and_transit_time(request_body)
# _logger.warn('GSO result:\n%s' % result)
except HTTPError as e:
# _logger.error(e)
return [{
'success': False,
'price': 0.0,
'error_message': _('GSO web service returned an error.'),
'warning_message': False,
}]
# delivery = list(filter(lambda d: d['ServiceCode'] == sudoself.gso_service_type, result['DeliveryServiceTypes']))
# if delivery:
rates = []
for delivery in result['DeliveryServiceTypes']:
delivery_date_gso = delivery['DeliveryDate'].replace('T', ' ')
delivery_date_gso = fields.Datetime.from_string(delivery_date_gso)
delivery_date_gso = delivery_date_gso.replace(tzinfo=pytz.timezone(GSO_TZ))
delivery_date_utc = delivery_date_gso.astimezone(pytz.utc)
delivery_date_utc = fields.Datetime.to_string(delivery_date_utc)
price = delivery.get('ShipmentCharges', {}).get('TotalCharge', 0.0)
carrier = self.gso_find_delivery_carrier_for_service(delivery['ServiceCode'])
if carrier:
rates.append({
'carrier': carrier,
'package': package or self.env['stock.quant.package'].browse(),
'success': True,
'price': price,
'error_message': False,
'warning_message': _('TotalCharge not found.') if price == 0.0 else False,
'date_planned': date_planned,
'date_delivered': delivery_date_utc,
'transit_days': False,
'service_code': delivery['ServiceCode'],
})
return rates
def gso_find_delivery_carrier_for_service(self, service_code):
if self.gso_service_type == service_code:
return self
# arbitrary decision, lets find the same account number
carrier = self.search([('gso_account_number', '=', self.gso_account_number),
('gso_service_type', '=', service_code)
], limit=1)
return carrier

View File

@@ -1,3 +1,5 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
import requests import requests
from json import dumps from json import dumps

View File

@@ -1,7 +1,7 @@
{ {
'name': 'Delivery Hibou', 'name': 'Delivery Hibou',
'summary': 'Adds underlying pinnings for things like "RMA Return Labels"', 'summary': 'Adds underlying pinnings for things like "RMA Return Labels"',
'version': '11.0.1.0.0', 'version': '11.0.1.2.1',
'author': "Hibou Corp.", 'author': "Hibou Corp.",
'category': 'Stock', 'category': 'Stock',
'license': 'AGPL-3', 'license': 'AGPL-3',

View File

@@ -1,5 +1,6 @@
from odoo import fields, models from odoo import api, fields, models
from odoo.addons.stock.models.stock_move import PROCUREMENT_PRIORITIES from odoo.addons.stock.models.stock_move import PROCUREMENT_PRIORITIES
from odoo.exceptions import UserError
class DeliveryCarrier(models.Model): class DeliveryCarrier(models.Model):
@@ -8,6 +9,9 @@ class DeliveryCarrier(models.Model):
automatic_insurance_value = fields.Float(string='Automatic Insurance Value', automatic_insurance_value = fields.Float(string='Automatic Insurance Value',
help='Will be used during shipping to determine if the ' help='Will be used during shipping to determine if the '
'picking\'s value warrants insurance being added.') 'picking\'s value warrants insurance being added.')
automatic_sig_req_value = fields.Float(string='Automatic Signature Required Value',
help='Will be used during shipping to determine if the '
'picking\'s value warrants signature required being added.')
procurement_priority = fields.Selection(PROCUREMENT_PRIORITIES, procurement_priority = fields.Selection(PROCUREMENT_PRIORITIES,
string='Procurement Priority', string='Procurement Priority',
help='Priority for this carrier. Will affect pickings ' help='Priority for this carrier. Will affect pickings '
@@ -15,21 +19,42 @@ class DeliveryCarrier(models.Model):
# Utility # Utility
def get_insurance_value(self, order=None, picking=None): def get_insurance_value(self, order=None, picking=None, package=None):
value = 0.0 value = 0.0
if order: if order:
if order.order_line: if order.order_line:
value = sum(order.order_line.filtered(lambda l: l.type != 'service').mapped('price_subtotal')) value = sum(order.order_line.filtered(lambda l: l.product_id.type != 'service').mapped('price_subtotal'))
else: else:
return value return value
if picking: if picking:
value = picking.declared_value() value = picking.declared_value(package=package)
if package and not package.require_insurance:
value = 0.0
else:
if picking.require_insurance == 'no': if picking.require_insurance == 'no':
value = 0.0 value = 0.0
elif picking.require_insurance == 'auto' and self.automatic_insurance_value and self.automatic_insurance_value > value: elif picking.require_insurance == 'auto' and self.automatic_insurance_value and self.automatic_insurance_value > value:
value = 0.0 value = 0.0
return value return value
def get_signature_required(self, order=None, picking=None, package=None):
value = 0.0
if order:
if order.order_line:
value = sum(order.order_line.filtered(lambda l: l.product_id.type != 'service').mapped('price_subtotal'))
else:
return False
if picking:
value = picking.declared_value(package=package)
if package:
return package.require_signature
else:
if picking.require_signature == 'no':
return False
elif picking.require_signature == 'yes':
return True
return self.automatic_sig_req_value and value >= self.automatic_sig_req_value
def get_third_party_account(self, order=None, picking=None): def get_third_party_account(self, order=None, picking=None):
if order and order.shipping_account_id: if order and order.shipping_account_id:
return order.shipping_account_id return order.shipping_account_id
@@ -157,3 +182,133 @@ class DeliveryCarrier(models.Model):
def _get_recipient_out(self, picking): def _get_recipient_out(self, picking):
return picking.partner_id return picking.partner_id
# -------------------------- #
# API for external providers #
# -------------------------- #
@api.multi
def rate_shipment_multi(self, order=None, picking=None, packages=None):
''' Compute the price of the order shipment
:param order: record of sale.order or None
:param picking: record of stock.picking or None
:param packages: recordset of stock.quant.package or None (requires picking also set)
:return list: dict: {
'carrier': delivery.carrier(),
'success': boolean,
'price': a float,
'error_message': a string containing an error message,
'warning_message': a string containing a warning message,
'date_planned': a datetime for when the shipment is supposed to leave,
'date_delivered': a datetime for when the shipment is supposed to arrive,
'transit_days': a Float for how many days it takes in transit,
'service_code': a string that represents the service level/agreement,
'package': stock.quant.package(),
}
e.g. self == delivery.carrier(5, 6)
then return might be:
[
{'carrier': delivery.carrier(5), 'price': 10.50, 'service_code': 'GROUND_HOME_DELIVERY', ...},
{'carrier': delivery.carrier(7), 'price': 12.99, 'service_code': 'FEDEX_EXPRESS_SAVER', ...}, # NEW!
{'carrier': delivery.carrier(6), 'price': 8.0, 'service_code': 'USPS_PRI', ...},
]
'''
self.ensure_one()
if picking:
self = self.with_context(date_planned=fields.Datetime.now())
if not packages:
packages = picking.package_ids
else:
if packages:
raise UserError('Cannot rate package without picking.')
self = self.with_context(date_planned=(order.date_planned or fields.Datetime.now()))
res = []
for carrier in self:
carrier_packages = packages and packages.filtered(lambda p: not p.carrier_tracking_ref and
(not p.carrier_id or p.carrier_id == carrier) and
p.packaging_id.package_carrier_type in (False, '', 'none', carrier.delivery_type))
if packages and not carrier_packages:
continue
if hasattr(carrier, '%s_rate_shipment_multi' % self.delivery_type):
try:
res += getattr(carrier, '%s_rate_shipment_multi' % carrier.delivery_type)(order=order,
picking=picking,
packages=carrier_packages)
except TypeError:
# TODO remove catch if after Odoo 14
# This is intended to find ones that don't support packages= kwarg
res += getattr(carrier, '%s_rate_shipment_multi' % carrier.delivery_type)(order=order,
picking=picking)
return res
def cancel_shipment(self, pickings, packages=None):
''' Cancel a shipment
:param pickings: A recordset of pickings
:param packages: Optional recordset of packages (should be for this carrier)
'''
self.ensure_one()
if hasattr(self, '%s_cancel_shipment' % self.delivery_type):
# No good way to tell if this method takes the kwarg for packages
if packages:
try:
return getattr(self, '%s_cancel_shipment' % self.delivery_type)(pickings, packages=packages)
except TypeError:
# we won't be able to cancel the packages properly
# here we will TRY to make a good call here where we put the package references into the picking
# and let the original mechanisms try to work here
tracking_ref = ','.join(packages.mapped('carrier_tracking_ref'))
pickings.write({
'carrier_id': self.id,
'carrier_tracking_ref': tracking_ref,
})
return getattr(self, '%s_cancel_shipment' % self.delivery_type)(pickings)
class ChooseDeliveryPackage(models.TransientModel):
_inherit = 'choose.delivery.package'
package_declared_value = fields.Float(string='Declared Value',
default=lambda self: self._default_package_declared_value())
package_require_insurance = fields.Boolean(string='Require Insurance')
package_require_signature = fields.Boolean(string='Require Signature')
def _default_package_declared_value(self):
# guard for install
if not self.env.context.get('active_id'):
return 0.0
if self.env.context.get('default_stock_quant_package_id'):
stock_quant_package = self.env['stock.quant.package'].browse(self.env.context['default_stock_quant_package_id'])
return stock_quant_package.declared_value
else:
picking_id = self.env['stock.picking'].browse(self.env.context['active_id'])
move_line_ids = [po for po in picking_id.move_line_ids if po.qty_done > 0 and not po.result_package_id]
total_value = sum([po.qty_done * po.product_id.standard_price for po in move_line_ids])
return total_value
@api.onchange('package_declared_value')
def _onchange_package_declared_value(self):
picking = self.env['stock.picking'].browse(self.env.context.get('active_id', 0))
value = self.package_declared_value
if picking.require_insurance == 'auto':
self.package_require_insurance = value and picking.carrier_id.automatic_insurance_value and value >= picking.carrier_id.automatic_insurance_value
else:
self.package_require_insurance = picking.require_insurance == 'yes'
if picking.require_signature == 'auto':
self.package_require_signature = value and picking.carrier_id.automatic_sig_req_value and value >= picking.carrier_id.automatic_sig_req_value
else:
self.package_require_signature = picking.require_signature == 'yes'
def put_in_pack(self):
super().put_in_pack()
if self.stock_quant_package_id:
self.stock_quant_package_id.write({
'declared_value': self.package_declared_value,
'require_insurance': self.package_require_insurance,
'require_signature': self.package_require_signature,
})

View File

@@ -1,4 +1,33 @@
from odoo import api, fields, models from odoo import api, fields, models, _
from odoo.exceptions import UserError
class StockQuantPackage(models.Model):
_inherit = 'stock.quant.package'
carrier_id = fields.Many2one('delivery.carrier', string='Carrier')
carrier_tracking_ref = fields.Char(string='Tracking Reference')
require_insurance = fields.Boolean(string='Require Insurance')
require_signature = fields.Boolean(string='Require Signature')
declared_value = fields.Float(string='Declared Value')
def _get_active_picking(self):
picking_id = self._context.get('active_id')
picking_model = self._context.get('active_model')
if not picking_id or picking_model != 'stock.picking':
picking_id = self._context.get('picking_active_id')
picking_model = self._context.get('picking_active_model')
if not picking_id or picking_model != 'stock.picking':
raise UserError('Cannot cancel package other than through shipment/picking.')
return self.env['stock.picking'].browse(picking_id)
def send_to_shipper(self):
picking = self._get_active_picking()
picking.with_context(packages=self).send_to_shipper()
def cancel_shipment(self):
picking = self._get_active_picking()
picking.with_context(packages=self).cancel_shipment()
class StockPicking(models.Model): class StockPicking(models.Model):
@@ -11,6 +40,28 @@ class StockPicking(models.Model):
('no', 'No'), ('no', 'No'),
], string='Require Insurance', default='auto', ], string='Require Insurance', default='auto',
help='If your carrier supports it, auto should be calculated off of the "Automatic Insurance Value" field.') help='If your carrier supports it, auto should be calculated off of the "Automatic Insurance Value" field.')
require_signature = fields.Selection([
('auto', 'Automatic'),
('yes', 'Yes'),
('no', 'No'),
], string='Require Signature', default='auto',
help='If your carrier supports it, auto should be calculated off of the "Automatic Signature Required Value" field.')
package_carrier_tracking_ref = fields.Char(string='Package Tracking Numbers', compute='_compute_package_carrier_tracking_ref')
@api.depends('package_ids.carrier_tracking_ref')
def _compute_package_carrier_tracking_ref(self):
for picking in self:
package_refs = picking.package_ids.filtered('carrier_tracking_ref').mapped('carrier_tracking_ref')
if package_refs:
picking.package_carrier_tracking_ref = ','.join(package_refs)
else:
picking.package_carrier_tracking_ref = False
@api.onchange('carrier_id')
def _onchange_carrier_id_for_priority(self):
for picking in self:
if picking.carrier_id and picking.carrier_id.procurement_priority:
picking.priority = picking.carrier_id.procurement_priority
@api.one @api.one
@api.depends('move_lines.priority', 'carrier_id') @api.depends('move_lines.priority', 'carrier_id')
@@ -27,18 +78,104 @@ class StockPicking(models.Model):
so = self.env['sale.order'].search([('name', '=', str(origin))], limit=1) so = self.env['sale.order'].search([('name', '=', str(origin))], limit=1)
if so and so.shipping_account_id: if so and so.shipping_account_id:
values['shipping_account_id'] = so.shipping_account_id.id values['shipping_account_id'] = so.shipping_account_id.id
carrier_id = values.get('carrier_id')
if carrier_id:
carrier = self.env['delivery.carrier'].browse(carrier_id)
if carrier.procurement_priority:
values['priority'] = carrier.procurement_priority
res = super(StockPicking, self).create(values) res = super(StockPicking, self).create(values)
return res return res
def declared_value(self): def declared_value(self, package=None):
self.ensure_one() self.ensure_one()
if package:
return package.declared_value
cost = sum([(l.product_id.standard_price * l.qty_done) for l in self.move_line_ids] or [0.0]) cost = sum([(l.product_id.standard_price * l.qty_done) for l in self.move_line_ids] or [0.0])
if not cost: if not cost:
# Assume Full Value # Assume Full Value
cost = sum([(l.product_id.standard_price * l.product_uom_qty) for l in self.move_lines] or [0.0]) cost = sum([(l.product_id.standard_price * l.product_uom_qty) for l in self.move_lines] or [0.0])
return cost return cost
def clear_carrier_tracking_ref(self):
self.write({'carrier_tracking_ref': False})
def reset_carrier_tracking_ref(self):
for picking in self:
picking.carrier_tracking_ref = picking.package_carrier_tracking_ref
# Override to send to specific packaging carriers
def send_to_shipper(self):
packages = self._context.get('packages')
self.ensure_one()
if not packages:
packages = self.package_ids
package_carriers = packages.mapped('carrier_id')
if not package_carriers:
# Original behavior
return super().send_to_shipper()
tracking_numbers = []
carrier_prices = []
order_currency = self.sale_id.currency_id or self.company_id.currency_id
for carrier in package_carriers:
self.carrier_id = carrier
carrier_packages = packages.filtered(lambda p: p.carrier_id == carrier)
res = carrier.send_shipping(self)
if res:
res = res[0]
if carrier.free_over and self.sale_id and self.sale_id._compute_amount_total_without_delivery() >= carrier.amount:
res['exact_price'] = 0.0
carrier_price = float(res['exact_price']) * (1.0 + (self.carrier_id.margin / 100.0))
carrier_prices.append(carrier_price)
tracking_number = ''
if res['tracking_number']:
tracking_number = res['tracking_number']
tracking_numbers.append(tracking_number)
# Try to add tracking to the individual packages.
potential_tracking_numbers = tracking_number.split(',')
if len(potential_tracking_numbers) == 1:
potential_tracking_numbers = tracking_number.split('+') # UPS for example...
if len(potential_tracking_numbers) >= len(carrier_packages):
for t, p in zip(potential_tracking_numbers, carrier_packages):
p.carrier_tracking_ref = t
else:
carrier_packages.write({'carrier_tracking_ref': tracking_number})
msg = _("Shipment sent to carrier %s for shipping with tracking number %s<br/>Cost: %.2f %s") % (carrier.name, tracking_number, carrier_price, order_currency.name)
self.message_post(body=msg)
self.carrier_price = sum(carrier_prices or [0.0])
self.carrier_tracking_ref = ','.join(tracking_numbers or [''])
# Override to provide per-package versions...
def cancel_shipment(self):
packages = self._context.get('packages')
pickings_with_package_tracking = self.filtered(lambda p: p.package_carrier_tracking_ref)
for picking in pickings_with_package_tracking:
if packages:
current_packages = packages
else:
current_packages = picking.package_ids
# Packages without a carrier can just be cleared
packages_without_carrier = current_packages.filtered(lambda p: not p.carrier_id and p.carrier_tracking_ref)
packages_without_carrier.write({
'carrier_tracking_ref': False,
})
# Packages with carrier can use the carrier method
packages_with_carrier = current_packages.filtered(lambda p: p.carrier_id and p.carrier_tracking_ref)
carriers = packages_with_carrier.mapped('carrier_id')
for carrier in carriers:
carrier_packages = packages_with_carrier.filtered(lambda p: p.carrier_id == carrier)
carrier.cancel_shipment(self, packages=carrier_packages)
package_refs = ','.join(carrier_packages.mapped('carrier_tracking_ref'))
msg = "Shipment %s cancelled" % package_refs
picking.message_post(body=msg)
carrier_packages.write({'carrier_tracking_ref': False})
pickings_without_package_tracking = self - pickings_with_package_tracking
if pickings_without_package_tracking:
# use original on these
super(StockPicking, pickings_without_package_tracking).cancel_shipment()
class StockMove(models.Model): class StockMove(models.Model):

View File

@@ -24,12 +24,14 @@ class TestDeliveryHibou(common.TransactionCase):
def test_delivery_hibou(self): def test_delivery_hibou(self):
# Assign a new shipping account # Assign a new shipping account
self.partner.shipping_account_id = self.shipping_account self.partner.shipping_account_ids = self.shipping_account
# Assign values to new Carrier # Assign values to new Carrier
test_insurance_value = 600 test_insurance_value = 600
test_procurement_priority = '2' test_sig_req_value = 300
test_procurement_priority = '1'
self.carrier.automatic_insurance_value = test_insurance_value self.carrier.automatic_insurance_value = test_insurance_value
self.carrier.automatic_sig_req_value = test_sig_req_value
self.carrier.procurement_priority = test_procurement_priority self.carrier.procurement_priority = test_procurement_priority
@@ -71,7 +73,9 @@ class TestDeliveryHibou(common.TransactionCase):
def test_carrier_hibou_out(self): def test_carrier_hibou_out(self):
test_insurance_value = 4000 test_insurance_value = 4000
test_sig_req_value = 4000
self.carrier.automatic_insurance_value = test_insurance_value self.carrier.automatic_insurance_value = test_insurance_value
self.carrier.automatic_sig_req_value = test_sig_req_value
picking_out = self.env.ref('stock.outgoing_shipment_main_warehouse') picking_out = self.env.ref('stock.outgoing_shipment_main_warehouse')
self.assertEqual(picking_out.state, 'assigned') self.assertEqual(picking_out.state, 'assigned')
@@ -87,21 +91,29 @@ class TestDeliveryHibou(common.TransactionCase):
# The 'value' is assumed to be all of the product value from the initial demand. # The 'value' is assumed to be all of the product value from the initial demand.
self.assertEqual(picking_out.declared_value(), 15.0 * 3300.0) self.assertEqual(picking_out.declared_value(), 15.0 * 3300.0)
self.assertEqual(picking_out.carrier_id.get_insurance_value(picking=picking_out), picking_out.declared_value()) self.assertEqual(picking_out.carrier_id.get_insurance_value(picking=picking_out), picking_out.declared_value())
self.assertTrue(picking_out.carrier_id.get_signature_required(picking=picking_out))
# Workflow where user explicitly opts out of insurance on the picking level. # Workflow where user explicitly opts out of insurance on the picking level.
picking_out.require_insurance = 'no' picking_out.require_insurance = 'no'
picking_out.require_signature = 'no'
self.assertEqual(picking_out.carrier_id.get_insurance_value(picking=picking_out), 0.0) self.assertEqual(picking_out.carrier_id.get_insurance_value(picking=picking_out), 0.0)
self.assertFalse(picking_out.carrier_id.get_signature_required(picking=picking_out))
picking_out.require_insurance = 'auto' picking_out.require_insurance = 'auto'
picking_out.require_signature = 'auto'
# Lets choose to only delivery one piece at the moment. # Lets choose to only delivery one piece at the moment.
# This does not meet the minimum on the carrier to have insurance value. # This does not meet the minimum on the carrier to have insurance value.
picking_out.move_line_ids.qty_done = 1.0 picking_out.move_line_ids.qty_done = 1.0
self.assertEqual(picking_out.declared_value(), 3300.0) self.assertEqual(picking_out.declared_value(), 3300.0)
self.assertEqual(picking_out.carrier_id.get_insurance_value(picking=picking_out), 0.0) self.assertEqual(picking_out.carrier_id.get_insurance_value(picking=picking_out), 0.0)
self.assertFalse(picking_out.carrier_id.get_signature_required(picking=picking_out))
# Workflow where user opts in to insurance. # Workflow where user opts in to insurance.
picking_out.require_insurance = 'yes' picking_out.require_insurance = 'yes'
picking_out.require_signature = 'yes'
self.assertEqual(picking_out.carrier_id.get_insurance_value(picking=picking_out), 3300.0) self.assertEqual(picking_out.carrier_id.get_insurance_value(picking=picking_out), 3300.0)
self.assertTrue(picking_out.carrier_id.get_signature_required(picking=picking_out))
picking_out.require_insurance = 'auto' picking_out.require_insurance = 'auto'
picking_out.require_signature = 'auto'
# Test with picking having 3rd party account. # Test with picking having 3rd party account.
self.assertEqual(picking_out.carrier_id.get_third_party_account(picking=picking_out), None) self.assertEqual(picking_out.carrier_id.get_third_party_account(picking=picking_out), None)

View File

@@ -7,8 +7,26 @@
<field name="arch" type="xml"> <field name="arch" type="xml">
<xpath expr="//field[@name='integration_level']" position="after"> <xpath expr="//field[@name='integration_level']" position="after">
<field name="automatic_insurance_value"/> <field name="automatic_insurance_value"/>
<field name="automatic_sig_req_value"/>
<field name="procurement_priority"/> <field name="procurement_priority"/>
</xpath> </xpath>
</field> </field>
</record> </record>
<record id="choose_delivery_package_view_form" model="ir.ui.view">
<field name="name">hibou.choose.delivery.package.form</field>
<field name="model">choose.delivery.package</field>
<field name="inherit_id" ref="delivery.choose_delivery_package_view_form" />
<field name="arch" type="xml">
<xpath expr="//field[@name='delivery_packaging_id']" position="attributes">
<attribute name="domain">[('product_id', '=', False)]</attribute>
</xpath>
<xpath expr="//field[@name='delivery_packaging_id']" position="after">
<field name="package_declared_value" />
<field name="package_require_insurance" />
<field name="package_require_signature" />
</xpath>
</field>
</record>
</odoo> </odoo>

View File

@@ -1,14 +1,52 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<odoo> <odoo>
<record id="hibou_view_quant_package_form" model="ir.ui.view">
<field name="name">hibou.stock.quant.package.form</field>
<field name="model">stock.quant.package</field>
<field name="inherit_id" ref="stock.view_quant_package_form" />
<field name="arch" type="xml">
<xpath expr="//field[@name='location_id']" position="after">
<label for="carrier_id"/>
<div name="carrier">
<field name="carrier_id" class="oe_inline"/>
<button type="object" class="fa fa-arrow-right oe_link" name="send_to_shipper" string="Ship" attrs="{'invisible':['|',('carrier_tracking_ref','!=',False),('carrier_id','=', False)]}"/>
</div>
<label for="carrier_tracking_ref"/>
<div name="tracking">
<field name="carrier_tracking_ref" class="oe_inline" />
<button type="object" class="fa fa-arrow-right oe_link" name="cancel_shipment" string="Cancel" attrs="{'invisible':['|',('carrier_tracking_ref','=',False),('carrier_id','=', False)]}"/>
</div>
<field name="declared_value" />
<field name="require_insurance" />
<field name="require_signature" />
</xpath>
</field>
</record>
<record id="view_picking_withcarrier_out_form" model="ir.ui.view"> <record id="view_picking_withcarrier_out_form" model="ir.ui.view">
<field name="name">hibou.delivery.stock.picking_withcarrier.form.view</field> <field name="name">hibou.delivery.stock.picking_withcarrier.form.view</field>
<field name="model">stock.picking</field> <field name="model">stock.picking</field>
<field name="inherit_id" ref="delivery.view_picking_withcarrier_out_form" /> <field name="inherit_id" ref="delivery.view_picking_withcarrier_out_form" />
<field name="priority" eval="200" />
<field name="arch" type="xml"> <field name="arch" type="xml">
<xpath expr="//field[@name='carrier_id']" position="before"> <xpath expr="//field[@name='carrier_id']" position="before">
<field name="require_insurance" attrs="{'readonly': [('state', 'in', ('done', 'cancel'))]}"/> <field name="require_insurance" attrs="{'readonly': [('state', 'in', ('done', 'cancel'))]}"/>
<field name="shipping_account_id" attrs="{'readonly': [('state', 'in', ('done', 'cancel'))]}"/> <field name="require_signature" attrs="{'readonly': [('state', 'in', ('done', 'cancel'))]}"/>
<field name="shipping_account_id" attrs="{'readonly': [('state', 'in', ('done', 'cancel'))]}"
options="{'no_create': True, 'no_open': True}"
domain="['|', ('partner_id', '=', False), ('partner_id', '=', partner_id)]"/>
<field name="package_carrier_tracking_ref" attrs="{'invisible': [('package_carrier_tracking_ref', '=', False)]}" />
<button name="clear_carrier_tracking_ref" type="object" string="Clear Tracking" attrs="{'invisible': [('carrier_tracking_ref', '!=', False)]}" />
<button name="reset_carrier_tracking_ref" type="object" string="Reset Tracking" attrs="{'invisible': [('package_carrier_tracking_ref', '!=', False)]}" />
<field name="package_ids" attrs="{'invisible': [('package_carrier_tracking_ref', '=', False)]}" nolabel="1" colspan="2">
<tree>
<field name="name" />
<field name="carrier_id" />
<field name="carrier_tracking_ref" />
<button type="object" name="cancel_shipment" string="Cancel" context="{'picking_active_id': parent.id, 'picking_active_model': 'stock.picking'}" />
</tree>
</field>
</xpath> </xpath>
</field> </field>
</record> </record>

View File

@@ -1 +1,3 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
from . import models from . import models

View File

@@ -1,10 +1,12 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
{ {
'name': 'Stamps.com (USPS) Shipping', 'name': 'Stamps.com (USPS) Shipping',
'summary': 'Send your shippings through Stamps.com and track them online.', 'summary': 'Send your shippings through Stamps.com and track them online.',
'version': '11.0.1.0.0', 'version': '11.0.2.1.1',
'author': "Hibou Corp.", 'author': "Hibou Corp.",
'category': 'Warehouse', 'category': 'Warehouse',
'license': 'AGPL-3', 'license': 'OPL-1',
'images': [], 'images': [],
'website': "https://hibou.io", 'website': "https://hibou.io",
'description': """ 'description': """
@@ -15,7 +17,6 @@ Send your shippings through Stamps.com and track them online.
""", """,
'depends': [ 'depends': [
'delivery',
'delivery_hibou', 'delivery_hibou',
], ],
'demo': [], 'demo': [],

View File

@@ -1 +1,3 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
from . import delivery_stamps from . import delivery_stamps

View File

@@ -1,3 +1,4 @@
Copyright (c) 2019 by Hibou Corp.
Copyright (c) 2014 by Jonathan Zempel. Copyright (c) 2014 by Jonathan Zempel.
Some rights reserved. Some rights reserved.

View File

@@ -15,7 +15,7 @@ from urllib.parse import urljoin
import os import os
VERSION = 84 VERSION = 111
class StampsConfiguration(object): class StampsConfiguration(object):

View File

@@ -145,12 +145,12 @@ class StampsService(BaseService):
def create_add_on(self): def create_add_on(self):
"""Create a new add-on object. """Create a new add-on object.
""" """
return self.create("AddOnV15") return self.create("AddOnV17")
def create_customs(self): def create_customs(self):
"""Create a new customs object. """Create a new customs object.
""" """
return self.create("CustomsV3") return self.create("CustomsV7")
def create_array_of_customs_lines(self): def create_array_of_customs_lines(self):
"""Create a new array of customs objects. """Create a new array of customs objects.
@@ -188,7 +188,7 @@ class StampsService(BaseService):
def create_shipping(self): def create_shipping(self):
"""Create a new shipping object. """Create a new shipping object.
""" """
return self.create("RateV31") return self.create("RateV40")
def get_address(self, address): def get_address(self, address):
"""Get a shipping address. """Get a shipping address.
@@ -202,7 +202,7 @@ class StampsService(BaseService):
""" """
return self.call("GetAccountInfo") return self.call("GetAccountInfo")
def get_label(self, from_address, to_address, rate, transaction_id, image_type=None, def get_label(self, rate, transaction_id, image_type=None,
customs=None, sample=False, extended_postage_info=False): customs=None, sample=False, extended_postage_info=False):
"""Get a shipping label. """Get a shipping label.
@@ -215,7 +215,7 @@ class StampsService(BaseService):
:param sample: Default ``False``. Get a sample label without postage. :param sample: Default ``False``. Get a sample label without postage.
""" """
return self.call("CreateIndicium", IntegratorTxID=transaction_id, return self.call("CreateIndicium", IntegratorTxID=transaction_id,
Rate=rate, From=from_address, To=to_address, ImageType=image_type, Customs=customs, Rate=rate, ImageType=image_type, Customs=customs,
SampleOnly=sample, ExtendedPostageInfo=extended_postage_info) SampleOnly=sample, ExtendedPostageInfo=extended_postage_info)
def get_postage_status(self, transaction_id): def get_postage_status(self, transaction_id):

View File

@@ -1,3 +1,6 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
import hashlib
from datetime import date from datetime import date
from logging import getLogger from logging import getLogger
from urllib.request import urlopen from urllib.request import urlopen
@@ -31,6 +34,13 @@ STAMPS_PACKAGE_TYPES = [
'Regional Rate Box C', 'Regional Rate Box C',
] ]
STAMPS_CONTENT_TYPES = {
'Letter': 'Document',
'Postcard': 'Document',
}
STAMPS_INTEGRATION_ID = 'f62cb4f0-aa07-4701-a1dd-f0e7c853ed3c'
class ProductPackaging(models.Model): class ProductPackaging(models.Model):
_inherit = 'product.packaging' _inherit = 'product.packaging'
@@ -44,12 +54,14 @@ class ProviderStamps(models.Model):
delivery_type = fields.Selection(selection_add=[('stamps', 'Stamps.com (USPS)')]) delivery_type = fields.Selection(selection_add=[('stamps', 'Stamps.com (USPS)')])
stamps_integration_id = fields.Char(string='Stamps.com Integration ID', groups='base.group_system')
stamps_username = fields.Char(string='Stamps.com Username', groups='base.group_system') stamps_username = fields.Char(string='Stamps.com Username', groups='base.group_system')
stamps_password = fields.Char(string='Stamps.com Password', groups='base.group_system') stamps_password = fields.Char(string='Stamps.com Password', groups='base.group_system')
stamps_service_type = fields.Selection([('US-FC', 'First-Class'), stamps_service_type = fields.Selection([('US-FC', 'First-Class'),
('US-FCI', 'First-Class International'),
('US-PM', 'Priority'), ('US-PM', 'Priority'),
('US-PMI', 'Priority Mail International'),
('US-EMI', ' Priority Mail Express International'),
], ],
required=True, string="Service Type", default="US-PM") required=True, string="Service Type", default="US-PM")
stamps_default_packaging_id = fields.Many2one('product.packaging', string='Default Package Type') stamps_default_packaging_id = fields.Many2one('product.packaging', string='Default Package Type')
@@ -67,12 +79,21 @@ class ProviderStamps(models.Model):
('BZpl', 'BZPL'), ('BZpl', 'BZPL'),
], ],
required=True, string="Image Type", default="Pdf") required=True, string="Image Type", default="Pdf")
stamps_addon_sc = fields.Boolean(string='Add Signature Confirmation')
stamps_addon_dc = fields.Boolean(string='Add Delivery Confirmation')
stamps_addon_hp = fields.Boolean(string='Add Hidden Postage')
def _stamps_package_type(self, package=None): def _stamps_package_type(self, package=None):
if not package: if not package:
return self.stamps_default_packaging_id.shipper_package_code return self.stamps_default_packaging_id.shipper_package_code
return package.packaging_id.shipper_package_code if package.packaging_id.shipper_package_code in STAMPS_PACKAGE_TYPES else 'Package' return package.packaging_id.shipper_package_code if package.packaging_id.shipper_package_code in STAMPS_PACKAGE_TYPES else 'Package'
def _stamps_content_type(self, package=None):
package_type = self._stamps_package_type(package=package)
if package_type in STAMPS_CONTENT_TYPES:
return STAMPS_CONTENT_TYPES[package_type]
return 'Merchandise'
def _stamps_package_is_cubic_pricing(self, package=None): def _stamps_package_is_cubic_pricing(self, package=None):
if not package: if not package:
return self.stamps_default_packaging_id.stamps_cubic_pricing return self.stamps_default_packaging_id.stamps_cubic_pricing
@@ -87,15 +108,21 @@ class ProviderStamps(models.Model):
def _get_stamps_service(self): def _get_stamps_service(self):
sudoself = self.sudo() sudoself = self.sudo()
config = StampsConfiguration(integration_id=sudoself.stamps_integration_id, config = StampsConfiguration(integration_id=STAMPS_INTEGRATION_ID,
username=sudoself.stamps_username, username=sudoself.stamps_username,
password=sudoself.stamps_password) password=sudoself.stamps_password,
wsdl=('testing' if not sudoself.prod_environment else None))
return StampsService(configuration=config) return StampsService(configuration=config)
def _stamps_convert_weight(self, weight): def _stamps_convert_weight(self, weight):
""" weight always expressed in KG """ """ weight always expressed in database units (KG/LBS) """
if self.stamps_default_packaging_id.max_weight and self.stamps_default_packaging_id.max_weight < weight: if self.stamps_default_packaging_id.max_weight and self.stamps_default_packaging_id.max_weight < weight:
raise ValidationError('Stamps cannot ship for weight: ' + str(weight) + 'kgs.') raise ValidationError('Stamps cannot ship for weight: ' + str(weight) + ' kgs/lbs.')
get_param = self.env['ir.config_parameter'].sudo().get_param
product_weight_in_lbs_param = get_param('product.weight_in_lbs')
if product_weight_in_lbs_param == '1':
return weight
weight_in_pounds = weight * 2.20462 weight_in_pounds = weight * 2.20462
return weight_in_pounds return weight_in_pounds
@@ -108,12 +135,37 @@ class ProviderStamps(models.Model):
raise ValidationError('Stamps needs ZIP. From: ' + str(order.warehouse_id.partner_id.zip) + ' To: ' + str(order.partner_shipping_id.zip)) raise ValidationError('Stamps needs ZIP. From: ' + str(order.warehouse_id.partner_id.zip) + ' To: ' + str(order.partner_shipping_id.zip))
ret_val = service.create_shipping() ret_val = service.create_shipping()
ret_val.ShipDate = date_planned.split()[0] if date_planned else date.today().isoformat() ret_val.ShipDate = date_planned.split(' ')[0] if date_planned else date.today().isoformat()
ret_val.FromZIPCode = self.get_shipper_warehouse(order=order).zip.split('-')[0] shipper_partner = self.get_shipper_warehouse(order=order)
ret_val.ToZIPCode = order.partner_shipping_id.zip.split('-')[0] ret_val.From = self._stamps_address(service, shipper_partner)
ret_val.To = self._stamps_address(service, order.partner_shipping_id)
ret_val.PackageType = self._stamps_package_type() ret_val.PackageType = self._stamps_package_type()
ret_val.ServiceType = self.stamps_service_type ret_val.ServiceType = self.stamps_service_type
ret_val.WeightLb = weight ret_val.WeightLb = weight
ret_val.ContentType = self._stamps_content_type()
return ret_val
def _get_stamps_shipping_multi(self, service, date_planned, order=False, picking=False, package=False):
if order:
weight = sum([(line.product_id.weight * line.product_qty) for line in order.order_line]) or 0.0
elif not package:
weight = picking.shipping_weight
else:
weight = package.shipping_weight or package.weight
weight = self._stamps_convert_weight(weight)
shipper = self.get_shipper_warehouse(order=order, picking=picking)
recipient = self.get_recipient(order=order, picking=picking)
if not all((shipper.zip, recipient.zip)):
raise ValidationError('Stamps needs ZIP. From: ' + str(shipper.zip) + ' To: ' + str(recipient.zip))
ret_val = service.create_shipping()
ret_val.ShipDate = date_planned.split(' ')[0] if date_planned else date.today().isoformat()
ret_val.From = self._stamps_address(service, shipper)
ret_val.To = self._stamps_address(service, recipient)
ret_val.PackageType = self._stamps_package_type(package=package)
ret_val.WeightLb = weight
ret_val.ContentType = 'Merchandise' ret_val.ContentType = 'Merchandise'
return ret_val return ret_val
@@ -123,20 +175,51 @@ class ProviderStamps(models.Model):
to = self.get_recipient(picking=picking) to = self.get_recipient(picking=picking)
return company, from_, to return company, from_, to
def _stamps_address(self, service, partner):
address = service.create_address()
if not partner.name or len(partner.name) < 2:
raise ValidationError('Partner (%s) name must be more than 2 characters.' % (partner, ))
address.FullName = partner.name
address.Address1 = partner.street
if partner.street2:
address.Address2 = partner.street2
address.City = partner.city
address.State = partner.state_id.code
if partner.country_id.code == 'US':
zip_pieces = partner.zip.split('-')
address.ZIPCode = zip_pieces[0]
if len(zip_pieces) >= 2:
address.ZIPCodeAddOn = zip_pieces[1]
else:
address.PostalCode = partner.zip or ''
address.Country = partner.country_id.code
res = service.get_address(address).Address
return res
def _stamps_hash_partner(self, partner):
to_hash = ''.join(f[1] if isinstance(f, tuple) else str(f) for f in partner.read(['name', 'street', 'street2', 'city', 'country_id', 'state_id', 'zip', 'phone', 'email'])[0].values())
return hashlib.sha1(to_hash.encode()).hexdigest()
def _stamps_get_shippings_for_picking(self, service, picking): def _stamps_get_shippings_for_picking(self, service, picking):
ret = [] ret = []
company, from_partner, to_partner = self._stamps_get_addresses_for_picking(picking) company, from_partner, to_partner = self._stamps_get_addresses_for_picking(picking)
if not all((from_partner.zip, to_partner.zip)): if not all((from_partner.zip, to_partner.zip)):
raise ValidationError('Stamps needs ZIP. From: ' + str(from_partner.zip) + ' To: ' + str(to_partner.zip)) raise ValidationError('Stamps needs ZIP/PostalCode. From: ' + str(from_partner.zip) + ' To: ' + str(to_partner.zip))
for package in picking.package_ids: picking_packages = picking.package_ids
package_carriers = picking_packages.mapped('carrier_id')
if package_carriers:
# only ship ours
picking_packages = picking_packages.filtered(lambda p: p.carrier_id == self and not p.carrier_tracking_ref)
for package in picking_packages:
weight = self._stamps_convert_weight(package.shipping_weight) weight = self._stamps_convert_weight(package.shipping_weight)
l, w, h = self._stamps_package_dimensions(package=package) l, w, h = self._stamps_package_dimensions(package=package)
ret_val = service.create_shipping() ret_val = service.create_shipping()
ret_val.ShipDate = date.today().isoformat() ret_val.ShipDate = date.today().isoformat()
ret_val.FromZIPCode = from_partner.zip.split('-')[0] ret_val.From = self._stamps_address(service, from_partner)
ret_val.ToZIPCode = to_partner.zip.split('-')[0] ret_val.To = self._stamps_address(service, to_partner)
ret_val.PackageType = self._stamps_package_type(package=package) ret_val.PackageType = self._stamps_package_type(package=package)
ret_val.CubicPricing = self._stamps_package_is_cubic_pricing(package=package) ret_val.CubicPricing = self._stamps_package_is_cubic_pricing(package=package)
ret_val.Length = l ret_val.Length = l
@@ -144,16 +227,16 @@ class ProviderStamps(models.Model):
ret_val.Height = h ret_val.Height = h
ret_val.ServiceType = self.stamps_service_type ret_val.ServiceType = self.stamps_service_type
ret_val.WeightLb = weight ret_val.WeightLb = weight
ret_val.ContentType = 'Merchandise' ret_val.ContentType = self._stamps_content_type(package=package)
ret.append((package.name + ret_val.ShipDate + str(ret_val.WeightLb), ret_val)) ret.append((package.name + ret_val.ShipDate + str(ret_val.WeightLb) + self._stamps_hash_partner(to_partner), ret_val))
if not ret: if not ret and not package_carriers:
weight = self._stamps_convert_weight(picking.shipping_weight) weight = self._stamps_convert_weight(picking.shipping_weight)
l, w, h = self._stamps_package_dimensions() l, w, h = self._stamps_package_dimensions()
ret_val = service.create_shipping() ret_val = service.create_shipping()
ret_val.ShipDate = date.today().isoformat() ret_val.ShipDate = date.today().isoformat()
ret_val.FromZIPCode = from_partner.zip.split('-')[0] ret_val.From = self._stamps_address(service, from_partner)
ret_val.ToZIPCode = to_partner.zip.split('-')[0] ret_val.To = self._stamps_address(service, to_partner)
ret_val.PackageType = self._stamps_package_type() ret_val.PackageType = self._stamps_package_type()
ret_val.CubicPricing = self._stamps_package_is_cubic_pricing() ret_val.CubicPricing = self._stamps_package_is_cubic_pricing()
ret_val.Length = l ret_val.Length = l
@@ -161,8 +244,8 @@ class ProviderStamps(models.Model):
ret_val.Height = h ret_val.Height = h
ret_val.ServiceType = self.stamps_service_type ret_val.ServiceType = self.stamps_service_type
ret_val.WeightLb = weight ret_val.WeightLb = weight
ret_val.ContentType = 'Merchandise' ret_val.ContentType = self._stamps_content_type()
ret.append((picking.name + ret_val.ShipDate + str(ret_val.WeightLb), ret_val)) ret.append((picking.name + ret_val.ShipDate + str(ret_val.WeightLb) + self._stamps_hash_partner(to_partner), ret_val))
return ret return ret
@@ -224,6 +307,9 @@ class ProviderStamps(models.Model):
return result return result
return result return result
def _stamps_needs_customs(self, from_partner, to_partner):
return from_partner.country_id.code != to_partner.country_id.code
def stamps_send_shipping(self, pickings): def stamps_send_shipping(self, pickings):
res = [] res = []
service = self._get_stamps_service() service = self._get_stamps_service()
@@ -232,33 +318,13 @@ class ProviderStamps(models.Model):
package_labels = [] package_labels = []
shippings = self._stamps_get_shippings_for_picking(service, picking) shippings = self._stamps_get_shippings_for_picking(service, picking)
if not shippings:
continue
company, from_partner, to_partner = self._stamps_get_addresses_for_picking(picking) company, from_partner, to_partner = self._stamps_get_addresses_for_picking(picking)
from_address = service.create_address() customs = None
from_address.FullName = company.name if self._stamps_needs_customs(from_partner, to_partner):
from_address.Address1 = from_partner.street customs = service.create_customs()
if from_partner.street2:
from_address.Address2 = from_partner.street2
from_address.City = from_partner.city
from_address.State = from_partner.state_id.code
from_zip_pieces = from_partner.zip.split('-')
from_address.ZIPCode = from_zip_pieces[0]
if len(from_zip_pieces) >= 2:
from_address.ZIPCodeAddOn = from_zip_pieces[1]
from_address = service.get_address(from_address).Address
to_address = service.create_address()
to_address.FullName = to_partner.name
to_address.Address1 = to_partner.street
if to_partner.street2:
to_address.Address2 = to_partner.street2
to_address.City = to_partner.city
to_address.State = to_partner.state_id.code
to_zip_pieces = to_partner.zip.split('-')
to_address.ZIPCode = to_zip_pieces[0]
if len(to_zip_pieces) >= 2:
to_address.ZIPCodeAddOn = to_zip_pieces[1]
to_address = service.get_address(to_address).Address
try: try:
for txn_id, shipping in shippings: for txn_id, shipping in shippings:
@@ -270,20 +336,78 @@ class ProviderStamps(models.Model):
shipping.DeliverDays = rate.DeliverDays shipping.DeliverDays = rate.DeliverDays
if hasattr(rate, 'DimWeighting'): if hasattr(rate, 'DimWeighting'):
shipping.DimWeighting = rate.DimWeighting shipping.DimWeighting = rate.DimWeighting
shipping.Zone = rate.Zone
shipping.RateCategory = rate.RateCategory shipping.RateCategory = rate.RateCategory
shipping.ToState = rate.ToState # shipping.ToState = rate.ToState
addons = []
if self.stamps_addon_sc:
add_on = service.create_add_on()
add_on.AddOnType = 'US-A-SC'
addons.append(add_on)
if self.stamps_addon_dc:
add_on = service.create_add_on() add_on = service.create_add_on()
add_on.AddOnType = 'US-A-DC' add_on.AddOnType = 'US-A-DC'
add_on2 = service.create_add_on() addons.append(add_on)
add_on2.AddOnType = 'SC-A-HP' if self.stamps_addon_hp:
shipping.AddOns.AddOnV15 = [add_on, add_on2] add_on = service.create_add_on()
add_on.AddOnType = 'SC-A-HP'
addons.append(add_on)
shipping.AddOns.AddOnV17 = addons
extended_postage_info = service.create_extended_postage_info() extended_postage_info = service.create_extended_postage_info()
if self.is_amazon(picking=picking): if self.is_amazon(picking=picking):
extended_postage_info.bridgeProfileType = 'Amazon MWS' extended_postage_info.bridgeProfileType = 'Amazon MWS'
label = service.get_label(from_address, to_address, shipping,
if customs:
customs.ContentType = shipping.ContentType
if not picking.package_ids:
raise ValidationError('Cannot use customs without packing items to ship first.')
customs_total = 0.0
product_values = {}
# Note multiple packages will result in all product being on customs form.
# Recommended to ship one customs international package at a time.
for quant in picking.mapped('package_ids.quant_ids'):
# Customs should have the price for the destination but we may not be able
# to rely on the price from the SO (e.g. kit BoM)
product = quant.product_id
quantity = quant.quantity
price = product.lst_price
if to_partner.property_product_pricelist:
# Note the quantity is used for the price, but it is per unit
price = to_partner.property_product_pricelist.get_product_price(product, quantity, to_partner)
if product not in product_values:
product_values[product] = {
'quantity': 0.0,
'value': 0.0,
}
product_values[product]['quantity'] += quantity
product_values[product]['value'] += price * quantity
# Note that Stamps will not allow you to use the scale weight if it is not equal
# to the sum of the customs lines.
# Thus we sum the line
new_total_weight = 0.0
customs_lines = []
for product, values in product_values.items():
customs_line = service.create_customs_lines()
customs_line.Description = product.name
customs_line.Quantity = values['quantity']
customs_total += round(values['value'], 2)
customs_line.Value = round(values['value'], 2)
line_weight = round(self._stamps_convert_weight(product.weight * values['quantity']), 2)
customs_line.WeightLb = line_weight
new_total_weight += line_weight
customs_line.HSTariffNumber = product.hs_code or ''
# customs_line.CountryOfOrigin =
customs_line.sku = product.default_code or ''
customs_lines.append(customs_line)
customs.CustomsLines.CustomsLine = customs_lines
shipping.DeclaredValue = round(customs_total, 2)
shipping.WeightLb = round(new_total_weight, 2)
label = service.get_label(shipping,
transaction_id=txn_id, image_type=self.stamps_image_type, transaction_id=txn_id, image_type=self.stamps_image_type,
extended_postage_info=extended_postage_info) extended_postage_info=extended_postage_info,
customs=customs)
package_labels.append((txn_id, label)) package_labels.append((txn_id, label))
except WebFault as e: except WebFault as e:
_logger.warn(e) _logger.warn(e)
@@ -307,9 +431,13 @@ class ProviderStamps(models.Model):
carrier_price += float(label.Rate.Amount) carrier_price += float(label.Rate.Amount)
url = label.URL url = label.URL
url_spaces = url.split(' ')
attachments = []
for i, url in enumerate(url_spaces, 1):
response = urlopen(url) response = urlopen(url)
attachment = response.read() attachment = response.read()
picking.message_post(body=body, attachments=[('LabelStamps-%s.%s' % (label.TrackingNumber, self.stamps_image_type), attachment)]) attachments.append(('LabelStamps-%s-%s.%s' % (label.TrackingNumber, i, self.stamps_image_type), attachment))
picking.message_post(body=body, attachments=attachments)
shipping_data = {'exact_price': carrier_price, 'tracking_number': ','.join(tracking_numbers)} shipping_data = {'exact_price': carrier_price, 'tracking_number': ','.join(tracking_numbers)}
res = res + [shipping_data] res = res + [shipping_data]
return res return res
@@ -324,9 +452,81 @@ class ProviderStamps(models.Model):
def stamps_cancel_shipment(self, picking): def stamps_cancel_shipment(self, picking):
service = self._get_stamps_service() service = self._get_stamps_service()
try: try:
service.remove_label(picking.carrier_tracking_ref) all_tracking = picking.carrier_tracking_ref
for tracking in all_tracking.split(','):
service.remove_label(tracking.strip())
picking.message_post(body=_(u'Shipment N° %s has been cancelled' % picking.carrier_tracking_ref)) picking.message_post(body=_(u'Shipment N° %s has been cancelled' % picking.carrier_tracking_ref))
picking.write({'carrier_tracking_ref': '', picking.write({'carrier_tracking_ref': '',
'carrier_price': 0.0}) 'carrier_price': 0.0})
except WebFault as e: except WebFault as e:
raise ValidationError(e) raise ValidationError(e)
def stamps_rate_shipment_multi(self, order=None, picking=None, packages=None):
if not packages:
return self._stamps_rate_shipment_multi_package(order=order, picking=picking)
else:
rates = []
for package in packages:
rates += self._stamps_rate_shipment_multi_package(order=order, picking=picking, package=package)
return rates
def _stamps_rate_shipment_multi_package(self, order=None, picking=None, package=None):
self.ensure_one()
date_planned = fields.Datetime.now()
if self.env.context.get('date_planned'):
date_planned = self.env.context.get('date_planned')
res = []
service = self._get_stamps_service()
shipping = self._get_stamps_shipping_multi(service, date_planned, order=order, picking=picking, package=package)
rates = service.get_rates(shipping)
for rate in rates:
price = float(rate.Amount)
if order:
currency = order.currency_id
else:
currency = picking.sale_id.currency_id if picking.sale_id else picking.company_id.currency_id
if currency.name != 'USD':
quote_currency = self.env['res.currency'].search([('name', '=', 'USD')], limit=1)
price = quote_currency.compute(rate.Amount, currency)
delivery_days = rate.DeliverDays
if delivery_days.find('-') >= 0:
delivery_days = delivery_days.split('-')
transit_days = int(delivery_days[-1])
else:
transit_days = int(delivery_days)
date_delivered = None
if transit_days > 0:
date_delivered = self.calculate_date_delivered(date_planned, transit_days)
service_code = rate.ServiceType
carrier = self.stamps_find_delivery_carrier_for_service(service_code)
if carrier:
res.append({
'carrier': carrier,
'package': package or self.env['stock.quant.package'].browse(),
'success': True,
'price': price,
'error_message': False,
'warning_message': False,
'transit_days': transit_days,
'date_delivered': date_delivered,
'date_planned': date_planned,
'service_code': service_code,
})
if not res:
res.append({
'success': False,
'price': 0.0,
'error_message': 'No valid rates returned from Stamps.com',
'warning_message': False
})
return res
def stamps_find_delivery_carrier_for_service(self, service_code):
if self.stamps_service_type == service_code:
return self
# arbitrary decision, lets find the same user name
carrier = self.search([('stamps_username', '=', self.stamps_username),
('stamps_service_type', '=', service_code)
], limit=1)
return carrier

View File

@@ -10,7 +10,6 @@
<page string="Stamps.com Configuration" attrs="{'invisible': [('delivery_type', '!=', 'stamps')]}"> <page string="Stamps.com Configuration" attrs="{'invisible': [('delivery_type', '!=', 'stamps')]}">
<group> <group>
<group> <group>
<field name="stamps_integration_id" attrs="{'required': [('delivery_type', '=', 'stamps')]}" />
<field name="stamps_username" attrs="{'required': [('delivery_type', '=', 'stamps')]}" /> <field name="stamps_username" attrs="{'required': [('delivery_type', '=', 'stamps')]}" />
<field name="stamps_password" attrs="{'required': [('delivery_type', '=', 'stamps')]}" password="True"/> <field name="stamps_password" attrs="{'required': [('delivery_type', '=', 'stamps')]}" password="True"/>
</group> </group>
@@ -18,6 +17,9 @@
<field name="stamps_service_type" attrs="{'required': [('delivery_type', '==', 'stamps')]}"/> <field name="stamps_service_type" attrs="{'required': [('delivery_type', '==', 'stamps')]}"/>
<field name="stamps_default_packaging_id" attrs="{'required': [('delivery_type', '==', 'stamps')]}"/> <field name="stamps_default_packaging_id" attrs="{'required': [('delivery_type', '==', 'stamps')]}"/>
<field name="stamps_image_type" attrs="{'required': [('delivery_type', '==', 'stamps')]}"/> <field name="stamps_image_type" attrs="{'required': [('delivery_type', '==', 'stamps')]}"/>
<field name="stamps_addon_sc"/>
<field name="stamps_addon_dc"/>
<field name="stamps_addon_hp"/>
</group> </group>
</group> </group>
</page> </page>
@@ -30,7 +32,7 @@
<field name="model">product.packaging</field> <field name="model">product.packaging</field>
<field name="inherit_id" ref="delivery.product_packaging_delivery_form"/> <field name="inherit_id" ref="delivery.product_packaging_delivery_form"/>
<field name="arch" type="xml"> <field name="arch" type="xml">
<xpath expr="//field[@name='max_weight']" position='after'> <xpath expr="//field[@name='barcode']" position='before'>
<field name="stamps_cubic_pricing"/> <field name="stamps_cubic_pricing"/>
</xpath> </xpath>
</field> </field>

View File

@@ -1 +1,3 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
from . import models from . import models

View File

@@ -1,15 +1,16 @@
{ {
'name': 'Hibou UPS Shipping', 'name': 'Hibou UPS Shipping',
'version': '11.0.1.0.0', 'version': '11.0.1.2.0',
'category': 'Stock', 'category': 'Stock',
'author': "Hibou Corp.", 'author': "Hibou Corp.",
'license': 'AGPL-3', 'license': 'OPL-1',
'website': 'https://hibou.io/', 'website': 'https://hibou.io/',
'depends': [ 'depends': [
'delivery_ups', 'delivery_ups',
'delivery_hibou', 'delivery_hibou',
], ],
'data': [ 'data': [
'views/stock_views.xml',
], ],
'demo': [ 'demo': [
], ],

View File

@@ -0,0 +1,59 @@
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema targetNamespace="http://www.ups.com/XMLSchema/XOLTWS/Error/v1.1" xmlns:error="http://www.ups.com/XMLSchema/XOLTWS/Error/v1.1" xmlns:xsd="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" version="201707">
<xsd:element name="Errors">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="ErrorDetail" type="error:ErrorDetailType" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:complexType name="ErrorDetailType">
<xsd:sequence>
<xsd:element name="Severity" type="xsd:string"/>
<xsd:element name="PrimaryErrorCode" type="error:CodeType"/>
<xsd:element name="MinimumRetrySeconds" type="xsd:string" minOccurs="0"/>
<xsd:element name="Location" type="error:LocationType" minOccurs="0"/>
<xsd:element name="SubErrorCode" type="error:CodeType" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="AdditionalInformation" type="error:AdditionalInfoType" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="ElementLevelInformation" type="error:ElementLevelInformationType" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ElementLevelInformationType">
<xsd:sequence>
<xsd:element name="Level" type="xsd:string"/>
<xsd:element name="ElementIdentifier" type="error:ElementIdentifierType" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ElementIdentifierType">
<xsd:sequence>
<xsd:element name="Code" type="xsd:string"/>
<xsd:element name="Value" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="CodeType">
<xsd:sequence>
<xsd:element name="Code" type="xsd:string"/>
<xsd:element name="Description" type="xsd:string"/>
<xsd:element name="Digest" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="AdditionalInfoType">
<xsd:sequence>
<xsd:element name="Type" type="xsd:string"/>
<xsd:element name="Value" type="error:AdditionalCodeDescType" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="AdditionalCodeDescType">
<xsd:sequence>
<xsd:element name="Code" type="xsd:string"/>
<xsd:element name="Description" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="LocationType">
<xsd:sequence>
<xsd:element name="LocationElementName" type="xsd:string" minOccurs="0"/>
<xsd:element name="XPathOfElement" type="xsd:string" minOccurs="0"/>
<xsd:element name="OriginalValue" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
</xsd:schema>

View File

@@ -0,0 +1,318 @@
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema targetNamespace="http://www.ups.com/XMLSchema/XOLTWS/IF/v1.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:ups="http://www.ups.com/XMLSchema" xmlns:IF="http://www.ups.com/XMLSchema/XOLTWS/IF/v1.0" elementFormDefault="qualified" version="201801">
<xsd:complexType name="InternationalFormType">
<xsd:sequence>
<xsd:element name="FormType" type="xsd:string" maxOccurs="6"/>
<xsd:element name="UserCreatedForm" type="IF:UserCreatedFormType" minOccurs="0"/>
<xsd:element name="CN22Form" type="IF:CN22FormType" minOccurs="0"/>
<xsd:element name="UPSPremiumCareForm" type="IF:UPSPremiumCareFormType" minOccurs="0"/>
<xsd:element name="AdditionalDocumentIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="FormGroupIdName" type="xsd:string" minOccurs="0"/>
<xsd:element name="SEDFilingOption" type="xsd:string" minOccurs="0"/>
<xsd:element name="EEIFilingOption" type="IF:EEIFilingOptionType" minOccurs="0"/>
<xsd:element name="Contacts" type="IF:ContactType" minOccurs="0"/>
<xsd:element name="Product" type="IF:ProductType" maxOccurs="50"/>
<xsd:element name="InvoiceNumber" type="xsd:string" minOccurs="0"/>
<xsd:element name="InvoiceDate" type="xsd:string" minOccurs="0"/>
<xsd:element name="PurchaseOrderNumber" type="xsd:string" minOccurs="0"/>
<xsd:element name="TermsOfShipment" type="xsd:string" minOccurs="0"/>
<xsd:element name="ReasonForExport" type="xsd:string" minOccurs="0"/>
<xsd:element name="Comments" type="xsd:string" minOccurs="0"/>
<xsd:element name="DeclarationStatement" type="xsd:string" minOccurs="0"/>
<xsd:element name="Discount" type="IF:IFChargesType" minOccurs="0"/>
<xsd:element name="FreightCharges" type="IF:IFChargesType" minOccurs="0"/>
<xsd:element name="InsuranceCharges" type="IF:IFChargesType" minOccurs="0"/>
<xsd:element name="OtherCharges" type="IF:OtherChargesType" minOccurs="0"/>
<xsd:element name="CurrencyCode" type="xsd:string"/>
<xsd:element name="BlanketPeriod" type="IF:BlanketPeriodType" minOccurs="0"/>
<xsd:element name="ExportDate" type="xsd:string" minOccurs="0"/>
<xsd:element name="ExportingCarrier" type="xsd:string" minOccurs="0"/>
<xsd:element name="CarrierID" type="xsd:string" minOccurs="0"/>
<xsd:element name="InBondCode" type="xsd:string" minOccurs="0"/>
<xsd:element name="EntryNumber" type="xsd:string" minOccurs="0"/>
<xsd:element name="PointOfOrigin" type="xsd:string" minOccurs="0"/>
<xsd:element name="PointOfOriginType" type="xsd:string" minOccurs="0"/>
<xsd:element name="ModeOfTransport" type="xsd:string" minOccurs="0"/>
<xsd:element name="PortOfExport" type="xsd:string" minOccurs="0"/>
<xsd:element name="PortOfUnloading" type="xsd:string" minOccurs="0"/>
<xsd:element name="LoadingPier" type="xsd:string" minOccurs="0"/>
<xsd:element name="PartiesToTransaction" type="xsd:string" minOccurs="0"/>
<xsd:element name="RoutedExportTransactionIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="ContainerizedIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="License" type="IF:LicenseType" minOccurs="0"/>
<xsd:element name="ECCNNumber" type="xsd:string" minOccurs="0"/>
<xsd:element name="OverridePaperlessIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="ShipperMemo" type="xsd:string" minOccurs="0"/>
<xsd:element name="MultiCurrencyInvoiceLineTotal" type="xsd:string" minOccurs="0"/>
<xsd:element name="HazardousMaterialsIndicator" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="UPSPremiumCareFormType">
<xsd:sequence>
<xsd:element name="ShipmentDate" type="xsd:string"/>
<xsd:element name="PageSize" type="xsd:string"/>
<xsd:element name="PrintType" type="xsd:string"/>
<xsd:element name="NumOfCopies" type="xsd:string"/>
<xsd:element name="LanguageForUPSPremiumCare" type="IF:LanguageForUPSPremiumCareType"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="LanguageForUPSPremiumCareType">
<xsd:sequence>
<xsd:element name="Language" type="xsd:string" maxOccurs="2"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="UserCreatedFormType">
<xsd:sequence>
<xsd:element name="DocumentID" type="xsd:string" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="CN22FormType">
<xsd:sequence>
<xsd:element name="LabelSize" type="xsd:string" minOccurs="0"/>
<xsd:element name="PrintsPerPage" type="xsd:string" minOccurs="0"/>
<xsd:element name="LabelPrintType" type="xsd:string" minOccurs="0"/>
<xsd:element name="CN22Type" type="xsd:string" minOccurs="0"/>
<xsd:element name="CN22OtherDescription" type="xsd:string" minOccurs="0"/>
<xsd:element name="FoldHereText" type="xsd:string" minOccurs="0"/>
<xsd:element name="CN22Content" type="IF:CN22ContentType" minOccurs="0" maxOccurs="30"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="CN22ContentType">
<xsd:sequence>
<xsd:element name="CN22ContentQuantity" type="xsd:string" minOccurs="0"/>
<xsd:element name="CN22ContentDescription" type="xsd:string" minOccurs="0"/>
<xsd:element name="CN22ContentWeight" type="IF:ProductWeightType" minOccurs="0"/>
<xsd:element name="CN22ContentTotalValue" type="xsd:string" minOccurs="0"/>
<xsd:element name="CN22ContentCurrencyCode" type="xsd:string" minOccurs="0"/>
<xsd:element name="CN22ContentCountryOfOrigin" type="xsd:string" minOccurs="0"/>
<xsd:element name="CN22ContentTariffNumber" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ContactType">
<xsd:sequence>
<xsd:element name="ForwardAgent" type="IF:ForwardAgentType" minOccurs="0"/>
<xsd:element name="UltimateConsignee" type="IF:UltimateConsigneeType" minOccurs="0"/>
<xsd:element name="IntermediateConsignee" type="IF:IntermediateConsigneeType" minOccurs="0"/>
<xsd:element name="Producer" type="IF:ProducerType" minOccurs="0"/>
<xsd:element name="SoldTo" type="IF:SoldToType" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ForwardAgentType">
<xsd:sequence>
<xsd:element name="CompanyName" type="xsd:string"/>
<xsd:element name="TaxIdentificationNumber" type="xsd:string"/>
<xsd:element name="Address" type="IF:AddressType"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="AddressType">
<xsd:sequence>
<xsd:element name="AddressLine" type="xsd:string" maxOccurs="3"/>
<xsd:element name="City" type="xsd:string"/>
<xsd:element name="StateProvinceCode" type="xsd:string" minOccurs="0"/>
<xsd:element name="Town" type="xsd:string" minOccurs="0"/>
<xsd:element name="PostalCode" type="xsd:string" minOccurs="0"/>
<xsd:element name="CountryCode" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="UltimateConsigneeType">
<xsd:sequence>
<xsd:element name="CompanyName" type="xsd:string"/>
<xsd:element name="Address" type="IF:AddressType"/>
<xsd:element name="UltimateConsigneeType" type="IF:UltimateConsigneeTypeType" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="IntermediateConsigneeType">
<xsd:sequence>
<xsd:element name="CompanyName" type="xsd:string"/>
<xsd:element name="Address" type="IF:AddressType"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ProducerType">
<xsd:sequence>
<xsd:element name="Option" type="xsd:string" minOccurs="0"/>
<xsd:element name="CompanyName" type="xsd:string" minOccurs="0"/>
<xsd:element name="TaxIdentificationNumber" type="xsd:string" minOccurs="0"/>
<xsd:element name="Address" type="IF:AddressType" minOccurs="0"/>
<xsd:element name="AttentionName" type="xsd:string" minOccurs="0"/>
<xsd:element name="Phone" type="IF:PhoneType" minOccurs="0"/>
<xsd:element name="EMailAddress" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ProductType">
<xsd:sequence>
<xsd:element name="Description" type="xsd:string" maxOccurs="3"/>
<xsd:element name="Unit" type="IF:UnitType" minOccurs="0"/>
<xsd:element name="CommodityCode" type="xsd:string" minOccurs="0"/>
<xsd:element name="PartNumber" type="xsd:string" minOccurs="0" ups:usage="notused"/>
<xsd:element name="OriginCountryCode" type="xsd:string" minOccurs="0"/>
<xsd:element name="JointProductionIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="NetCostCode" type="xsd:string" minOccurs="0"/>
<xsd:element name="NetCostDateRange" type="IF:NetCostDateType" minOccurs="0"/>
<xsd:element name="PreferenceCriteria" type="xsd:string" minOccurs="0"/>
<xsd:element name="ProducerInfo" type="xsd:string" minOccurs="0"/>
<xsd:element name="MarksAndNumbers" type="xsd:string" minOccurs="0"/>
<xsd:element name="NumberOfPackagesPerCommodity" type="xsd:string" minOccurs="0"/>
<xsd:element name="ProductWeight" type="IF:ProductWeightType" minOccurs="0"/>
<xsd:element name="VehicleID" type="xsd:string" minOccurs="0"/>
<xsd:element name="ScheduleB" type="IF:ScheduleBType" minOccurs="0"/>
<xsd:element name="ExportType" type="xsd:string" minOccurs="0"/>
<xsd:element name="SEDTotalValue" type="xsd:string" minOccurs="0"/>
<xsd:element name="ExcludeFromForm" type="IF:ExcludeFromFormType" minOccurs="0"/>
<xsd:element name="ProductCurrencyCode" type="xsd:string" minOccurs="0"/>
<xsd:element name="PackingListInfo" type="IF:PackingListInfoType" minOccurs="0"/>
<xsd:element name="EEIInformation" type="IF:EEIInformationType" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ExcludeFromFormType">
<xsd:sequence>
<xsd:element name="FormType" type="xsd:string" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="UnitType">
<xsd:sequence>
<xsd:element name="Number" type="xsd:string"/>
<xsd:element name="UnitOfMeasurement" type="IF:UnitOfMeasurementType"/>
<xsd:element name="Value" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="PackingListInfoType">
<xsd:sequence>
<xsd:element name="PackageAssociated" type="IF:PackageAssociatedType" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="PackageAssociatedType">
<xsd:sequence>
<xsd:element name="PackageNumber" type="xsd:string" minOccurs="0"/>
<xsd:element name="ProductAmount" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="UnitOfMeasurementType">
<xsd:sequence>
<xsd:element name="Code" type="xsd:string"/>
<xsd:element name="Description" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="NetCostDateType">
<xsd:sequence>
<xsd:element name="BeginDate" type="xsd:string"/>
<xsd:element name="EndDate" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ProductWeightType">
<xsd:sequence>
<xsd:element name="UnitOfMeasurement" type="IF:UnitOfMeasurementType"/>
<xsd:element name="Weight" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ScheduleBType">
<xsd:sequence>
<xsd:element name="Number" type="xsd:string"/>
<xsd:element name="Quantity" type="xsd:string" minOccurs="0" maxOccurs="2"/>
<xsd:element name="UnitOfMeasurement" type="IF:UnitOfMeasurementType" maxOccurs="2"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="IFChargesType">
<xsd:sequence>
<xsd:element name="MonetaryValue" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="OtherChargesType">
<xsd:sequence>
<xsd:element name="MonetaryValue" type="xsd:string"/>
<xsd:element name="Description" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="BlanketPeriodType">
<xsd:sequence>
<xsd:element name="BeginDate" type="xsd:string"/>
<xsd:element name="EndDate" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="LicenseType">
<xsd:sequence>
<xsd:element name="Number" type="xsd:string" minOccurs="0"/>
<xsd:element name="Date" type="xsd:string" minOccurs="0"/>
<xsd:element name="ExceptionCode" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="SoldToType">
<xsd:sequence>
<xsd:element name="Name" type="xsd:string"/>
<xsd:element name="AttentionName" type="xsd:string"/>
<xsd:element name="TaxIdentificationNumber" type="xsd:string" minOccurs="0"/>
<xsd:element name="Phone" type="IF:PhoneType" minOccurs="0"/>
<xsd:element name="Option" type="xsd:string" minOccurs="0"/>
<xsd:element name="Address" type="IF:AddressType"/>
<xsd:element name="EMailAddress" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="PhoneType">
<xsd:sequence>
<xsd:element name="Number" type="xsd:string"/>
<xsd:element name="Extension" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="DDTCInformationType">
<xsd:sequence>
<xsd:element name="ITARExemptionNumber" type="xsd:string" minOccurs="0"/>
<xsd:element name="USMLCategoryCode" type="xsd:string" minOccurs="0"/>
<xsd:element name="EligiblePartyIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="RegistrationNumber" type="xsd:string" minOccurs="0"/>
<xsd:element name="Quantity" type="xsd:string" minOccurs="0"/>
<xsd:element name="UnitOfMeasurement" type="IF:UnitOfMeasurementType"/>
<xsd:element name="SignificantMilitaryEquipmentIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="ACMNumber" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="EEILicenseType">
<xsd:sequence>
<xsd:element name="Number" type="xsd:string" minOccurs="0"/>
<xsd:element name="Code" type="xsd:string" minOccurs="0"/>
<xsd:element name="LicenseLineValue" type="xsd:string" minOccurs="0"/>
<xsd:element name="ECCNNumber" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="EEIFilingOptionType">
<xsd:sequence>
<xsd:element name="Code" type="xsd:string" minOccurs="0"/>
<xsd:element name="EMailAddress" type="xsd:string" minOccurs="0"/>
<xsd:element name="Description" type="xsd:string" minOccurs="0"/>
<xsd:element name="UPSFiled" type="IF:UPSFiledType" minOccurs="0"/>
<xsd:element name="ShipperFiled" type="IF:ShipperFiledType" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="UPSFiledType">
<xsd:sequence>
<xsd:element name="POA" type="IF:POAType"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ShipperFiledType">
<xsd:sequence>
<xsd:element name="Code" type="xsd:string" minOccurs="0"/>
<xsd:element name="Description" type="xsd:string" minOccurs="0"/>
<xsd:element name="PreDepartureITNNumber" type="xsd:string" minOccurs="0"/>
<xsd:element name="ExemptionLegend" type="xsd:string" minOccurs="0"/>
<xsd:element name="EEIShipmentReferenceNumber" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="EEIInformationType">
<xsd:sequence>
<xsd:element name="ExportInformation" type="xsd:string" minOccurs="0"/>
<xsd:element name="License" type="IF:EEILicenseType" minOccurs="0"/>
<xsd:element name="DDTCInformation" type="IF:DDTCInformationType" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="POAType">
<xsd:sequence>
<xsd:element name="Code" type="xsd:string" minOccurs="0"/>
<xsd:element name="Description" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="UltimateConsigneeTypeType">
<xsd:sequence>
<xsd:element name="Code" type="xsd:string" minOccurs="0"/>
<xsd:element name="Description" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
</xsd:schema>

View File

@@ -0,0 +1,166 @@
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema targetNamespace="http://www.ups.com/XMLSchema/XOLTWS/LBRecovery/v1.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:lbrec="http://www.ups.com/XMLSchema/XOLTWS/LBRecovery/v1.0" xmlns:ups="http://www.ups.com/XMLSchema" xmlns:common="http://www.ups.com/XMLSchema/XOLTWS/Common/v1.0" elementFormDefault="qualified" version="201707">
<xsd:import namespace="http://www.ups.com/XMLSchema/XOLTWS/Common/v1.0" schemaLocation="common.xsd"/>
<xsd:element name="LabelRecoveryRequest">
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="common:Request"/>
<xsd:element name="LabelSpecification" type="lbrec:LabelSpecificationType" minOccurs="0"/>
<xsd:element name="Translate" type="lbrec:TranslateType" minOccurs="0"/>
<xsd:element name="LabelDelivery" type="lbrec:LabelDeliveryType" minOccurs="0"/>
<xsd:element name="TrackingNumber" type="xsd:string" minOccurs="0"/>
<xsd:element name="MailInnovationsTrackingNumber" type="xsd:string" minOccurs="0"/>
<xsd:element name="ReferenceValues" type="lbrec:ReferenceValuesType" minOccurs="0"/>
<xsd:element name="UPSPremiumCareForm" type="lbrec:LRUPSPremiumCareFormType" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:complexType name="ReferenceValuesType">
<xsd:sequence>
<xsd:element name="ReferenceNumber" type="lbrec:ReferenceNumberType"/>
<xsd:element name="ShipperNumber" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ReferenceNumberType">
<xsd:sequence>
<xsd:element name="Value" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="LabelSpecificationType">
<xsd:sequence>
<xsd:element name="HTTPUserAgent" type="xsd:string" minOccurs="0"/>
<xsd:element name="LabelImageFormat" type="lbrec:LabelImageFormatType" minOccurs="0"/>
<xsd:element name="LabelStockSize" type="lbrec:LabelStockSizeType" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="LabelStockSizeType">
<xsd:sequence>
<xsd:element name="Height" type="xsd:string"/>
<xsd:element name="Width" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="LabelImageFormatType">
<xsd:sequence>
<xsd:element name="Code" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="TranslateType">
<xsd:sequence>
<xsd:element name="LanguageCode" type="xsd:string"/>
<xsd:element name="DialectCode" type="xsd:string"/>
<xsd:element name="Code" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="LabelDeliveryType">
<xsd:sequence>
<xsd:element name="LabelLinkIndicator" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:element name="LabelRecoveryResponse">
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="common:Response"/>
<xsd:element name="ShipmentIdentificationNumber" type="xsd:string" minOccurs="0"/>
<xsd:element name="CODTurnInPage" type="lbrec:ResponseImageType" minOccurs="0"/>
<xsd:element name="Form" type="lbrec:FormType" minOccurs="0"/>
<xsd:element name="HighValueReport" type="lbrec:HighValueReportType" minOccurs="0"/>
<xsd:choice minOccurs="0" maxOccurs="unbounded">
<xsd:element name="LabelResults" type="lbrec:LabelResultsType"/>
<xsd:element name="TrackingCandidate" type="lbrec:TrackingCandidateType"/>
</xsd:choice>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:complexType name="LabelResultsType">
<xsd:sequence>
<xsd:element name="TrackingNumber" type="xsd:string"/>
<xsd:element name="LabelImage" type="lbrec:LabelImageType"/>
<xsd:element name="Receipt" type="lbrec:ReceiptType" minOccurs="0"/>
<xsd:element name="Form" type="lbrec:FormType" minOccurs="0"/>
<xsd:element name="MailInnovationsTrackingNumber" type="xsd:string" minOccurs="0"/>
<xsd:element name="MailInnovationsLabelImage" type="lbrec:LabelImageType" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="LabelImageType">
<xsd:sequence>
<xsd:element name="LabelImageFormat" type="lbrec:LabelImageFormatType"/>
<xsd:element name="GraphicImage" type="xsd:string"/>
<xsd:element name="HTMLImage" type="xsd:string" minOccurs="0"/>
<xsd:element name="PDF417" type="xsd:string" minOccurs="0"/>
<xsd:element name="InternationalSignatureGraphicImage" type="xsd:string" minOccurs="0"/>
<xsd:element name="URL" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ReceiptType">
<xsd:sequence>
<xsd:element name="HTMLImage" type="xsd:string" minOccurs="0"/>
<xsd:element name="Image" type="lbrec:ImageType" minOccurs="0"/>
<xsd:element name="URL" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ImageType">
<xsd:sequence>
<xsd:element name="ImageFormat" type="lbrec:LabelImageFormatType" minOccurs="0"/>
<xsd:element name="GraphicImage" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="TrackingCandidateType">
<xsd:sequence>
<xsd:element name="TrackingNumber" type="xsd:string"/>
<xsd:element name="DestinationPostalCode" type="xsd:string" minOccurs="0"/>
<xsd:element name="DestinationCountryCode" type="xsd:string" minOccurs="0"/>
<xsd:element name="PickupDateRange" type="lbrec:PickupDateRangeType" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="PickupDateRangeType">
<xsd:sequence>
<xsd:element name="BeginDate" type="xsd:string"/>
<xsd:element name="EndDate" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ResponseImageType">
<xsd:sequence>
<xsd:element name="Image" type="lbrec:ResponseImageDetailType"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ResponseImageDetailType">
<xsd:sequence>
<xsd:element name="ImageFormat" type="lbrec:ImageFormatCodeType"/>
<xsd:element name="GraphicImage" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ImageFormatCodeType">
<xsd:sequence>
<xsd:element name="Code" type="xsd:string"/>
<xsd:element name="Description" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="FormType">
<xsd:sequence>
<xsd:element name="Image" type="lbrec:FormImageType"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="FormImageType">
<xsd:sequence>
<xsd:element name="ImageFormat" type="lbrec:ImageFormatCodeType"/>
<xsd:element name="GraphicImage" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="HighValueReportType">
<xsd:sequence>
<xsd:element name="Image" type="lbrec:HVRImageType"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="HVRImageType">
<xsd:sequence>
<xsd:element name="ImageFormat" type="lbrec:ImageFormatCodeType"/>
<xsd:element name="GraphicImage" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="LRUPSPremiumCareFormType">
<xsd:sequence>
<xsd:element name="PageSize" type="xsd:string"/>
<xsd:element name="PrintType" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
</xsd:schema>

View File

@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions name="LabelRecovery" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:error="http://www.ups.com/XMLSchema/XOLTWS/Error/v1.1" xmlns:upss="http://www.ups.com/XMLSchema/XOLTWS/UPSS/v1.0" xmlns:lbrec="http://www.ups.com/XMLSchema/XOLTWS/LBRecovery/v1.0" xmlns:tns="http://www.ups.com/WSDL/XOLTWS/LBRecovery/v1.0" targetNamespace="http://www.ups.com/WSDL/XOLTWS/LBRecovery/v1.0">
<wsdl:types>
<xsd:schema>
<!-- This schema defines the UPS Security header used for authorization purposes -->
<xsd:import namespace="http://www.ups.com/XMLSchema/XOLTWS/UPSS/v1.0" schemaLocation="UPSSecurity.xsd"/>
<!-- This schema defines the error detail data types returned within SOAPFaults to provide more specific information pertaining to the problem. -->
<xsd:import namespace="http://www.ups.com/XMLSchema/XOLTWS/Error/v1.1" schemaLocation="Error1.1.xsd"/>
<!-- ################## Start MCEnrollment Specific schema ################### -->
<xsd:import namespace="http://www.ups.com/XMLSchema/XOLTWS/LBRecovery/v1.0" schemaLocation="LBRecovery.xsd"/>
<!-- ################## End MCEnrollment specific schema ##################### -->
</xsd:schema>
</wsdl:types>
<wsdl:message name="LabelRecoveryRequestMessage">
<wsdl:part name="Body" element="lbrec:LabelRecoveryRequest"/>
<wsdl:part name="UPSSecurity" element="upss:UPSSecurity"/>
</wsdl:message>
<wsdl:message name="LabelRecoveryResponseMessage">
<wsdl:part name="Body" element="lbrec:LabelRecoveryResponse"/>
</wsdl:message>
<wsdl:message name="LabelRecoveryErrorMessage">
<wsdl:part name="LabelRecoveryError" element="error:Errors"/>
</wsdl:message>
<wsdl:portType name="PortType">
<wsdl:operation name="ProcessLabelRecovery">
<wsdl:input name="LabelRecoveryRequest" message="tns:LabelRecoveryRequestMessage"/>
<wsdl:output name="LabelRecoveryResponse" message="tns:LabelRecoveryResponseMessage"/>
<wsdl:fault name="LabelRecoveryError" message="tns:LabelRecoveryErrorMessage"/>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="LabelRecoveryBinding" type="tns:PortType">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="ProcessLabelRecovery">
<soap:operation soapAction="http://onlinetools.ups.com/webservices/ShipBinding/v1.1" style="document"/>
<wsdl:input name="LabelRecoveryRequest">
<soap:body parts="Body" use="literal"/>
<soap:header message="tns:LabelRecoveryRequestMessage" part="UPSSecurity" use="literal">
<soap:headerfault message="tns:LabelRecoveryErrorMessage" part="LabelRecoveryError" use="literal"/>
</soap:header>
</wsdl:input>
<wsdl:output name="LabelRecoveryResponse">
<soap:body parts="Body" use="literal"/>
</wsdl:output>
<wsdl:fault name="LabelRecoveryError">
<soap:fault name="LabelRecoveryError" use="literal"/>
</wsdl:fault>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="LBRecovery">
<wsdl:port name="Port" binding="tns:LabelRecoveryBinding">
<soap:address location="https://wwwcie.ups.com/webservices/LBRecovery"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>

View File

@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- UPS Rate Service WSDL Release Date Dec 29, 2007 -->
<!-- Copyright 2007-2008 United Parcel Service of America, Inc. All rights reserved. -->
<wsdl:definitions name="RateWS" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:error="http://www.ups.com/XMLSchema/XOLTWS/Error/v1.1" xmlns:upss="http://www.ups.com/XMLSchema/XOLTWS/UPSS/v1.0" xmlns:rate="http://www.ups.com/XMLSchema/XOLTWS/Rate/v1.1" xmlns:tns="http://www.ups.com/WSDL/XOLTWS/Rate/v1.1" targetNamespace="http://www.ups.com/WSDL/XOLTWS/Rate/v1.1">
<wsdl:types>
<xsd:schema>
<!-- This schema defines the UPS Security header used for authorization purposes -->
<xsd:import namespace="http://www.ups.com/XMLSchema/XOLTWS/UPSS/v1.0" schemaLocation="UPSSecurity.xsd"/>
<!-- This schema defines the error detail data types returned within SOAPFaults to provide more specific information pertaining to the problem. -->
<xsd:import namespace="http://www.ups.com/XMLSchema/XOLTWS/Error/v1.1" schemaLocation="Error1.1.xsd"/>
<!-- This schema defines the Rate service data types -->
<xsd:import namespace="http://www.ups.com/XMLSchema/XOLTWS/Rate/v1.1" schemaLocation="RateWebServiceSchema.xsd"/>
</xsd:schema>
</wsdl:types>
<wsdl:message name="RateRequestMessage">
<wsdl:part name="Body" element="rate:RateRequest"/>
<wsdl:part name="UPSSecurity" element="upss:UPSSecurity"/>
</wsdl:message>
<wsdl:message name="RateResponseMessage">
<wsdl:part name="Body" element="rate:RateResponse"/>
</wsdl:message>
<wsdl:message name="RateErrorMessage">
<wsdl:part name="RateError" element="error:Errors"/>
</wsdl:message>
<wsdl:portType name="RatePortType">
<wsdl:operation name="ProcessRate">
<wsdl:input name="RateRequest" message="tns:RateRequestMessage"/>
<wsdl:output name="RateResponse" message="tns:RateResponseMessage"/>
<wsdl:fault name="RateError" message="tns:RateErrorMessage"/>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="RateBinding" type="tns:RatePortType">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="ProcessRate">
<soap:operation soapAction="http://onlinetools.ups.com/webservices/RateBinding/v1.1" style="document"/>
<wsdl:input name="RateRequest">
<soap:body parts="Body" use="literal"/>
<soap:header message="tns:RateRequestMessage" part="UPSSecurity" use="literal">
<soap:headerfault message="tns:RateErrorMessage" part="RateError" use="literal"/>
</soap:header>
</wsdl:input>
<wsdl:output name="RateResponse">
<soap:body parts="Body" use="literal"/>
</wsdl:output>
<wsdl:fault name="RateError">
<soap:fault name="RateError" use="literal"/>
</wsdl:fault>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="RateService">
<wsdl:port name="RatePort" binding="tns:RateBinding">
<!-- Production URL -->
<!-- <soap:address location="https://onlinetools.ups.com/webservices/Rate"/> -->
<!-- CIE (Customer Integration Environment) URL -->
<soap:address location="https://wwwcie.ups.com/webservices/Rate"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>

View File

@@ -0,0 +1,644 @@
<xsd:schema targetNamespace="http://www.ups.com/XMLSchema/XOLTWS/Rate/v1.1" xmlns:ups="http://www.ups.com/XMLSchema" xmlns:rate="http://www.ups.com/XMLSchema/XOLTWS/Rate/v1.1" xmlns:common="http://www.ups.com/XMLSchema/XOLTWS/Common/v1.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" version="201801">
<xsd:import namespace="http://www.ups.com/XMLSchema/XOLTWS/Common/v1.0" schemaLocation="common.xsd"/>
<xsd:element name="RateRequest">
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="common:Request"/>
<xsd:element name="PickupType" type="rate:CodeDescriptionType" minOccurs="0"/>
<xsd:element name="CustomerClassification" type="rate:CodeDescriptionType" minOccurs="0"/>
<xsd:element name="Shipment" type="rate:ShipmentType"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="RateResponse">
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="common:Response"/>
<xsd:element name="RatedShipment" type="rate:RatedShipmentType" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:complexType name="BillingWeightType">
<xsd:sequence>
<xsd:element name="UnitOfMeasurement" type="rate:CodeDescriptionType"/>
<xsd:element name="Weight" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="RatedPackageType">
<xsd:sequence>
<xsd:element name="TransportationCharges" type="rate:ChargesType" minOccurs="0"/>
<xsd:element name="BaseServiceCharge" type="rate:ChargesType" minOccurs="0"/>
<xsd:element name="ServiceOptionsCharges" type="rate:ChargesType" minOccurs="0"/>
<xsd:element name="TotalCharges" type="rate:ChargesType" minOccurs="0"/>
<xsd:element name="Weight" type="xsd:string" minOccurs="0"/>
<xsd:element name="BillingWeight" type="rate:BillingWeightType" minOccurs="0"/>
<xsd:element name="Accessorial" type="rate:AccessorialType" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="ItemizedCharges" type="rate:ChargesType" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="NegotiatedCharges" type="rate:NegotiatedChargesType" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="AccessorialType">
<xsd:sequence>
<xsd:element name="Code" type="xsd:string"/>
<xsd:element name="Description" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="NegotiatedChargesType">
<xsd:sequence>
<xsd:element name="ItemizedCharges" type="rate:ChargesType" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="RatedShipmentType">
<xsd:sequence>
<xsd:element name="Disclaimer" type="rate:DisclaimerType" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="Service" type="rate:CodeDescriptionType"/>
<xsd:element name="RateChart" type="xsd:string" minOccurs="0"/>
<xsd:element name="RatedShipmentAlert" type="rate:RatedShipmentInfoType" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="BillableWeightCalculationMethod" type="xsd:string" minOccurs="0"/>
<xsd:element name="RatingMethod" type="xsd:string" minOccurs="0"/>
<xsd:element name="BillingWeight" type="rate:BillingWeightType"/>
<xsd:element name="TransportationCharges" type="rate:ChargesType"/>
<xsd:element name="BaseServiceCharge" type="rate:ChargesType" minOccurs="0"/>
<xsd:element name="ItemizedCharges" type="rate:ChargesType" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="FRSShipmentData" type="rate:FRSShipmentType" minOccurs="0"/>
<xsd:element name="ServiceOptionsCharges" type="rate:ChargesType"/>
<xsd:element name="TaxCharges" type="rate:TaxChargeType" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="TotalCharges" type="rate:ChargesType"/>
<xsd:element name="TotalChargesWithTaxes" type="rate:ChargesType" minOccurs="0"/>
<xsd:element name="NegotiatedRateCharges" type="rate:TotalChargeType" minOccurs="0"/>
<xsd:element name="GuaranteedDelivery" type="rate:GuaranteedDeliveryType" minOccurs="0"/>
<xsd:element name="RatedPackage" type="rate:RatedPackageType" maxOccurs="unbounded"/>
<xsd:element name="TimeInTransit" type="rate:TimeInTransitResponseType" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ReturnContractServicesType">
<xsd:sequence>
<xsd:element name="Code" type="xsd:string"/>
<xsd:element name="Description" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="TimeInTransitResponseType">
<xsd:sequence>
<xsd:element name="PickupDate" type="xsd:string"/>
<xsd:element name="DocumentsOnlyIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="PackageBillType" type="xsd:string" minOccurs="0"/>
<xsd:element name="AutoDutyCode" type="xsd:string" minOccurs="0"/>
<xsd:element name="Disclaimer" type="xsd:string" minOccurs="0"/>
<xsd:element name="ServiceSummary" type="rate:ServiceSummaryType"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ServiceSummaryType">
<xsd:sequence>
<xsd:element name="Service" type="rate:CodeDescriptionType"/>
<xsd:element name="EstimatedArrival" type="rate:EstimatedArrivalType"/>
<xsd:element name="GuaranteedIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="Disclaimer" type="xsd:string" minOccurs="0"/>
<xsd:element name="SaturdayDelivery" type="xsd:string" minOccurs="0"/>
<xsd:element name="SaturdayDeliveryDisclaimer" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="EstimatedArrivalType">
<xsd:sequence>
<xsd:element name="Arrival" type="rate:PickupType"/>
<xsd:element name="BusinessDaysInTransit" type="xsd:string"/>
<xsd:element name="Pickup" type="rate:PickupType"/>
<xsd:element name="DayOfWeek" type="xsd:string" minOccurs="0"/>
<xsd:element name="CustomerCenterCutoff" type="xsd:string" minOccurs="0"/>
<xsd:element name="DelayCount" type="xsd:string" minOccurs="0"/>
<xsd:element name="HolidayCount" type="xsd:string" minOccurs="0"/>
<xsd:element name="RestDays" type="xsd:string" minOccurs="0"/>
<xsd:element name="TotalTransitDays" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="DisclaimerType">
<xsd:sequence>
<xsd:element name="Code" type="xsd:string"/>
<xsd:element name="Description" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="TaxChargeType">
<xsd:sequence>
<xsd:element name="Type" type="xsd:string"/>
<xsd:element name="MonetaryValue" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="TotalChargeType">
<xsd:sequence>
<xsd:element name="ItemizedCharges" type="rate:ChargesType" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="TaxCharges" type="rate:TaxChargeType" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="TotalCharge" type="rate:ChargesType"/>
<xsd:element name="TotalChargesWithTaxes" type="rate:ChargesType" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="RatedShipmentInfoType">
<xsd:sequence>
<xsd:element name="Code" type="xsd:string"/>
<xsd:element name="Description" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ChargesType">
<xsd:sequence>
<xsd:element name="Code" type="xsd:string" minOccurs="0"/>
<xsd:element name="Description" type="xsd:string" minOccurs="0"/>
<xsd:element name="CurrencyCode" type="xsd:string"/>
<xsd:element name="MonetaryValue" type="xsd:string"/>
<xsd:element name="SubType" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="TransportationChargesType">
<xsd:sequence>
<xsd:element name="GrossCharge" type="rate:ChargesType"/>
<xsd:element name="DiscountAmount" type="rate:ChargesType"/>
<xsd:element name="DiscountPercentage" type="xsd:string"/>
<xsd:element name="NetCharge" type="rate:ChargesType"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="FRSShipmentType">
<xsd:sequence>
<xsd:element name="TransportationCharges" type="rate:TransportationChargesType"/>
<xsd:element name="FreightDensityRate" type="rate:FreightDensityRateType" minOccurs="0"/>
<xsd:element name="HandlingUnits" type="rate:HandlingUnitsResponseType" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="FreightDensityRateType">
<xsd:sequence>
<xsd:element name="Density" type="xsd:string"/>
<xsd:element name="TotalCubicFeet" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="HandlingUnitsResponseType">
<xsd:sequence>
<xsd:element name="Quantity" type="xsd:string"/>
<xsd:element name="Type" type="rate:CodeDescriptionType"/>
<xsd:element name="Dimensions" type="rate:HandlingUnitsDimensionsType"/>
<xsd:element name="AdjustedHeight" type="rate:AdjustedHeightType" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="AddressType">
<xsd:sequence>
<xsd:element name="AddressLine" type="xsd:string" minOccurs="0" maxOccurs="3"/>
<xsd:element name="City" type="xsd:string" minOccurs="0"/>
<xsd:element name="StateProvinceCode" type="xsd:string" minOccurs="0"/>
<xsd:element name="PostalCode" type="xsd:string" minOccurs="0"/>
<xsd:element name="CountryCode" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ShipToAddressType">
<xsd:complexContent>
<xsd:extension base="rate:AddressType">
<xsd:sequence>
<xsd:element name="ResidentialAddressIndicator" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="ShipAddressType">
<xsd:complexContent>
<xsd:extension base="rate:AddressType">
<xsd:sequence/>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="CODType">
<xsd:sequence>
<xsd:element name="CODFundsCode" type="xsd:string"/>
<xsd:element name="CODAmount" type="rate:CODAmountType"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="CODAmountType">
<xsd:sequence>
<xsd:element name="CurrencyCode" type="xsd:string"/>
<xsd:element name="MonetaryValue" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="DeliveryConfirmationType">
<xsd:sequence>
<xsd:element name="DCISType" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="DimensionsType">
<xsd:sequence>
<xsd:element name="UnitOfMeasurement" type="rate:CodeDescriptionType"/>
<xsd:element name="Length" type="xsd:string" minOccurs="0"/>
<xsd:element name="Width" type="xsd:string" minOccurs="0"/>
<xsd:element name="Height" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="InsuredValueType">
<xsd:sequence>
<xsd:element name="CurrencyCode" type="xsd:string"/>
<xsd:element name="MonetaryValue" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="PackageType">
<xsd:sequence>
<xsd:element name="PackagingType" type="rate:CodeDescriptionType" minOccurs="0"/>
<xsd:element name="Dimensions" type="rate:DimensionsType" minOccurs="0"/>
<xsd:element name="DimWeight" type="rate:PackageWeightType" minOccurs="0"/>
<xsd:element name="PackageWeight" type="rate:PackageWeightType" minOccurs="0"/>
<xsd:element name="Commodity" type="rate:CommodityType" minOccurs="0"/>
<xsd:element name="LargePackageIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="PackageServiceOptions" type="rate:PackageServiceOptionsType" minOccurs="0"/>
<xsd:element name="AdditionalHandlingIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="UPSPremier" type="rate:UPSPremierType" minOccurs="0"/>
<xsd:element name="OversizeIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="MinimumBillableWeightIndicator" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="CommodityType">
<xsd:sequence>
<xsd:element name="FreightClass" type="xsd:string"/>
<xsd:element name="NMFC" type="rate:NMFCCommodityType" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="NMFCCommodityType">
<xsd:sequence>
<xsd:element name="PrimeCode" type="xsd:string"/>
<xsd:element name="SubCode" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="PackageServiceOptionsType">
<xsd:sequence>
<xsd:element name="DeliveryConfirmation" type="rate:DeliveryConfirmationType" minOccurs="0"/>
<xsd:element name="AccessPointCOD" type="rate:PackageServiceOptionsAccessPointCODType" minOccurs="0"/>
<xsd:element name="COD" type="rate:CODType" minOccurs="0"/>
<xsd:element name="DeclaredValue" type="rate:InsuredValueType" minOccurs="0"/>
<xsd:element name="ShipperDeclaredValue" type="rate:ShipperDeclaredValueType" minOccurs="0"/>
<xsd:element name="ShipperReleaseIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="ProactiveIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="RefrigerationIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="Insurance" type="rate:InsuranceType" minOccurs="0"/>
<xsd:element name="UPSPremiumCareIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="HazMat" type="rate:HazMatType" minOccurs="0"/>
<xsd:element name="DryIce" type="rate:DryIceType" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="UPSPremierType">
<xsd:sequence>
<xsd:element name="Category" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="HazMatType">
<xsd:sequence>
<xsd:element name="PackageIdentifier" type="xsd:string" minOccurs="0"/>
<xsd:element name="QValue" type="xsd:string" minOccurs="0"/>
<xsd:element name="OverPackedIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="AllPackedInOneIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="HazMatChemicalRecord" type="rate:HazMatChemicalRecordType" maxOccurs="3"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="HazMatChemicalRecordType">
<xsd:sequence>
<xsd:element name="ChemicalRecordIdentifier" type="xsd:string" minOccurs="0"/>
<xsd:element name="ClassDivisionNumber" type="xsd:string" minOccurs="0"/>
<xsd:element name="IDNumber" type="xsd:string" minOccurs="0"/>
<xsd:element name="TransportationMode" type="xsd:string"/>
<xsd:element name="RegulationSet" type="xsd:string"/>
<xsd:element name="EmergencyPhone" type="xsd:string" minOccurs="0"/>
<xsd:element name="EmergencyContact" type="xsd:string" minOccurs="0"/>
<xsd:element name="ReportableQuantity" type="xsd:string" minOccurs="0"/>
<xsd:element name="SubRiskClass" type="xsd:string" minOccurs="0"/>
<xsd:element name="PackagingGroupType" type="xsd:string" minOccurs="0"/>
<xsd:element name="Quantity" type="xsd:string" minOccurs="0"/>
<xsd:element name="UOM" type="xsd:string" minOccurs="0"/>
<xsd:element name="PackagingInstructionCode" type="xsd:string" minOccurs="0"/>
<xsd:element name="ProperShippingName" type="xsd:string" minOccurs="0"/>
<xsd:element name="TechnicalName" type="xsd:string" minOccurs="0"/>
<xsd:element name="AdditionalDescription" type="xsd:string" minOccurs="0"/>
<xsd:element name="PackagingType" type="xsd:string" minOccurs="0"/>
<xsd:element name="HazardLabelRequired" type="xsd:string" minOccurs="0"/>
<xsd:element name="PackagingTypeQuantity" type="xsd:string" minOccurs="0"/>
<xsd:element name="CommodityRegulatedLevelCode" type="xsd:string" minOccurs="0"/>
<xsd:element name="TransportCategory" type="xsd:string" minOccurs="0"/>
<xsd:element name="TunnelRestrictionCode" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="PackageServiceOptionsAccessPointCODType">
<xsd:sequence>
<xsd:element name="CurrencyCode" type="xsd:string"/>
<xsd:element name="MonetaryValue" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="DryIceType">
<xsd:sequence>
<xsd:element name="RegulationSet" type="xsd:string"/>
<xsd:element name="DryIceWeight" type="rate:DryIceWeightType"/>
<xsd:element name="MedicalUseIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="AuditRequired" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="DryIceWeightType">
<xsd:sequence>
<xsd:element name="UnitOfMeasurement" type="rate:CodeDescriptionType"/>
<xsd:element name="Weight" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ShipperDeclaredValueType">
<xsd:sequence>
<xsd:element name="CurrencyCode" type="xsd:string"/>
<xsd:element name="MonetaryValue" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="InsuranceType">
<xsd:sequence>
<xsd:element name="BasicFlexibleParcelIndicator" type="rate:InsuranceValueType" minOccurs="0"/>
<xsd:element name="ExtendedFlexibleParcelIndicator" type="rate:InsuranceValueType" minOccurs="0"/>
<xsd:element name="TimeInTransitFlexibleParcelIndicator" type="rate:InsuranceValueType" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="InsuranceValueType">
<xsd:sequence>
<xsd:element name="CurrencyCode" type="xsd:string"/>
<xsd:element name="MonetaryValue" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="PackageWeightType">
<xsd:sequence>
<xsd:element name="UnitOfMeasurement" type="rate:CodeDescriptionType"/>
<xsd:element name="Weight" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="UOMCodeDescriptionType">
<xsd:sequence>
<xsd:element name="Code" type="xsd:string" minOccurs="0"/>
<xsd:element name="Description" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="CodeDescriptionType">
<xsd:sequence>
<xsd:element name="Code" type="xsd:string" minOccurs="0"/>
<xsd:element name="Description" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ShipmentRatingOptionsType">
<xsd:sequence>
<xsd:element name="NegotiatedRatesIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="FRSShipmentIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="RateChartIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="UserLevelDiscountIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="TPFCNegotiatedRatesIndicator" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ShipFromType">
<xsd:sequence>
<xsd:element name="Name" type="xsd:string" minOccurs="0"/>
<xsd:element name="AttentionName" type="xsd:string" minOccurs="0"/>
<xsd:element name="Address" type="rate:ShipAddressType"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ShipToType">
<xsd:sequence>
<xsd:element name="Name" type="xsd:string" minOccurs="0"/>
<xsd:element name="AttentionName" type="xsd:string" minOccurs="0"/>
<xsd:element name="Address" type="rate:ShipToAddressType"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ShipmentType">
<xsd:sequence>
<xsd:element name="OriginRecordTransactionTimestamp" type="xsd:string" minOccurs="0"/>
<xsd:element name="Shipper" type="rate:ShipperType"/>
<xsd:element name="ShipTo" type="rate:ShipToType"/>
<xsd:element name="ShipFrom" type="rate:ShipFromType" minOccurs="0"/>
<xsd:element name="AlternateDeliveryAddress" type="rate:AlternateDeliveryAddressType" minOccurs="0"/>
<xsd:element name="ShipmentIndicationType" type="rate:IndicationType" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="PaymentDetails" type="rate:PaymentDetailsType" minOccurs="0"/>
<xsd:element name="FRSPaymentInformation" type="rate:FRSPaymentInfoType" minOccurs="0"/>
<xsd:element name="FreightShipmentInformation" type="rate:FreightShipmentInformationType" minOccurs="0"/>
<xsd:element name="GoodsNotInFreeCirculationIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="Service" type="rate:CodeDescriptionType" minOccurs="0"/>
<xsd:element name="NumOfPieces" type="xsd:string" minOccurs="0"/>
<xsd:element name="ShipmentTotalWeight" type="rate:ShipmentWeightType" minOccurs="0"/>
<xsd:element name="DocumentsOnlyIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="Package" type="rate:PackageType" maxOccurs="unbounded"/>
<xsd:element name="ShipmentServiceOptions" type="rate:ShipmentServiceOptionsType" minOccurs="0"/>
<xsd:element name="ShipmentRatingOptions" type="rate:ShipmentRatingOptionsType" minOccurs="0"/>
<xsd:element name="InvoiceLineTotal" type="rate:InvoiceLineTotalType" minOccurs="0"/>
<xsd:element name="RatingMethodRequestedIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="TaxInformationIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="PromotionalDiscountInformation" type="rate:PromotionalDiscountInformationType" minOccurs="0"/>
<xsd:element name="DeliveryTimeInformation" type="rate:TimeInTransitRequestType" minOccurs="0"/>
<xsd:element name="MasterCartonIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="WWEShipmentIndicator" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="TimeInTransitRequestType">
<xsd:sequence>
<xsd:element name="PackageBillType" type="xsd:string" minOccurs="0"/>
<xsd:element name="Pickup" type="rate:PickupType" minOccurs="0"/>
<xsd:element name="ReturnContractServices" type="rate:ReturnContractServicesType" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="PickupType">
<xsd:sequence>
<xsd:element name="Date" type="xsd:string"/>
<xsd:element name="Time" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="PromotionalDiscountInformationType">
<xsd:sequence>
<xsd:element name="PromoCode" type="xsd:string"/>
<xsd:element name="PromoAliasCode" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ShipmentWeightType">
<xsd:sequence>
<xsd:element name="UnitOfMeasurement" type="rate:CodeDescriptionType"/>
<xsd:element name="Weight" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="PaymentDetailsType">
<xsd:sequence>
<xsd:element name="ShipmentCharge" type="rate:ShipmentChargeType" minOccurs="0" maxOccurs="2"/>
<xsd:element name="SplitDutyVATIndicator" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ShipmentChargeType">
<xsd:sequence>
<xsd:element name="Type" type="xsd:string"/>
<xsd:element name="BillShipper" type="rate:BillShipperChargeType" minOccurs="0"/>
<xsd:element name="BillReceiver" type="rate:BillReceiverChargeType" minOccurs="0"/>
<xsd:element name="BillThirdParty" type="rate:BillThirdPartyChargeType" minOccurs="0"/>
<xsd:element name="ConsigneeBilledIndicator" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="BillShipperChargeType">
<xsd:sequence>
<xsd:element name="AccountNumber" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="BillReceiverChargeType">
<xsd:sequence>
<xsd:element name="AccountNumber" type="xsd:string" minOccurs="0"/>
<xsd:element name="Address" type="rate:BillReceiverAddressType" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="BillThirdPartyChargeType">
<xsd:sequence>
<xsd:element name="AccountNumber" type="xsd:string" minOccurs="0"/>
<xsd:element name="Address" type="rate:AddressType" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="BillReceiverAddressType">
<xsd:sequence>
<xsd:element name="PostalCode" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="AlternateDeliveryAddressType">
<xsd:sequence>
<xsd:element name="Name" type="xsd:string" minOccurs="0"/>
<xsd:element name="Address" type="rate:ADRType"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ADRType">
<xsd:sequence>
<xsd:element name="AddressLine" type="xsd:string" minOccurs="0" maxOccurs="3"/>
<xsd:element name="City" type="xsd:string" minOccurs="0"/>
<xsd:element name="StateProvinceCode" type="xsd:string" minOccurs="0"/>
<xsd:element name="PostalCode" type="xsd:string" minOccurs="0"/>
<xsd:element name="CountryCode" type="xsd:string"/>
<xsd:element name="ResidentialAddressIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="POBoxIndicator" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="IndicationType">
<xsd:sequence>
<xsd:element name="Code" type="xsd:string"/>
<xsd:element name="Description" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ShipmentServiceOptionsType">
<xsd:sequence>
<xsd:element name="SaturdayPickupIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="SaturdayDeliveryIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="AccessPointCOD" type="rate:ShipmentServiceOptionsAccessPointCODType" minOccurs="0"/>
<xsd:element name="DeliverToAddresseeOnlyIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="DirectDeliveryOnlyIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="COD" type="rate:CODType" minOccurs="0"/>
<xsd:element name="DeliveryConfirmation" type="rate:DeliveryConfirmationType" minOccurs="0"/>
<xsd:element name="ReturnOfDocumentIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="UPScarbonneutralIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="CertificateOfOriginIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="PickupOptions" type="rate:PickupOptionsType" minOccurs="0"/>
<xsd:element name="DeliveryOptions" type="rate:DeliveryOptionsType" minOccurs="0"/>
<xsd:element name="RestrictedArticles" type="rate:RestrictedArticlesType" minOccurs="0"/>
<xsd:element name="ShipperExportDeclarationIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="CommercialInvoiceRemovalIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="ImportControl" type="rate:ImportControlType" minOccurs="0"/>
<xsd:element name="ReturnService" type="rate:ReturnServiceType" minOccurs="0"/>
<xsd:element name="SDLShipmentIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="EPRAIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="InsideDelivery" type="xsd:string" minOccurs="0"/>
<xsd:element name="ItemDisposalIndicator" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ShipmentServiceOptionsAccessPointCODType">
<xsd:sequence>
<xsd:element name="CurrencyCode" type="xsd:string"/>
<xsd:element name="MonetaryValue" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ReturnServiceType">
<xsd:sequence>
<xsd:element name="Code" type="xsd:string"/>
<xsd:element name="Description" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ImportControlType">
<xsd:sequence>
<xsd:element name="Code" type="xsd:string"/>
<xsd:element name="Description" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="RestrictedArticlesType">
<xsd:sequence>
<xsd:element name="AlcoholicBeveragesIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="DiagnosticSpecimensIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="PerishablesIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="PlantsIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="SeedsIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="SpecialExceptionsIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="TobaccoIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="ECigarettesIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="HempCBDIndicator" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="PickupOptionsType">
<xsd:sequence>
<xsd:element name="LiftGateAtPickupIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="HoldForPickupIndicator" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="DeliveryOptionsType">
<xsd:sequence>
<xsd:element name="LiftGateAtDeliveryIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="DropOffAtUPSFacilityIndicator" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ShipperType">
<xsd:sequence>
<xsd:element name="Name" type="xsd:string" minOccurs="0"/>
<xsd:element name="AttentionName" type="xsd:string" minOccurs="0"/>
<xsd:element name="ShipperNumber" type="xsd:string" minOccurs="0"/>
<xsd:element name="Address" type="rate:AddressType"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="GuaranteedDeliveryType">
<xsd:sequence>
<xsd:element name="BusinessDaysInTransit" type="xsd:string" minOccurs="0"/>
<xsd:element name="DeliveryByTime" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="FRSPaymentInfoType">
<xsd:sequence>
<xsd:element name="Type" type="rate:CodeDescriptionType"/>
<xsd:element name="AccountNumber" type="xsd:string" minOccurs="0"/>
<xsd:element name="Address" type="rate:PayerAddressType" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="FreightShipmentInformationType">
<xsd:sequence>
<xsd:element name="FreightDensityInfo" type="rate:FreightDensityInfoType" minOccurs="0"/>
<xsd:element name="DensityEligibleIndicator" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="PayerAddressType">
<xsd:sequence>
<xsd:element name="PostalCode" type="xsd:string" minOccurs="0"/>
<xsd:element name="CountryCode" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="FreightDensityInfoType">
<xsd:sequence>
<xsd:element name="AdjustedHeightIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="AdjustedHeight" type="rate:AdjustedHeightType" minOccurs="0"/>
<xsd:element name="HandlingUnits" type="rate:HandlingUnitsType" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="AdjustedHeightType">
<xsd:sequence>
<xsd:element name="Value" type="xsd:string"/>
<xsd:element name="UnitOfMeasurement" type="rate:CodeDescriptionType"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="HandlingUnitsType">
<xsd:sequence>
<xsd:element name="Quantity" type="xsd:string"/>
<xsd:element name="Type" type="rate:CodeDescriptionType"/>
<xsd:element name="Dimensions" type="rate:HandlingUnitsDimensionsType"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="HandlingUnitsDimensionsType">
<xsd:sequence>
<xsd:element name="UnitOfMeasurement" type="rate:CodeDescriptionType"/>
<xsd:element name="Length" type="xsd:string"/>
<xsd:element name="Width" type="xsd:string"/>
<xsd:element name="Height" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="InvoiceLineTotalType">
<xsd:sequence>
<xsd:element name="CurrencyCode" type="xsd:string" minOccurs="0"/>
<xsd:element name="MonetaryValue" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
</xsd:schema>

View File

@@ -0,0 +1,123 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- UPS Ship Service WSDL Release Date Dec 29, 2007 -->
<!-- Copyright 2007-2008 United Parcel Service of America, Inc. All rights reserved. -->
<wsdl:definitions name="Ship" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:error="http://www.ups.com/XMLSchema/XOLTWS/Error/v1.1" xmlns:upss="http://www.ups.com/XMLSchema/XOLTWS/UPSS/v1.0" xmlns:ship="http://www.ups.com/XMLSchema/XOLTWS/Ship/v1.0" xmlns:tns="http://www.ups.com/WSDL/XOLTWS/Ship/v1.0" targetNamespace="http://www.ups.com/WSDL/XOLTWS/Ship/v1.0">
<wsdl:types>
<xsd:schema>
<!-- This schema defines the UPS Security header used for authorization purposes -->
<xsd:import namespace="http://www.ups.com/XMLSchema/XOLTWS/UPSS/v1.0" schemaLocation="UPSSecurity.xsd"/>
<!-- This schema defines the error detail data types returned within SOAPFaults to provide more specific information pertaining to the problem. -->
<xsd:import namespace="http://www.ups.com/XMLSchema/XOLTWS/Error/v1.1" schemaLocation="Error1.1.xsd"/>
<!-- This schema defines the Ship service data types -->
<xsd:import namespace="http://www.ups.com/XMLSchema/XOLTWS/Ship/v1.0" schemaLocation="ShipWebServiceSchema.xsd"/>
</xsd:schema>
</wsdl:types>
<!-- Ship request/response Message Calls -->
<wsdl:message name="ShipmentRequestMessage">
<wsdl:part name="Body" element="ship:ShipmentRequest"/>
<wsdl:part name="UPSSecurity" element="upss:UPSSecurity"/>
</wsdl:message>
<wsdl:message name="ShipmentResponseMessage">
<wsdl:part name="Body" element="ship:ShipmentResponse"/>
</wsdl:message>
<wsdl:message name="ShipmentErrorMessage">
<wsdl:part name="ShipmentError" element="error:Errors"/>
</wsdl:message>
<wsdl:message name="ShipConfirmRequestMessage">
<wsdl:part name="Body" element="ship:ShipConfirmRequest"/>
<wsdl:part name="UPSSecurity" element="upss:UPSSecurity"/>
</wsdl:message>
<wsdl:message name="ShipConfirmResponseMessage">
<wsdl:part name="Body" element="ship:ShipConfirmResponse"/>
</wsdl:message>
<wsdl:message name="ShipConfirmErrorMessage">
<wsdl:part name="ShipConfirmError" element="error:Errors"/>
</wsdl:message>
<wsdl:message name="ShipAcceptRequestMessage">
<wsdl:part name="Body" element="ship:ShipAcceptRequest"/>
<wsdl:part name="UPSSecurity" element="upss:UPSSecurity"/>
</wsdl:message>
<wsdl:message name="ShipAcceptResponseMessage">
<wsdl:part name="Body" element="ship:ShipAcceptResponse"/>
</wsdl:message>
<wsdl:message name="ShipAcceptErrorMessage">
<wsdl:part name="ShipAcceptError" element="error:Errors"/>
</wsdl:message>
<!-- -->
<!-- Ship Web Service port declaration -->
<wsdl:portType name="ShipPortType">
<wsdl:operation name="ProcessShipment">
<wsdl:input name="ShipmentRequest" message="tns:ShipmentRequestMessage"/>
<wsdl:output name="ShipmentResponse" message="tns:ShipmentResponseMessage"/>
<wsdl:fault name="ShipmentError" message="tns:ShipmentErrorMessage"/>
</wsdl:operation>
<wsdl:operation name="ProcessShipConfirm">
<wsdl:input name="ShipConfirmRequest" message="tns:ShipConfirmRequestMessage"/>
<wsdl:output name="ShipConfirmResponse" message="tns:ShipConfirmResponseMessage"/>
<wsdl:fault name="ShipConfirmError" message="tns:ShipConfirmErrorMessage"/>
</wsdl:operation>
<wsdl:operation name="ProcessShipAccept">
<wsdl:input name="ShipAcceptRequest" message="tns:ShipAcceptRequestMessage"/>
<wsdl:output name="ShipAcceptResponse" message="tns:ShipAcceptResponseMessage"/>
<wsdl:fault name="ShipAcceptError" message="tns:ShipAcceptErrorMessage"/>
</wsdl:operation>
</wsdl:portType>
<!-- Ship Web Service binding -->
<wsdl:binding name="ShipBinding" type="tns:ShipPortType">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="ProcessShipment">
<soap:operation soapAction="http://onlinetools.ups.com/webservices/ShipBinding/v1.0" style="document"/>
<wsdl:input name="ShipmentRequest">
<soap:body parts="Body" use="literal"/>
<soap:header message="tns:ShipmentRequestMessage" part="UPSSecurity" use="literal">
<soap:headerfault message="tns:ShipmentErrorMessage" part="ShipmentError" use="literal"/>
</soap:header>
</wsdl:input>
<wsdl:output name="ShipmentResponse">
<soap:body parts="Body" use="literal"/>
</wsdl:output>
<wsdl:fault name="ShipmentError">
<soap:fault name="ShipmentError" use="literal"/>
</wsdl:fault>
</wsdl:operation>
<wsdl:operation name="ProcessShipConfirm">
<soap:operation soapAction="http://onlinetools.ups.com/webservices/ShipBinding/v1.0" style="document"/>
<wsdl:input name="ShipConfirmRequest">
<soap:body parts="Body" use="literal"/>
<soap:header message="tns:ShipConfirmRequestMessage" part="UPSSecurity" use="literal">
<soap:headerfault message="tns:ShipConfirmErrorMessage" part="ShipConfirmError" use="literal"/>
</soap:header>
</wsdl:input>
<wsdl:output name="ShipConfirmResponse">
<soap:body parts="Body" use="literal"/>
</wsdl:output>
<wsdl:fault name="ShipConfirmError">
<soap:fault name="ShipConfirmError" use="literal"/>
</wsdl:fault>
</wsdl:operation>
<wsdl:operation name="ProcessShipAccept">
<soap:operation soapAction="http://onlinetools.ups.com/webservices/ShipBinding/v1.0" style="document"/>
<wsdl:input name="ShipAcceptRequest">
<soap:body parts="Body" use="literal"/>
<soap:header message="tns:ShipAcceptRequestMessage" part="UPSSecurity" use="literal">
<soap:headerfault message="tns:ShipAcceptErrorMessage" part="ShipAcceptError" use="literal"/>
</soap:header>
</wsdl:input>
<wsdl:output name="ShipAcceptResponse">
<soap:body parts="Body" use="literal"/>
</wsdl:output>
<wsdl:fault name="ShipAcceptError">
<soap:fault name="ShipAcceptError" use="literal"/>
</wsdl:fault>
</wsdl:operation>
</wsdl:binding>
<!-- Ship Web Service-->
<wsdl:service name="ShipService">
<wsdl:port name="ShipPort" binding="tns:ShipBinding">
<!-- Production URL -->
<!-- <soap:address location="https://onlinetools.ups.com/webservices/Ship"/> -->
<!-- CIE (Customer Integration Environment) URL -->
<soap:address location="https://wwwcie.ups.com/webservices/Ship"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>

View File

@@ -0,0 +1,933 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xsd:schema targetNamespace="http://www.ups.com/XMLSchema/XOLTWS/Ship/v1.0" xmlns:ups="http://www.ups.com/XMLSchema" xmlns:ship="http://www.ups.com/XMLSchema/XOLTWS/Ship/v1.0" xmlns:ifs="http://www.ups.com/XMLSchema/XOLTWS/IF/v1.0" xmlns:common="http://www.ups.com/XMLSchema/XOLTWS/Common/v1.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" version="201801">
<xsd:import namespace="http://www.ups.com/XMLSchema/XOLTWS/Common/v1.0" schemaLocation="common.xsd"/>
<xsd:import namespace="http://www.ups.com/XMLSchema/XOLTWS/IF/v1.0" schemaLocation="IFWS.xsd"/>
<xsd:element name="ShipmentRequest">
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="common:Request"/>
<xsd:element name="Shipment" type="ship:ShipmentType"/>
<xsd:element name="LabelSpecification" type="ship:LabelSpecificationType" minOccurs="0"/>
<xsd:element name="ReceiptSpecification" type="ship:ReceiptSpecificationType" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="ShipConfirmRequest">
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="common:Request"/>
<xsd:element name="Shipment" type="ship:ShipmentType"/>
<xsd:element name="LabelSpecification" type="ship:LabelSpecificationType" minOccurs="0"/>
<xsd:element name="ReceiptSpecification" type="ship:ReceiptSpecificationType" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="ShipAcceptRequest">
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="common:Request"/>
<xsd:element name="ShipmentDigest" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="ShipmentResponse">
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="common:Response"/>
<xsd:element name="ShipmentResults" type="ship:ShipmentResultsType"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="ShipConfirmResponse">
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="common:Response"/>
<xsd:element name="ShipmentResults" type="ship:ShipmentResultsType"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="ShipAcceptResponse">
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="common:Response"/>
<xsd:element name="ShipmentResults" type="ship:ShipmentResultsType"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:complexType name="ShipmentType">
<xsd:sequence>
<xsd:element name="Description" type="xsd:string" minOccurs="0"/>
<xsd:element name="ReturnService" type="ship:ReturnServiceType" minOccurs="0"/>
<xsd:element name="DocumentsOnlyIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="Shipper" type="ship:ShipperType"/>
<xsd:element name="ShipTo" type="ship:ShipToType"/>
<xsd:element name="AlternateDeliveryAddress" type="ship:AlternateDeliveryAddressType" minOccurs="0"/>
<xsd:element name="ShipFrom" type="ship:ShipFromType" minOccurs="0"/>
<xsd:element name="PaymentInformation" type="ship:PaymentInfoType" minOccurs="0"/>
<xsd:element name="FRSPaymentInformation" type="ship:FRSPaymentInfoType" minOccurs="0"/>
<xsd:element name="FreightShipmentInformation" type="ship:FreightShipmentInformationType" minOccurs="0"/>
<xsd:element name="GoodsNotInFreeCirculationIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="ShipmentRatingOptions" type="ship:RateInfoType" minOccurs="0"/>
<xsd:element name="MovementReferenceNumber" type="xsd:string" minOccurs="0"/>
<xsd:element name="ReferenceNumber" type="ship:ReferenceNumberType" minOccurs="0" maxOccurs="2"/>
<xsd:element name="Service" type="ship:ServiceType"/>
<xsd:element name="InvoiceLineTotal" type="ship:CurrencyMonetaryType" minOccurs="0"/>
<xsd:element name="NumOfPiecesInShipment" type="xsd:string" minOccurs="0"/>
<xsd:element name="USPSEndorsement" type="xsd:string" minOccurs="0"/>
<xsd:element name="MILabelCN22Indicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="SubClassification" type="xsd:string" minOccurs="0"/>
<xsd:element name="CostCenter" type="xsd:string" minOccurs="0"/>
<xsd:element name="CostCenterBarcodeIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="PackageID" type="xsd:string" minOccurs="0"/>
<xsd:element name="PackageIDBarcodeIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="IrregularIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="ShipmentIndicationType" type="ship:IndicationType" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="MIDualReturnShipmentKey" type="xsd:string" minOccurs="0"/>
<xsd:element name="MIDualReturnShipmentIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="RatingMethodRequestedIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="TaxInformationIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="PromotionalDiscountInformation" type="ship:PromotionalDiscountInformationType" minOccurs="0"/>
<xsd:element name="DGSignatoryInfo" type="ship:DGSignatoryInfoType" minOccurs="0"/>
<xsd:element name="MasterCartonID" type="xsd:string" minOccurs="0"/>
<xsd:element name="MasterCartonIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="Locale" type="xsd:string" minOccurs="0"/>
<xsd:element name="ShipmentValueThresholdCode" type="xsd:string" minOccurs="0"/>
<xsd:element name="ShipmentServiceOptions" minOccurs="0">
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="ship:ShipmentServiceOptionsType"/>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
<xsd:element name="Package" type="ship:PackageType" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="PromotionalDiscountInformationType">
<xsd:sequence>
<xsd:element name="PromoCode" type="xsd:string"/>
<xsd:element name="PromoAliasCode" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ReturnServiceType">
<xsd:sequence>
<xsd:element name="Code" type="xsd:string"/>
<xsd:element name="Description" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ShipperType">
<xsd:complexContent>
<xsd:extension base="ship:CompanyInfoType">
<xsd:sequence>
<xsd:element name="ShipperNumber" type="xsd:string" minOccurs="0"/>
<xsd:element name="FaxNumber" type="xsd:string" minOccurs="0"/>
<xsd:element name="EMailAddress" type="xsd:string" minOccurs="0"/>
<xsd:element name="Address" type="ship:ShipAddressType"/>
</xsd:sequence>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="CompanyInfoType">
<xsd:sequence>
<xsd:element name="Name" type="xsd:string"/>
<xsd:element name="AttentionName" type="xsd:string" minOccurs="0"/>
<xsd:element name="CompanyDisplayableName" type="xsd:string" minOccurs="0"/>
<xsd:element name="TaxIdentificationNumber" type="xsd:string" minOccurs="0"/>
<xsd:element name="TaxIDType" type="ship:TaxIDCodeDescType" minOccurs="0"/>
<xsd:element name="Phone" type="ship:ShipPhoneType" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ShipPhoneType">
<xsd:sequence>
<xsd:element name="Number" type="xsd:string"/>
<xsd:element name="Extension" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ShipAddressType">
<xsd:sequence>
<xsd:element name="AddressLine" type="xsd:string" maxOccurs="3"/>
<xsd:element name="City" type="xsd:string"/>
<xsd:element name="StateProvinceCode" type="xsd:string" minOccurs="0"/>
<xsd:element name="PostalCode" type="xsd:string" minOccurs="0"/>
<xsd:element name="CountryCode" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ShipToType">
<xsd:complexContent>
<xsd:extension base="ship:CompanyInfoType">
<xsd:sequence>
<xsd:element name="FaxNumber" type="xsd:string" minOccurs="0"/>
<xsd:element name="EMailAddress" type="xsd:string" minOccurs="0"/>
<xsd:element name="Address" type="ship:ShipToAddressType"/>
<xsd:element name="LocationID" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="ShipToAddressType">
<xsd:complexContent>
<xsd:extension base="ship:ShipAddressType">
<xsd:sequence>
<xsd:element name="ResidentialAddressIndicator" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="ShipFromType">
<xsd:complexContent>
<xsd:extension base="ship:CompanyInfoType">
<xsd:sequence>
<xsd:element name="FaxNumber" type="xsd:string" minOccurs="0"/>
<xsd:element name="Address" type="ship:ShipAddressType"/>
<xsd:element name="EMailAddress" type="xsd:string" minOccurs="0"/>
<xsd:element name="VendorInfo" type="ship:VendorInfoType" minOccurs="0"/>
</xsd:sequence>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="PrepaidType">
<xsd:sequence>
<xsd:element name="BillShipper" type="ship:BillShipperType"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="BillShipperType">
<xsd:sequence>
<xsd:element name="AccountNumber" type="xsd:string" minOccurs="0"/>
<xsd:element name="CreditCard" type="ship:CreditCardType" minOccurs="0"/>
<xsd:element name="AlternatePaymentMethod" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="CreditCardType">
<xsd:sequence>
<xsd:element name="Type" type="xsd:string"/>
<xsd:element name="Number" type="xsd:string"/>
<xsd:element name="ExpirationDate" type="xsd:string"/>
<xsd:element name="SecurityCode" type="xsd:string"/>
<xsd:element name="Address" type="ship:CreditCardAddressType" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="CreditCardAddressType">
<xsd:sequence>
<xsd:element name="AddressLine" type="xsd:string" maxOccurs="3"/>
<xsd:element name="City" type="xsd:string"/>
<xsd:element name="StateProvinceCode" type="xsd:string"/>
<xsd:element name="PostalCode" type="xsd:string"/>
<xsd:element name="CountryCode" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="BillThirdPartyChargeType">
<xsd:sequence>
<xsd:element name="AccountNumber" type="xsd:string"/>
<xsd:element name="Address" type="ship:AccountAddressType"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="AccountAddressType">
<xsd:sequence>
<xsd:element name="PostalCode" type="xsd:string" minOccurs="0"/>
<xsd:element name="CountryCode" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="FreightCollectType">
<xsd:sequence>
<xsd:element name="BillReceiver" type="ship:BillReceiverType"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="BillReceiverType">
<xsd:sequence>
<xsd:element name="AccountNumber" type="xsd:string"/>
<xsd:element name="Address" type="ship:BillReceiverAddressType" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="BillReceiverAddressType">
<xsd:sequence>
<xsd:element name="PostalCode" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="PaymentInfoType">
<xsd:sequence>
<xsd:element name="ShipmentCharge" type="ship:ShipmentChargeType" maxOccurs="2"/>
<xsd:element name="SplitDutyVATIndicator" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ShipmentChargeType">
<xsd:sequence>
<xsd:element name="Type" type="xsd:string"/>
<xsd:element name="BillShipper" type="ship:BillShipperType" minOccurs="0"/>
<xsd:element name="BillReceiver" type="ship:BillReceiverType" minOccurs="0"/>
<xsd:element name="BillThirdParty" type="ship:BillThirdPartyChargeType" minOccurs="0"/>
<xsd:element name="ConsigneeBilledIndicator" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="FRSPaymentInfoType">
<xsd:sequence>
<xsd:element name="Type" type="ship:PaymentType"/>
<xsd:element name="AccountNumber" type="xsd:string"/>
<xsd:element name="Address" type="ship:AccountAddressType" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="PaymentType">
<xsd:sequence>
<xsd:element name="Code" type="xsd:string"/>
<xsd:element name="Description" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="RateInfoType">
<xsd:sequence>
<xsd:element name="NegotiatedRatesIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="FRSShipmentIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="RateChartIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="TPFCNegotiatedRatesIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="UserLevelDiscountIndicator" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ReferenceNumberType">
<xsd:sequence>
<xsd:element name="BarCodeIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="Code" type="xsd:string" minOccurs="0"/>
<xsd:element name="Value" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ServiceType">
<xsd:sequence>
<xsd:element name="Code" type="xsd:string"/>
<xsd:element name="Description" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="CurrencyMonetaryType">
<xsd:sequence>
<xsd:element name="CurrencyCode" type="xsd:string"/>
<xsd:element name="MonetaryValue" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ShipmentServiceOptionsType">
<xsd:sequence>
<xsd:element name="SaturdayDeliveryIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="SaturdayPickupIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="COD" type="ship:CODType" minOccurs="0"/>
<xsd:element name="AccessPointCOD" type="ship:ShipmentServiceOptionsAccessPointCODType" minOccurs="0"/>
<xsd:element name="DeliverToAddresseeOnlyIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="DirectDeliveryOnlyIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="Notification" type="ship:NotificationType" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="LabelDelivery" type="ship:LabelDeliveryType" minOccurs="0"/>
<xsd:element name="InternationalForms" type="ifs:InternationalFormType" minOccurs="0"/>
<xsd:element name="DeliveryConfirmation" type="ship:DeliveryConfirmationType" minOccurs="0"/>
<xsd:element name="ReturnOfDocumentIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="ImportControlIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="LabelMethod" type="ship:LabelMethodType" minOccurs="0"/>
<xsd:element name="CommercialInvoiceRemovalIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="UPScarbonneutralIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="PreAlertNotification" type="ship:PreAlertNotificationType" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="ExchangeForwardIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="HoldForPickupIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="DropoffAtUPSFacilityIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="LiftGateForPickUpIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="LiftGateForDeliveryIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="SDLShipmentIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="EPRAReleaseCode" type="xsd:string" minOccurs="0"/>
<xsd:element name="RestrictedArticles" type="ship:RestrictedArticlesType" minOccurs="0"/>
<xsd:element name="InsideDelivery" type="xsd:string" minOccurs="0"/>
<xsd:element name="ItemDisposal" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="RestrictedArticlesType">
<xsd:sequence>
<xsd:element name="DiagnosticSpecimensIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="AlcoholicBeveragesIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="PerishablesIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="PlantsIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="SeedsIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="SpecialExceptionsIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="TobaccoIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="ECigarettesIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="HempCBDIndicator" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="PreAlertNotificationType">
<xsd:sequence>
<xsd:element name="EMailMessage" type="ship:PreAlertEMailMessageType" minOccurs="0"/>
<xsd:element name="VoiceMessage" type="ship:PreAlertVoiceMessageType" minOccurs="0"/>
<xsd:element name="TextMessage" type="ship:PreAlertTextMessageType" minOccurs="0"/>
<xsd:element name="Locale" type="ship:LocaleType"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="PreAlertEMailMessageType">
<xsd:sequence>
<xsd:element name="EMailAddress" type="xsd:string"/>
<xsd:element name="UndeliverableEMailAddress" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="LocaleType">
<xsd:sequence>
<xsd:element name="Language" type="xsd:string"/>
<xsd:element name="Dialect" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="PreAlertVoiceMessageType">
<xsd:sequence>
<xsd:element name="PhoneNumber" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="PreAlertTextMessageType">
<xsd:sequence>
<xsd:element name="PhoneNumber" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ContactInfoType">
<xsd:sequence>
<xsd:element name="Name" type="xsd:string" minOccurs="0"/>
<xsd:element name="Phone" type="ship:ShipPhoneType" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="CODType">
<xsd:sequence>
<xsd:element name="CODFundsCode" type="xsd:string"/>
<xsd:element name="CODAmount" type="ship:CurrencyMonetaryType"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ShipmentServiceOptionsAccessPointCODType">
<xsd:sequence>
<xsd:element name="CurrencyCode" type="xsd:string"/>
<xsd:element name="MonetaryValue" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="NotificationType">
<xsd:sequence>
<xsd:element name="NotificationCode" type="xsd:string"/>
<xsd:element name="EMail" type="ship:EmailDetailsType"/>
<xsd:element name="VoiceMessage" type="ship:ShipmentServiceOptionsNotificationVoiceMessageType" minOccurs="0"/>
<xsd:element name="TextMessage" type="ship:ShipmentServiceOptionsNotificationTextMessageType" minOccurs="0"/>
<xsd:element name="Locale" type="ship:LocaleType" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="LabelDeliveryType">
<xsd:sequence>
<xsd:element name="EMail" type="ship:EmailDetailsType" minOccurs="0"/>
<xsd:element name="LabelLinksIndicator" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="EmailDetailsType">
<xsd:sequence>
<xsd:element name="EMailAddress" type="xsd:string" maxOccurs="unbounded"/>
<xsd:element name="UndeliverableEMailAddress" type="xsd:string" minOccurs="0"/>
<xsd:element name="FromEMailAddress" type="xsd:string" minOccurs="0"/>
<xsd:element name="FromName" type="xsd:string" minOccurs="0"/>
<xsd:element name="Memo" type="xsd:string" minOccurs="0"/>
<xsd:element name="Subject" type="xsd:string" minOccurs="0"/>
<xsd:element name="SubjectCode" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="PackageType">
<xsd:sequence>
<xsd:element name="Description" type="xsd:string" minOccurs="0"/>
<xsd:element name="PalletDescription" type="xsd:string" minOccurs="0"/>
<xsd:element name="NumOfPieces" type="xsd:string" minOccurs="0"/>
<xsd:element name="UnitPrice" type="xsd:string" minOccurs="0"/>
<xsd:element name="Packaging" type="ship:PackagingType" minOccurs="0"/>
<xsd:element name="Dimensions" type="ship:DimensionsType" minOccurs="0"/>
<xsd:element name="DimWeight" type="ship:PackageWeightType" minOccurs="0"/>
<xsd:element name="PackageWeight" type="ship:PackageWeightType" minOccurs="0"/>
<xsd:element name="LargePackageIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="ReferenceNumber" type="ship:ReferenceNumberType" minOccurs="0" maxOccurs="5"/>
<xsd:element name="AdditionalHandlingIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="PackageServiceOptions" type="ship:PackageServiceOptionsType" minOccurs="0"/>
<xsd:element name="Commodity" type="ship:CommodityType" minOccurs="0"/>
<xsd:element name="HazMatPackageInformation" type="ship:HazMatPackageInformationType" minOccurs="0"/>
<xsd:element name="SimpleRate" type="ship:CodeDescriptionType" minOccurs="0"/>
<xsd:element name="UPSPremier" type="ship:UPSPremierType" minOccurs="0"/>
<xsd:element name="OversizeIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="MinimumBillableWeightIndicator" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="PackagingType">
<xsd:sequence>
<xsd:element name="Code" type="xsd:string"/>
<xsd:element name="Description" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="DimensionsType">
<xsd:sequence>
<xsd:element name="UnitOfMeasurement" type="ship:ShipUnitOfMeasurementType"/>
<xsd:element name="Length" type="xsd:string"/>
<xsd:element name="Width" type="xsd:string"/>
<xsd:element name="Height" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ShipUnitOfMeasurementType">
<xsd:sequence>
<xsd:element name="Code" type="xsd:string"/>
<xsd:element name="Description" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="PackageWeightType">
<xsd:sequence>
<xsd:element name="UnitOfMeasurement" type="ship:ShipUnitOfMeasurementType"/>
<xsd:element name="Weight" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="PackageServiceOptionsType">
<xsd:sequence>
<xsd:element name="DeliveryConfirmation" type="ship:DeliveryConfirmationType" minOccurs="0"/>
<xsd:element name="DeclaredValue" type="ship:PackageDeclaredValueType" minOccurs="0"/>
<xsd:element name="COD" type="ship:PSOCODType" minOccurs="0"/>
<xsd:element name="AccessPointCOD" type="ship:PackageServiceOptionsAccessPointCODType" minOccurs="0"/>
<xsd:element name="ShipperReleaseIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="Notification" type="ship:PSONotificationType" minOccurs="0"/>
<xsd:element name="HazMat" type="ship:HazMatType" minOccurs="0" maxOccurs="3"/>
<xsd:element name="DryIce" type="ship:DryIceType" minOccurs="0"/>
<xsd:element name="UPSPremiumCareIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="ProactiveIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="PackageIdentifier" type="xsd:string" minOccurs="0"/>
<xsd:element name="ClinicalTrialsID" type="xsd:string" minOccurs="0"/>
<xsd:element name="RefrigerationIndicator" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="PackageDeclaredValueType">
<xsd:sequence>
<xsd:element name="Type" type="ship:DeclaredValueType" minOccurs="0"/>
<xsd:element name="CurrencyCode" type="xsd:string"/>
<xsd:element name="MonetaryValue" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="DeclaredValueType">
<xsd:sequence>
<xsd:element name="Code" type="xsd:string"/>
<xsd:element name="Description" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="DeliveryConfirmationType">
<xsd:sequence>
<xsd:element name="DCISType" type="xsd:string"/>
<xsd:element name="DCISNumber" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="LabelMethodType">
<xsd:sequence>
<xsd:element name="Code" type="xsd:string"/>
<xsd:element name="Description" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="PSOCODType">
<xsd:sequence>
<xsd:element name="CODFundsCode" type="xsd:string"/>
<xsd:element name="CODAmount" type="ship:CurrencyMonetaryType"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="PackageServiceOptionsAccessPointCODType">
<xsd:sequence>
<xsd:element name="CurrencyCode" type="xsd:string"/>
<xsd:element name="MonetaryValue" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="PSONotificationType">
<xsd:sequence>
<xsd:element name="NotificationCode" type="xsd:string"/>
<xsd:element name="EMail" type="ship:EmailDetailsType"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="LabelSpecificationType">
<xsd:sequence>
<xsd:element name="LabelImageFormat" type="ship:LabelImageFormatType"/>
<xsd:element name="HTTPUserAgent" type="xsd:string" minOccurs="0"/>
<xsd:element name="LabelStockSize" type="ship:LabelStockSizeType" minOccurs="0"/>
<xsd:element name="Instruction" type="ship:InstructionCodeDescriptionType" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="CharacterSet" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="InstructionCodeDescriptionType">
<xsd:sequence>
<xsd:element name="Code" type="xsd:string"/>
<xsd:element name="Description" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="LabelImageFormatType">
<xsd:sequence>
<xsd:element name="Code" type="xsd:string"/>
<xsd:element name="Description" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="LabelStockSizeType">
<xsd:sequence>
<xsd:element name="Height" type="xsd:string"/>
<xsd:element name="Width" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="CommodityType">
<xsd:sequence>
<xsd:element name="FreightClass" type="xsd:string"/>
<xsd:element name="NMFC" type="ship:NMFCType" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="NMFCType">
<xsd:sequence>
<xsd:element name="PrimeCode" type="xsd:string"/>
<xsd:element name="SubCode" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ShipmentResultsType">
<xsd:sequence>
<xsd:element name="Disclaimer" type="ship:DisclaimerType" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="ShipmentCharges" type="ship:ShipmentChargesType" minOccurs="0"/>
<xsd:element name="NegotiatedRateCharges" type="ship:NegotiatedRateChargesType" minOccurs="0"/>
<xsd:element name="FRSShipmentData" type="ship:FRSShipmentDataType" minOccurs="0"/>
<xsd:element name="RatingMethod" type="xsd:string" minOccurs="0"/>
<xsd:element name="BillableWeightCalculationMethod" type="xsd:string" minOccurs="0"/>
<xsd:element name="BillingWeight" type="ship:BillingWeightType"/>
<xsd:element name="ShipmentIdentificationNumber" type="xsd:string" minOccurs="0"/>
<xsd:element name="MIDualReturnShipmentKey" type="xsd:string" minOccurs="0"/>
<xsd:element name="ShipmentDigest" type="xsd:string" minOccurs="0"/>
<xsd:element name="PackageResults" type="ship:PackageResultsType" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="ControlLogReceipt" type="ship:ImageType" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="Form" type="ship:FormType" minOccurs="0"/>
<xsd:element name="CODTurnInPage" type="ship:SCReportType" minOccurs="0"/>
<xsd:element name="HighValueReport" type="ship:HighValueReportType" minOccurs="0"/>
<xsd:element name="LabelURL" type="xsd:string" minOccurs="0"/>
<xsd:element name="LocalLanguageLabelURL" type="xsd:string" minOccurs="0"/>
<xsd:element name="ReceiptURL" type="xsd:string" minOccurs="0"/>
<xsd:element name="LocalLanguageReceiptURL" type="xsd:string" minOccurs="0"/>
<xsd:element name="DGPaperImage" type="xsd:string" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="MasterCartonID" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="DisclaimerType">
<xsd:sequence>
<xsd:element name="Code" type="xsd:string"/>
<xsd:element name="Description" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ShipmentChargesType">
<xsd:sequence>
<xsd:element name="RateChart" type="xsd:string" minOccurs="0"/>
<xsd:element name="BaseServiceCharge" type="ship:ShipChargeType" minOccurs="0"/>
<xsd:element name="TransportationCharges" type="ship:ShipChargeType"/>
<xsd:element name="ItemizedCharges" type="ship:ShipChargeType" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="ServiceOptionsCharges" type="ship:ShipChargeType"/>
<xsd:element name="TaxCharges" type="ship:TaxChargeType" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="TotalCharges" type="ship:ShipChargeType"/>
<xsd:element name="TotalChargesWithTaxes" type="ship:ShipChargeType" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="NegotiatedRateChargesType">
<xsd:sequence>
<xsd:element name="ItemizedCharges" type="ship:ShipChargeType" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="TaxCharges" type="ship:TaxChargeType" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="TotalCharge" type="ship:ShipChargeType" minOccurs="0"/>
<xsd:element name="TotalChargesWithTaxes" type="ship:ShipChargeType" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ShipChargeType">
<xsd:sequence>
<xsd:element name="Code" type="xsd:string" minOccurs="0"/>
<xsd:element name="Description" type="xsd:string" minOccurs="0"/>
<xsd:element name="CurrencyCode" type="xsd:string"/>
<xsd:element name="MonetaryValue" type="xsd:string"/>
<xsd:element name="SubType" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="TaxChargeType">
<xsd:sequence>
<xsd:element name="Type" type="xsd:string"/>
<xsd:element name="MonetaryValue" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="FRSShipmentDataType">
<xsd:sequence>
<xsd:element name="TransportationCharges" type="ship:TransportationChargeType"/>
<xsd:element name="FreightDensityRate" type="ship:FreightDensityRateType" minOccurs="0"/>
<xsd:element name="HandlingUnits" type="ship:HandlingUnitsResponseType" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="TransportationChargeType">
<xsd:sequence>
<xsd:element name="GrossCharge" type="ship:ShipChargeType"/>
<xsd:element name="DiscountAmount" type="ship:ShipChargeType"/>
<xsd:element name="DiscountPercentage" type="xsd:string"/>
<xsd:element name="NetCharge" type="ship:ShipChargeType"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="BillingWeightType">
<xsd:sequence>
<xsd:element name="UnitOfMeasurement" type="ship:BillingUnitOfMeasurementType"/>
<xsd:element name="Weight" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="BillingUnitOfMeasurementType">
<xsd:sequence>
<xsd:element name="Code" type="xsd:string"/>
<xsd:element name="Description" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="PackageResultsType">
<xsd:sequence>
<xsd:element name="TrackingNumber" type="xsd:string"/>
<xsd:element name="BaseServiceCharge" type="ship:ShipChargeType" minOccurs="0"/>
<xsd:element name="ServiceOptionsCharges" type="ship:ShipChargeType" minOccurs="0"/>
<xsd:element name="ShippingLabel" type="ship:LabelType" minOccurs="0"/>
<xsd:element name="ShippingReceipt" type="ship:ReceiptType" minOccurs="0"/>
<xsd:element name="USPSPICNumber" type="xsd:string" minOccurs="0"/>
<xsd:element name="CN22Number" type="xsd:string" minOccurs="0"/>
<xsd:element name="Accessorial" type="ship:AccessorialType" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="Form" type="ship:FormType" minOccurs="0"/>
<xsd:element name="ItemizedCharges" type="ship:ShipChargeType" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="NegotiatedCharges" type="ship:NegotiatedChargesType" minOccurs="0"/>
<xsd:element name="SimpleRate" type="ship:SimpleRateType" minOccurs="0"/>
<xsd:element name="SensorID" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="AccessorialType">
<xsd:sequence>
<xsd:element name="Code" type="xsd:string"/>
<xsd:element name="Description" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="LabelType">
<xsd:complexContent>
<xsd:extension base="ship:ImageType">
<xsd:sequence>
<xsd:element name="GraphicImagePart" type="xsd:string" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="InternationalSignatureGraphicImage" type="xsd:string" minOccurs="0"/>
<xsd:element name="HTMLImage" type="xsd:string" minOccurs="0"/>
<xsd:element name="PDF417" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="ReceiptType">
<xsd:complexContent>
<xsd:extension base="ship:ImageType"/>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="ImageType">
<xsd:sequence>
<xsd:element name="ImageFormat" type="ship:ImageFormatType"/>
<xsd:element name="GraphicImage" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="FormType">
<xsd:sequence>
<xsd:element name="Code" type="xsd:string"/>
<xsd:element name="Description" type="xsd:string"/>
<xsd:element name="Image" type="ship:FormImageType" minOccurs="0"/>
<xsd:element name="FormGroupId" type="xsd:string" minOccurs="0"/>
<xsd:element name="FormGroupIdName" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="FormImageType">
<xsd:sequence>
<xsd:element name="ImageFormat" type="ship:ImageFormatType" minOccurs="0"/>
<xsd:element name="GraphicImage" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ImageFormatType">
<xsd:sequence>
<xsd:element name="Code" type="xsd:string"/>
<xsd:element name="Description" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="SCReportType">
<xsd:sequence>
<xsd:element name="Image" type="ship:ImageType"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="HighValueReportType">
<xsd:sequence>
<xsd:element name="Image" type="ship:ImageType"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="HazMatPackageInformationType">
<xsd:sequence>
<xsd:element name="AllPackedInOneIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="OverPackedIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="QValue" type="xsd:string" minOccurs="0"/>
<xsd:element name="OuterPackagingType" type="xsd:string" minOccurs="0" />
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="HazMatType">
<xsd:sequence>
<xsd:element name="PackagingTypeQuantity" type="xsd:string" minOccurs="0"/>
<xsd:element name="RecordIdentifier1" type="xsd:string" minOccurs="0"/>
<xsd:element name="RecordIdentifier2" type="xsd:string" minOccurs="0"/>
<xsd:element name="RecordIdentifier3" type="xsd:string" minOccurs="0"/>
<xsd:element name="SubRiskClass" type="xsd:string" minOccurs="0"/>
<xsd:element name="aDRItemNumber" type="xsd:string" minOccurs="0"/>
<xsd:element name="aDRPackingGroupLetter" type="xsd:string" minOccurs="0"/>
<xsd:element name="TechnicalName" type="xsd:string" minOccurs="0"/>
<xsd:element name="HazardLabelRequired" type="xsd:string" minOccurs="0"/>
<xsd:element name="ClassDivisionNumber" type="xsd:string"/>
<xsd:element name="ReferenceNumber" type="xsd:string" minOccurs="0"/>
<xsd:element name="Quantity" type="xsd:string"/>
<xsd:element name="UOM" type="xsd:string"/>
<xsd:element name="PackagingType" type="xsd:string"/>
<xsd:element name="IDNumber" type="xsd:string"/>
<xsd:element name="ProperShippingName" type="xsd:string"/>
<xsd:element name="AdditionalDescription" type="xsd:string" minOccurs="0"/>
<xsd:element name="PackagingGroupType" type="xsd:string" minOccurs="0"/>
<xsd:element name="PackagingInstructionCode" type="xsd:string" minOccurs="0"/>
<xsd:element name="EmergencyPhone" type="xsd:string" minOccurs="0"/>
<xsd:element name="EmergencyContact" type="xsd:string" minOccurs="0"/>
<xsd:element name="ReportableQuantity" type="xsd:string" minOccurs="0"/>
<xsd:element name="RegulationSet" type="xsd:string"/>
<xsd:element name="TransportationMode" type="xsd:string"/>
<xsd:element name="CommodityRegulatedLevelCode" type="xsd:string" minOccurs="0"/>
<xsd:element name="TransportCategory" type="xsd:string" minOccurs="0"/>
<xsd:element name="TunnelRestrictionCode" type="xsd:string" minOccurs="0"/>
<xsd:element name="ChemicalRecordIdentifier" type="xsd:string" minOccurs="0"/>
<xsd:element name="LocalTechnicalName" type="xsd:string" minOccurs="0"/>
<xsd:element name="LocalProperShippingName" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="DryIceType">
<xsd:sequence>
<xsd:element name="RegulationSet" type="xsd:string"/>
<xsd:element name="DryIceWeight" type="ship:DryIceWeightType"/>
<xsd:element name="MedicalUseIndicator" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="DryIceWeightType">
<xsd:sequence>
<xsd:element name="UnitOfMeasurement" type="ship:ShipUnitOfMeasurementType"/>
<xsd:element name="Weight" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ReceiptSpecificationType">
<xsd:sequence>
<xsd:element name="ImageFormat" type="ship:ReceiptImageFormatType"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ReceiptImageFormatType">
<xsd:sequence>
<xsd:element name="Code" type="xsd:string"/>
<xsd:element name="Description" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="TaxIDCodeDescType">
<xsd:sequence>
<xsd:element name="Code" type="xsd:string"/>
<xsd:element name="Description" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="IndicationType">
<xsd:sequence>
<xsd:element name="Code" type="xsd:string"/>
<xsd:element name="Description" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="AlternateDeliveryAddressType">
<xsd:sequence>
<xsd:element name="Name" type="xsd:string" minOccurs="0"/>
<xsd:element name="AttentionName" type="xsd:string" minOccurs="0"/>
<xsd:element name="UPSAccessPointID" type="xsd:string" minOccurs="0"/>
<xsd:element name="Address" type="ship:ADLAddressType"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ShipmentServiceOptionsNotificationVoiceMessageType">
<xsd:sequence>
<xsd:element name="PhoneNumber" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ShipmentServiceOptionsNotificationTextMessageType">
<xsd:sequence>
<xsd:element name="PhoneNumber" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ADLAddressType">
<xsd:sequence>
<xsd:element name="AddressLine" type="xsd:string" maxOccurs="3"/>
<xsd:element name="City" type="xsd:string"/>
<xsd:element name="StateProvinceCode" type="xsd:string" minOccurs="0"/>
<xsd:element name="PostalCode" type="xsd:string" minOccurs="0"/>
<xsd:element name="CountryCode" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="FreightShipmentInformationType">
<xsd:sequence>
<xsd:element name="FreightDensityInfo" type="ship:FreightDensityInfoType" minOccurs="0"/>
<xsd:element name="DensityEligibleIndicator" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="HandlingUnitsType">
<xsd:sequence>
<xsd:element name="Quantity" type="xsd:string"/>
<xsd:element name="Type" type="ship:ShipUnitOfMeasurementType"/>
<xsd:element name="Dimensions" type="ship:HandlingUnitsDimensionsType"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="HandlingUnitsResponseType">
<xsd:sequence>
<xsd:element name="Quantity" type="xsd:string"/>
<xsd:element name="Type" type="ship:ShipUnitOfMeasurementType"/>
<xsd:element name="Dimensions" type="ship:HandlingUnitsDimensionsType"/>
<xsd:element name="AdjustedHeight" type="ship:AdjustedHeightType" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="HandlingUnitsDimensionsType">
<xsd:sequence>
<xsd:element name="UnitOfMeasurement" type="ship:ShipUnitOfMeasurementType"/>
<xsd:element name="Length" type="xsd:string"/>
<xsd:element name="Width" type="xsd:string"/>
<xsd:element name="Height" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="FreightDensityRateType">
<xsd:sequence>
<xsd:element name="Density" type="xsd:string" minOccurs="0"/>
<xsd:element name="TotalCubicFeet" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="FreightDensityInfoType">
<xsd:sequence>
<xsd:element name="AdjustedHeightIndicator" type="xsd:string" minOccurs="0"/>
<xsd:element name="AdjustedHeight" type="ship:AdjustedHeightType" minOccurs="0"/>
<xsd:element name="HandlingUnits" type="ship:HandlingUnitsType" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="AdjustedHeightType">
<xsd:sequence>
<xsd:element name="Value" type="xsd:string"/>
<xsd:element name="UnitOfMeasurement" type="ship:ShipUnitOfMeasurementType"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="NegotiatedChargesType">
<xsd:sequence>
<xsd:element name="ItemizedCharges" type="ship:ShipChargeType" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="DGSignatoryInfoType">
<xsd:sequence>
<xsd:element name="Name" type="xsd:string" minOccurs="0"/>
<xsd:element name="Title" type="xsd:string" minOccurs="0"/>
<xsd:element name="Place" type="xsd:string" minOccurs="0"/>
<xsd:element name="Date" type="xsd:string" minOccurs="0"/>
<xsd:element name="ShipperDeclaration" type="xsd:string" minOccurs="0"/>
<xsd:element name="UploadOnlyIndicator" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="UPSPremierType">
<xsd:sequence>
<xsd:element name="Category" type="xsd:string"/>
<xsd:element name="SensorID" type="xsd:string" minOccurs="0"/>
<xsd:element name="HandlingInstructions" type="ship:HandlingInstructionsType" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="HandlingInstructionsType">
<xsd:sequence>
<xsd:element name="Instruction" type="xsd:string" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="CodeDescriptionType">
<xsd:sequence>
<xsd:element name="Code" type="xsd:string" minOccurs="0"/>
<xsd:element name="Description" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="SimpleRateType">
<xsd:sequence>
<xsd:element name="Code" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="VendorInfoType">
<xsd:sequence>
<xsd:element name="VendorCollectIDTypeCode" type="xsd:string" minOccurs="0"/>
<xsd:element name="VendorCollectIDNumber" type="xsd:string" minOccurs="0"/>
<xsd:element name="ConsigneeType" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
</xsd:schema>

View File

@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- UPS Time In Transit Service WSDL Release Date Dec 29, 2007 -->
<!-- Copyright 2007-2008 United Parcel Service of America, Inc. All rights reserved. -->
<wsdl:definitions name="TimeInTransit" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:error="http://www.ups.com/XMLSchema/XOLTWS/Error/v1.1" xmlns:upss="http://www.ups.com/XMLSchema/XOLTWS/UPSS/v1.0" xmlns:tnt="http://www.ups.com/XMLSchema/XOLTWS/tnt/v1.0" xmlns:tns="http://www.ups.com/WSDL/XOLTWS/TNT/v1.0" targetNamespace="http://www.ups.com/WSDL/XOLTWS/TNT/v1.0">
<wsdl:types>
<xsd:schema>
<!-- This schema defines the UPS Security header used for authorization purposes -->
<xsd:import namespace="http://www.ups.com/XMLSchema/XOLTWS/UPSS/v1.0" schemaLocation="UPSSecurity.xsd"/>
<!-- This schema defines the error detail data types returned within SOAPFaults to provide more specific information pertaining to the problem. -->
<xsd:import namespace="http://www.ups.com/XMLSchema/XOLTWS/Error/v1.1" schemaLocation="Error1.1.xsd"/>
<!-- This schema defines the TimeInTransit service data types -->
<xsd:import namespace="http://www.ups.com/XMLSchema/XOLTWS/tnt/v1.0" schemaLocation="TimeInTransitWebServiceSchema.xsd"/>
</xsd:schema>
</wsdl:types>
<wsdl:message name="TimeInTransitRequestMessage">
<wsdl:part name="Body" element="tnt:TimeInTransitRequest"/>
<wsdl:part name="UPSSecurity" element="upss:UPSSecurity"/>
</wsdl:message>
<wsdl:message name="TimeInTransitResponseMessage">
<wsdl:part name="Body" element="tnt:TimeInTransitResponse"/>
</wsdl:message>
<wsdl:message name="TimeInTransitErrorMessage">
<wsdl:part name="TimeInTransitError" element="error:Errors"/>
</wsdl:message>
<wsdl:portType name="TimeInTransitPortType">
<wsdl:operation name="ProcessTimeInTransit">
<wsdl:input name="TimeInTransitRequest" message="tns:TimeInTransitRequestMessage"/>
<wsdl:output name="TimeInTransitResponse" message="tns:TimeInTransitResponseMessage"/>
<wsdl:fault name="TimeInTransitError" message="tns:TimeInTransitErrorMessage"/>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="TimeInTransitBinding" type="tns:TimeInTransitPortType">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="ProcessTimeInTransit">
<soap:operation soapAction="http://onlinetools.ups.com/webservices/TimeInTransitBinding/v1.0" style="document"/>
<wsdl:input name="TimeInTransitRequest">
<soap:body parts="Body" use="literal"/>
<soap:header message="tns:TimeInTransitRequestMessage" part="UPSSecurity" use="literal">
<soap:headerfault message="tns:TimeInTransitErrorMessage" part="TimeInTransitError" use="literal"/>
</soap:header>
</wsdl:input>
<wsdl:output name="TimeInTransitResponse">
<soap:body parts="Body" use="literal"/>
</wsdl:output>
<wsdl:fault name="TimeInTransitError">
<soap:fault name="TimeInTransitError" use="literal"/>
</wsdl:fault>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="TimeInTransitService">
<wsdl:port name="TimeInTransitPort" binding="tns:TimeInTransitBinding">
<!-- Production URL -->
<!-- <soap:address location="https://onlinetools.ups.com/webservices/TimeInTransit"/> -->
<!-- CIE (Customer Integration Environment) URL -->
<soap:address location="https://wwwcie.ups.com/webservices/TimeInTransit"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>

View File

@@ -0,0 +1,193 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xs:schema targetNamespace="http://www.ups.com/XMLSchema/XOLTWS/tnt/v1.0" xmlns:common="http://www.ups.com/XMLSchema/XOLTWS/Common/v1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tnt="http://www.ups.com/XMLSchema/XOLTWS/tnt/v1.0" elementFormDefault="qualified">
<xs:import namespace="http://www.ups.com/XMLSchema/XOLTWS/Common/v1.0" schemaLocation="common.xsd"/>
<!--++++++++++++++++++++Start of Common for Request & Response++++++++++++++++++++-->
<xs:complexType name="ResponseShipListAddressType">
<xs:sequence>
<xs:element name="Town" type="xs:string" minOccurs="0"/>
<xs:element name="City" type="xs:string"/>
<xs:element name="StateProvinceCode" type="xs:string" minOccurs="0"/>
<xs:element name="CountryCode" type="xs:string"/>
<xs:element name="PostcodePrimaryLow" type="xs:string" minOccurs="0"/>
<xs:element name="PostcodePrimaryHigh" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="RequestShipFromAddressType">
<xs:sequence>
<xs:element name="Town" type="xs:string" minOccurs="0"/>
<xs:element name="City" type="xs:string" minOccurs="0"/>
<xs:element name="StateProvinceCode" type="xs:string" minOccurs="0"/>
<xs:element name="CountryCode" type="xs:string"/>
<xs:element name="PostalCode" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="RequestShipToAddressType">
<xs:complexContent>
<xs:extension base="tnt:RequestShipFromAddressType">
<xs:sequence>
<xs:element name="ResidentialAddressIndicator" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="ResponseShipFromAddressType">
<xs:sequence>
<xs:element name="Town" type="xs:string" minOccurs="0"/>
<xs:element name="City" type="xs:string"/>
<xs:element name="StateProvinceCode" type="xs:string" minOccurs="0"/>
<xs:element name="CountryCode" type="xs:string"/>
<xs:element name="Country" type="xs:string"/>
<xs:element name="PostalCode" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="ResponseShipToAddressType">
<xs:complexContent>
<xs:extension base="tnt:ResponseShipFromAddressType">
<xs:sequence>
<xs:element name="ResidentialAddressIndicator" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="RequestShipFromType">
<xs:sequence>
<xs:element name="Address" type="tnt:RequestShipFromAddressType"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="RequestShipToType">
<xs:sequence>
<xs:element name="Address" type="tnt:RequestShipToAddressType"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="ResponseShipFromType">
<xs:sequence>
<xs:element name="Address" type="tnt:ResponseShipFromAddressType"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="ResponseShipToType">
<xs:sequence>
<xs:element name="Address" type="tnt:ResponseShipToAddressType"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="ShipmentWeightType">
<xs:sequence>
<xs:element name="UnitOfMeasurement" type="tnt:CodeDescriptionType"/>
<xs:element name="Weight" type="xs:string"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="CodeDescriptionType">
<xs:sequence>
<xs:element name="Code" type="xs:string"/>
<xs:element name="Description" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="PickupType">
<xs:sequence>
<xs:element name="Date" type="xs:string"/>
<xs:element name="Time" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="InvoiceLineTotalType">
<xs:sequence>
<xs:element name="CurrencyCode" type="xs:string"/>
<xs:element name="MonetaryValue" type="xs:string"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="ReturnContractServicesType">
<xs:sequence>
<xs:element name="Code" type="xs:string"/>
<xs:element name="Description" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<!--++++++++++++++++++++End of Common for Request & Response +++++++++++++++++++++-->
<!--++++++++++++++++++Start Request++++++++++++++++-->
<xs:element name="TimeInTransitRequest">
<xs:complexType>
<xs:sequence>
<xs:element ref="common:Request"/>
<xs:element name="ShipFrom" type="tnt:RequestShipFromType"/>
<xs:element name="ShipTo" type="tnt:RequestShipToType"/>
<xs:element name="Pickup" type="tnt:PickupType"/>
<xs:element name="ShipmentWeight" type="tnt:ShipmentWeightType" minOccurs="0"/>
<xs:element name="TotalPackagesInShipment" type="xs:string" minOccurs="0"/>
<xs:element name="InvoiceLineTotal" type="tnt:InvoiceLineTotalType" minOccurs="0"/>
<xs:element name="DocumentsOnlyIndicator" type="xs:string" minOccurs="0"/>
<xs:element name="BillType" type="xs:string" minOccurs="0"/>
<xs:element name="MaximumListSize" type="xs:string" minOccurs="0"/>
<xs:element name="SaturdayDeliveryInfoRequestIndicator" type="xs:string" minOccurs="0"/>
<xs:element name="DropOffAtFacilityIndicator" type="xs:string" minOccurs="0"/>
<xs:element name="HoldForPickupIndicator" type="xs:string" minOccurs="0"/>
<xs:element name="IncludeAllServicesIndicator" type="xs:string" minOccurs="0" tnt:usage="ups"/>
<xs:element name="ReturnContractServices" type="tnt:ReturnContractServicesType" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<!--++++++++++++++++++++End of Request +++++++++++++++++++++-->
<!--++++++++++++++++++++Start Response+++++++++++++++++++++-->
<xs:complexType name="EstimatedArrivalType">
<xs:sequence>
<xs:element name="Arrival" type="tnt:PickupType"/>
<xs:element name="BusinessDaysInTransit" type="xs:string"/>
<xs:element name="Pickup" type="tnt:PickupType"/>
<xs:element name="DayOfWeek" type="xs:string" minOccurs="0"/>
<xs:element name="CustomerCenterCutoff" type="xs:string" minOccurs="0"/>
<xs:element name="DelayCount" type="xs:string" minOccurs="0"/>
<xs:element name="HolidayCount" type="xs:string" minOccurs="0"/>
<xs:element name="RestDays" type="xs:string" minOccurs="0"/>
<xs:element name="TotalTransitDays" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="ServiceSummaryType">
<xs:sequence>
<xs:element name="Service" type="tnt:CodeDescriptionType"/>
<xs:element name="GuaranteedIndicator" type="xs:string" minOccurs="0"/>
<xs:element name="Disclaimer" type="xs:string" minOccurs="0"/>
<xs:element name="EstimatedArrival" type="tnt:EstimatedArrivalType"/>
<xs:element name="SaturdayDelivery" type="xs:string" minOccurs="0"/>
<xs:element name="SaturdayDeliveryDisclaimer" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="TransitResponseType">
<xs:sequence>
<xs:element name="ShipFrom" type="tnt:ResponseShipFromType"/>
<xs:element name="ShipTo" type="tnt:ResponseShipToType"/>
<xs:element name="PickupDate" type="xs:string"/>
<xs:element name="ShipmentWeight" type="tnt:ShipmentWeightType" minOccurs="0"/>
<xs:element name="InvoiceLineTotal" type="tnt:InvoiceLineTotalType" minOccurs="0"/>
<xs:element name="DocumentsOnlyIndicator" type="xs:string" minOccurs="0"/>
<xs:element name="BillType" type="xs:string" minOccurs="0"/>
<xs:element name="MaximumListSize" type="xs:string" minOccurs="0"/>
<xs:element name="ServiceSummary" type="tnt:ServiceSummaryType" maxOccurs="unbounded"/>
<xs:element name="AutoDutyCode" type="xs:string" minOccurs="0"/>
<xs:element name="Disclaimer" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="CandidateType">
<xs:sequence>
<xs:element name="Address" type="tnt:ResponseShipListAddressType"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="ShipListType">
<xs:sequence>
<xs:element name="Candidate" type="tnt:CandidateType" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="CandidateResponseType">
<xs:sequence>
<xs:element name="ShipFromList" type="tnt:ShipListType" minOccurs="0"/>
<xs:element name="ShipToList" type="tnt:ShipListType" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:element name="TimeInTransitResponse">
<xs:complexType>
<xs:sequence>
<xs:element ref="common:Response"/>
<xs:choice>
<xs:element name="TransitResponse" type="tnt:TransitResponseType" minOccurs="0"/>
<xs:element name="CandidateResponse" type="tnt:CandidateResponseType" minOccurs="0"/>
</xs:choice>
</xs:sequence>
</xs:complexType>
</xs:element>
<!--+++++++++++++++++++End of Response++++++++++++++++++-->
</xs:schema>

View File

@@ -0,0 +1,23 @@
<xsd:schema targetNamespace="http://www.ups.com/XMLSchema/XOLTWS/UPSS/v1.0" xmlns:upss="http://www.ups.com/XMLSchema/XOLTWS/UPSS/v1.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
<xsd:element name="UPSSecurity">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="UsernameToken">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="Username" type="xsd:string"/>
<xsd:element name="Password" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="ServiceAccessToken">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="AccessLicenseNumber" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>

View File

@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- UPS Void Shipment Service WSDL Release Date Mar 11, 2008 -->
<!-- Copyright 2007-2008 United Parcel Service of America, Inc. All rights reserved. -->
<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:error="http://www.ups.com/XMLSchema/XOLTWS/Error/v1.1" xmlns:upss="http://www.ups.com/XMLSchema/XOLTWS/UPSS/v1.0" xmlns:common="http://www.ups.com/XMLSchema/XOLTWS/Common/v1.0" xmlns:void="http://www.ups.com/XMLSchema/XOLTWS/Void/v1.1" xmlns:tns="http://www.ups.com/WSDL/XOLTWS/Void/v1.1" targetNamespace="http://www.ups.com/WSDL/XOLTWS/Void/v1.1">
<wsdl:types>
<xsd:schema>
<!-- This schema defines the UPS Security header used for authorization purposes -->
<xsd:import namespace="http://www.ups.com/XMLSchema/XOLTWS/UPSS/v1.0" schemaLocation="UPSSecurity.xsd"/>
<!-- This schema defines the error detail data types returned within SOAPFaults to provide more specific information pertaining to the problem. -->
<xsd:import namespace="http://www.ups.com/XMLSchema/XOLTWS/Error/v1.1" schemaLocation="Error1.1.xsd"/>
<!-- This schema defines the Void Shipment service data types -->
<xsd:import namespace="http://www.ups.com/XMLSchema/XOLTWS/Void/v1.1" schemaLocation="VoidWebServiceSchema.xsd"/>
</xsd:schema>
</wsdl:types>
<wsdl:message name="VoidRequestMessage">
<wsdl:part name="Body" element="void:VoidShipmentRequest"/>
<wsdl:part name="UPSSecurity" element="upss:UPSSecurity"/>
</wsdl:message>
<wsdl:message name="VoidResponseMessage">
<wsdl:part name="Body" element="void:VoidShipmentResponse"/>
</wsdl:message>
<wsdl:message name="VoidErrorMessage">
<wsdl:part name="VoidError" element="error:Errors"/>
</wsdl:message>
<wsdl:portType name="VoidPortType">
<wsdl:operation name="ProcessVoid">
<wsdl:input name="VoidShipmentRequest" message="tns:VoidRequestMessage"/>
<wsdl:output name="VoidShipmentResponse" message="tns:VoidResponseMessage"/>
<wsdl:fault name="VoidError" message="tns:VoidErrorMessage"/>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="VoidBinding" type="tns:VoidPortType">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="ProcessVoid">
<soap:operation soapAction="http://onlinetools.ups.com/webservices/VoidBinding/v1.1" style="document"/>
<wsdl:input name="VoidShipmentRequest">
<soap:body parts="Body" use="literal"/>
<soap:header message="tns:VoidRequestMessage" part="UPSSecurity" use="literal">
<soap:headerfault message="tns:VoidErrorMessage" part="VoidError" use="literal"/>
</soap:header>
</wsdl:input>
<wsdl:output name="VoidShipmentResponse">
<soap:body parts="Body" use="literal"/>
</wsdl:output>
<wsdl:fault name="VoidError">
<soap:fault name="VoidError" use="literal"/>
</wsdl:fault>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="VoidService">
<wsdl:port name="VoidPort" binding="tns:VoidBinding">
<!-- Production URL -->
<!-- <soap:address location="https://onlinetools.ups.com/webservices/Void"/> -->
<!-- CIE (Customer Integration Environment) URL -->
<soap:address location="https://wwwcie.ups.com/webservices/Void"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>

View File

@@ -0,0 +1,45 @@
<xsd:schema targetNamespace="http://www.ups.com/XMLSchema/XOLTWS/Void/v1.1" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:void="http://www.ups.com/XMLSchema/XOLTWS/Void/v1.1" xmlns:common="http://www.ups.com/XMLSchema/XOLTWS/Common/v1.0" elementFormDefault="qualified" version="201601">
<xsd:import namespace="http://www.ups.com/XMLSchema/XOLTWS/Common/v1.0" schemaLocation="common.xsd"/>
<xsd:element name="VoidShipmentRequest">
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="common:Request"/>
<xsd:element name="VoidShipment">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="ShipmentIdentificationNumber" type="xsd:string"/>
<xsd:element name="TrackingNumber" type="xsd:string" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="VoidShipmentResponse">
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="common:Response"/>
<xsd:element name="SummaryResult">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="Status" type="void:CodeDescriptionType"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="PackageLevelResult" type="void:PackageLevelResult" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:complexType name="PackageLevelResult">
<xsd:sequence>
<xsd:element name="TrackingNumber" type="xsd:string"/>
<xsd:element name="Status" type="void:CodeDescriptionType"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="CodeDescriptionType">
<xsd:sequence>
<xsd:element name="Code" type="xsd:string"/>
<xsd:element name="Description" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
</xsd:schema>

View File

@@ -0,0 +1,65 @@
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema targetNamespace="http://www.ups.com/XMLSchema/XOLTWS/Common/v1.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:ups="http://www.ups.com/XMLSchema" xmlns:common="http://www.ups.com/XMLSchema/XOLTWS/Common/v1.0" elementFormDefault="qualified" version="201707">
<xsd:element name="Request" type="common:RequestType"/>
<xsd:element name="Response" type="common:ResponseType"/>
<xsd:element name="ClientInformation" type="common:ClientInformationType"/>
<xsd:complexType name="ClientInformationType">
<xsd:sequence>
<xsd:element name="Property" minOccurs="0" maxOccurs="unbounded">
<xsd:complexType>
<xsd:simpleContent>
<xsd:extension base="xsd:string">
<xsd:attribute name="Key" type="xsd:string" use="required"/>
</xsd:extension>
</xsd:simpleContent>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="RequestType">
<xsd:sequence>
<xsd:element name="RequestOption" type="xsd:string" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="SubVersion" type="xsd:string" minOccurs="0"/>
<xsd:element name="TransactionReference" type="common:TransactionReferenceType" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="TransactionReferenceType">
<xsd:sequence>
<xsd:element name="CustomerContext" type="xsd:string" minOccurs="0"/>
<xsd:element name="TransactionIdentifier" type="xsd:string" minOccurs="0" ups:usage="notused"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ResponseType">
<xsd:sequence>
<xsd:element name="ResponseStatus" type="common:CodeDescriptionType"/>
<xsd:element name="Alert" type="common:CodeDescriptionType" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="AlertDetail" type="common:DetailType" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="TransactionReference" type="common:TransactionReferenceType" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="CodeDescriptionType">
<xsd:sequence>
<xsd:element name="Code" type="xsd:string"/>
<xsd:element name="Description" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="DetailType">
<xsd:sequence>
<xsd:element name="Code" type="xsd:string"/>
<xsd:element name="Description" type="xsd:string"/>
<xsd:element name="ElementLevelInformation" type="common:ElementLevelInformationType" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ElementLevelInformationType">
<xsd:sequence>
<xsd:element name="Level" type="xsd:string"/>
<xsd:element name="ElementIdentifier" type="common:ElementIdentifierType" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ElementIdentifierType">
<xsd:sequence>
<xsd:element name="Code" type="xsd:string"/>
<xsd:element name="Value" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
</xsd:schema>

View File

@@ -1,2 +1,5 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
from . import delivery_ups from . import delivery_ups
from . import stock
from . import ups_request_patch from . import ups_request_patch

View File

@@ -1,12 +1,26 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
from odoo import api, fields, models, _ from odoo import api, fields, models, _
from odoo.exceptions import UserError, ValidationError from odoo.exceptions import UserError, ValidationError
from odoo.addons.delivery_ups.models.ups_request import UPSRequest, Package from odoo.addons.delivery_ups.models.ups_request import UPSRequest, Package
from odoo.tools import pdf from odoo.tools import pdf
import logging
_logger = logging.getLogger(__name__)
class ProviderUPS(models.Model): class ProviderUPS(models.Model):
_inherit = 'delivery.carrier' _inherit = 'delivery.carrier'
def _get_ups_signature_required_type(self, order=None, picking=None, package=None):
# '3' for Adult Sig.
return '2'
def _get_ups_signature_required(self, order=None, picking=None, package=None):
if self.get_signature_required(order=order, picking=picking, package=package):
return self._get_ups_signature_required_type(order=order, picking=picking, package=package)
return False
def _get_ups_is_third_party(self, order=None, picking=None): def _get_ups_is_third_party(self, order=None, picking=None):
third_party_account = self.get_third_party_account(order=order, picking=picking) third_party_account = self.get_third_party_account(order=order, picking=picking)
if third_party_account: if third_party_account:
@@ -17,6 +31,16 @@ class ProviderUPS(models.Model):
return True return True
return False return False
def _get_main_ups_account_number(self, order=None, picking=None):
wh = None
if order:
wh = order.warehouse_id
if picking:
wh = picking.picking_type_id.warehouse_id
if wh and wh.ups_shipper_number:
return wh.ups_shipper_number
return self.ups_shipper_number
def _get_ups_account_number(self, order=None, picking=None): def _get_ups_account_number(self, order=None, picking=None):
""" """
Common hook to customize what UPS Account number to use. Common hook to customize what UPS Account number to use.
@@ -30,6 +54,8 @@ class ProviderUPS(models.Model):
return third_party_account.name return third_party_account.name
if order and order.ups_carrier_account: if order and order.ups_carrier_account:
return order.ups_carrier_account return order.ups_carrier_account
if picking and picking.picking_type_id.warehouse_id.ups_shipper_number:
return picking.picking_type_id.warehouse_id.ups_shipper_number
if picking and picking.sale_id.ups_carrier_account: if picking and picking.sale_id.ups_carrier_account:
return picking.sale_id.ups_carrier_account return picking.sale_id.ups_carrier_account
return self.ups_shipper_number return self.ups_shipper_number
@@ -37,14 +63,20 @@ class ProviderUPS(models.Model):
def _get_ups_carrier_account(self, picking): def _get_ups_carrier_account(self, picking):
# 3rd party billing should return False if not used. # 3rd party billing should return False if not used.
account = self._get_ups_account_number(picking=picking) account = self._get_ups_account_number(picking=picking)
return account if account != self.ups_shipper_number else False return account if account not in (self.ups_shipper_number, picking.picking_type_id.warehouse_id.ups_shipper_number) else False
""" """
Overrides to use Hibou Delivery methods to get shipper etc. and to add 'transit_days' to result. Overrides to use Hibou Delivery methods to get shipper etc. and to add 'transit_days' to result.
""" """
def ups_rate_shipment(self, order): def ups_rate_shipment(self, order):
superself = self.sudo() superself = self.sudo()
srm = UPSRequest(self.log_xml, superself.ups_username, superself.ups_passwd, superself.ups_shipper_number, superself.ups_access_number, self.prod_environment) # Hibou Shipping
signature_required = superself._get_ups_signature_required(order=order)
insurance_value = superself.get_insurance_value(order=order)
currency = order.currency_id or order.company_id.currency_id
insurance_currency_code = currency.name
srm = UPSRequest(self.log_xml, superself.ups_username, superself.ups_passwd, superself._get_main_ups_account_number(order=order), superself.ups_access_number, self.prod_environment)
ResCurrency = self.env['res.currency'] ResCurrency = self.env['res.currency']
max_weight = self.ups_default_packaging_id.max_weight max_weight = self.ups_default_packaging_id.max_weight
packages = [] packages = []
@@ -59,11 +91,14 @@ class ProviderUPS(models.Model):
last_package_weight = total_weight % max_weight last_package_weight = total_weight % max_weight
for seq in range(total_package): for seq in range(total_package):
packages.append(Package(self, max_weight)) packages.append(Package(self, max_weight,
insurance_value=insurance_value, insurance_currency_code=insurance_currency_code, signature_required=signature_required))
if last_package_weight: if last_package_weight:
packages.append(Package(self, last_package_weight)) packages.append(Package(self, last_package_weight,
insurance_value=insurance_value, insurance_currency_code=insurance_currency_code, signature_required=signature_required))
else: else:
packages.append(Package(self, total_weight)) packages.append(Package(self, total_weight,
insurance_value=insurance_value, insurance_currency_code=insurance_currency_code, signature_required=signature_required))
shipment_info = { shipment_info = {
'total_qty': total_qty # required when service type = 'UPS Worldwide Express Freight' 'total_qty': total_qty # required when service type = 'UPS Worldwide Express Freight'
@@ -126,24 +161,38 @@ class ProviderUPS(models.Model):
def ups_send_shipping(self, pickings): def ups_send_shipping(self, pickings):
res = [] res = []
superself = self.sudo() superself = self.sudo()
srm = UPSRequest(self.log_xml, superself.ups_username, superself.ups_passwd, superself.ups_shipper_number, superself.ups_access_number, self.prod_environment)
ResCurrency = self.env['res.currency'] ResCurrency = self.env['res.currency']
for picking in pickings: for picking in pickings:
srm = UPSRequest(self.log_xml, superself.ups_username, superself.ups_passwd, superself._get_main_ups_account_number(picking=picking), superself.ups_access_number, self.prod_environment)
# Hibou Delivery # Hibou Delivery
shipper_company = superself.get_shipper_company(picking=picking) shipper_company = superself.get_shipper_company(picking=picking)
shipper_warehouse = superself.get_shipper_warehouse(picking=picking) shipper_warehouse = superself.get_shipper_warehouse(picking=picking)
recipient = superself.get_recipient(picking=picking) recipient = superself.get_recipient(picking=picking)
currency = picking.sale_id.currency_id if picking.sale_id else picking.company_id.currency_id
insurance_currency_code = currency.name
picking_packages = picking.package_ids
package_carriers = picking_packages.mapped('carrier_id')
if package_carriers:
# only ship ours
picking_packages = picking_packages.filtered(lambda p: p.carrier_id == self and not p.carrier_tracking_ref)
if not picking_packages:
continue
packages = [] packages = []
package_names = [] package_names = []
if picking.package_ids: if picking_packages:
# Create all packages # Create all packages
for package in picking.package_ids: for package in picking_packages:
packages.append(Package(self, package.shipping_weight, quant_pack=package.packaging_id, name=package.name)) packages.append(Package(self, package.shipping_weight, quant_pack=package.packaging_id, name=package.name,
insurance_value=superself.get_insurance_value(picking=picking, package=package), insurance_currency_code=insurance_currency_code, signature_required=superself._get_ups_signature_required(picking=picking, package=package)))
package_names.append(package.name) package_names.append(package.name)
# what is the point of weight_bulk?
# Create one package with the rest (the content that is not in a package) # Create one package with the rest (the content that is not in a package)
if picking.weight_bulk: # if picking.weight_bulk:
packages.append(Package(self, picking.weight_bulk)) # packages.append(Package(self, picking.weight_bulk))
invoice_line_total = 0 invoice_line_total = 0
for move in picking.move_lines: for move in picking.move_lines:
@@ -177,7 +226,7 @@ class ProviderUPS(models.Model):
if check_value: if check_value:
raise UserError(check_value) raise UserError(check_value)
package_type = picking.package_ids and picking.package_ids[0].packaging_id.shipper_package_code or self.ups_default_packaging_id.shipper_package_code package_type = picking_packages and picking_packages[0].packaging_id.shipper_package_code or self.ups_default_packaging_id.shipper_package_code
result = srm.send_shipping( result = srm.send_shipping(
shipment_info=shipment_info, packages=packages, shipper=shipper_company, ship_from=shipper_warehouse, shipment_info=shipment_info, packages=packages, shipper=shipper_company, ship_from=shipper_warehouse,
ship_to=recipient, packaging_type=package_type, service_type=ups_service_type, label_file_type=self.ups_label_file_type, ups_carrier_account=ups_carrier_account, ship_to=recipient, packaging_type=package_type, service_type=ups_service_type, label_file_type=self.ups_label_file_type, ups_carrier_account=ups_carrier_account,
@@ -216,3 +265,149 @@ class ProviderUPS(models.Model):
'tracking_number': carrier_tracking_ref} 'tracking_number': carrier_tracking_ref}
res = res + [shipping_data] res = res + [shipping_data]
return res return res
def ups_rate_shipment_multi(self, order=None, picking=None, packages=None):
if not packages:
return self._ups_rate_shipment_multi_package(order=order, picking=picking)
else:
rates = []
for package in packages:
rates += self._ups_rate_shipment_multi_package(order=order, picking=picking, package=package)
return rates
def _ups_rate_shipment_multi_package(self, order=None, picking=None, package=None):
superself = self.sudo()
srm = UPSRequest(self.log_xml, superself.ups_username, superself.ups_passwd, superself._get_main_ups_account_number(order=order, picking=picking), superself.ups_access_number, self.prod_environment)
ResCurrency = self.env['res.currency']
max_weight = self.ups_default_packaging_id.max_weight
packages = []
if order:
insurance_value = superself.get_insurance_value(order=order)
signature_required = superself._get_ups_signature_required(order=order)
currency = order.currency_id
insurance_currency_code = currency.name
company = order.company_id
date_order = order.date_order or fields.Date.today()
total_qty = 0
total_weight = 0
for line in order.order_line.filtered(lambda line: not line.is_delivery):
total_qty += line.product_uom_qty
total_weight += line.product_id.weight * line.product_qty
if max_weight and total_weight > max_weight:
total_package = int(total_weight / max_weight)
last_package_weight = total_weight % max_weight
for seq in range(total_package):
packages.append(Package(self, max_weight, insurance_value=insurance_value, insurance_currency_code=insurance_currency_code, signature_required=signature_required))
if last_package_weight:
packages.append(Package(self, last_package_weight, insurance_value=insurance_value, insurance_currency_code=insurance_currency_code, signature_required=signature_required))
else:
packages.append(Package(self, total_weight, insurance_value=insurance_value, insurance_currency_code=insurance_currency_code, signature_required=signature_required))
else:
currency = picking.sale_id.currency_id if picking.sale_id else picking.company_id.currency_id
insurance_currency_code = currency.name
company = picking.company_id
date_order = picking.sale_id.date_order or fields.Date.today() if picking.sale_id else fields.Date.today()
# Is total quantity the number of packages or the number of items being shipped?
if package:
total_qty = 1
packages = [Package(self, package.shipping_weight, insurance_value=superself.get_insurance_value(picking=picking, package=package), insurance_currency_code=insurance_currency_code, signature_required=superself._get_ups_signature_required(picking=picking, package=package))]
elif picking.package_ids:
# all packages....
total_qty = len(picking.package_ids)
packages = [Package(self, package.shipping_weight, insurance_value=superself.get_insurance_value(picking=picking, package=package), insurance_currency_code=insurance_currency_code, signature_required=superself._get_ups_signature_required(picking=picking, package=package)) for package in picking.package_ids.filtered(lambda p: not p.carrier_id)]
else:
total_qty = 1
packages.append(Package(self, picking.shipping_weight or picking.weight, insurance_value=superself.get_insurance_value(picking=picking), insurance_currency_code=insurance_currency_code, signature_required=superself._get_ups_signature_required(picking=picking)))
shipment_info = {
'total_qty': total_qty # required when service type = 'UPS Worldwide Express Freight'
}
if self.ups_cod:
cod_info = {
'currency': currency.name,
'monetary_value': order.amount_total if order else picking.sale_id.amount_total,
'funds_code': self.ups_cod_funds_code,
}
else:
cod_info = None
# Hibou Delivery
shipper_company = self.get_shipper_company(order=order, picking=picking)
shipper_warehouse = self.get_shipper_warehouse(order=order, picking=picking)
recipient = self.get_recipient(order=order, picking=picking)
date_planned = fields.Datetime.now()
if self.env.context.get('date_planned'):
date_planned = self.env.context.get('date_planned')
check_value = srm.check_required_value(shipper_company, shipper_warehouse, recipient, order=order, picking=picking)
if check_value:
return [{'success': False,
'price': 0.0,
'error_message': check_value,
'warning_message': False,
}]
# We now use Shop if we send multi=True
ups_service_type = (order.ups_service_type or self.ups_default_service_type) if order else self.ups_default_service_type
result = srm.get_shipping_price(
shipment_info=shipment_info, packages=packages, shipper=shipper_company, ship_from=shipper_warehouse,
ship_to=recipient, packaging_type=self.ups_default_packaging_id.shipper_package_code, service_type=ups_service_type,
saturday_delivery=self.ups_saturday_delivery, cod_info=cod_info, date_planned=date_planned, multi=True)
# Hibou Delivery
is_third_party = self._get_ups_is_third_party(order=order, picking=picking)
response = []
for rate in result:
if isinstance(rate, str):
# assume error
response.append({
'success': False, 'price': 0.0,
'error_message': _('Error:\n%s') % rate,
'warning_message': False,
})
elif rate.get('error_message'):
_logger.error('UPS error: %s' % rate['error_message'])
response.append({
'success': False, 'price': 0.0,
'error_message': _('Error:\n%s') % rate['error_message'],
'warning_message': False,
})
else:
if currency.name == rate['currency_code']:
price = float(rate['price'])
else:
quote_currency = ResCurrency.search([('name', '=', rate['currency_code'])], limit=1)
price = quote_currency._convert(
float(rate['price']), currency, company, date_order)
if is_third_party:
# Don't show delivery amount, if ups bill my account option is true
price = 0.0
service_code = rate['service_code']
carrier = self.ups_find_delivery_carrier_for_service(service_code)
if carrier:
response.append({
'carrier': carrier,
'package': package or self.env['stock.quant.package'].browse(),
'success': True,
'price': price,
'error_message': False,
'warning_message': False,
'date_planned': date_planned,
'date_delivered': None,
'transit_days': rate.get('transit_days', 0),
'service_code': service_code,
})
return response
def ups_find_delivery_carrier_for_service(self, service_code):
if self.ups_default_service_type == service_code:
return self
# arbitrary decision, lets find the same account number
carrier = self.search([('ups_shipper_number', '=', self.ups_shipper_number),
('ups_default_service_type', '=', service_code)
], limit=1)
return carrier

View File

@@ -0,0 +1,9 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
from odoo import fields, models
class StockWarehouse(models.Model):
_inherit = 'stock.warehouse'
ups_shipper_number = fields.Char(string='UPS Shipper Number')

View File

@@ -1,15 +1,52 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
from os import path as os_path
import suds import suds
from odoo.addons.delivery_ups.models.ups_request import UPSRequest from odoo.addons.delivery_ups.models.ups_request import UPSRequest, Package
SUDS_VERSION = suds.__version__ SUDS_VERSION = suds.__version__
import logging
_logger = logging.getLogger(__name__)
# If you're getting SOAP/suds errors
# logging.getLogger('suds.client').setLevel(logging.DEBUG)
TNT_CODE_MAP = {
'GND': '03',
# TODO fill in the rest if needed....
}
def patched__init__(self, debug_logger, username, password, shipper_number, access_number, prod_environment):
self.debug_logger = debug_logger
# Product and Testing url
self.endurl = "https://onlinetools.ups.com/webservices/"
if not prod_environment:
self.endurl = "https://wwwcie.ups.com/webservices/"
# Basic detail require to authenticate
self.username = username
self.password = password
self.shipper_number = shipper_number
self.access_number = access_number
self.rate_wsdl = '../api/RateWS.wsdl'
self.ship_wsdl = '../api/Ship.wsdl'
self.void_wsdl = '../api/Void.wsdl'
dirname = os_path.dirname(__file__)
self.tnt_wsdl = os_path.join(dirname, '../api/TNTWS.wsdl')
def patched_get_shipping_price(self, shipment_info, packages, shipper, ship_from, ship_to, packaging_type, service_type, def patched_get_shipping_price(self, shipment_info, packages, shipper, ship_from, ship_to, packaging_type, service_type,
saturday_delivery, cod_info, date_planned=False): saturday_delivery, cod_info, date_planned=False, multi=False):
client = self._set_client(self.rate_wsdl, 'Rate', 'RateRequest') client = self._set_client(self.rate_wsdl, 'Rate', 'RateRequest')
request = client.factory.create('ns0:RequestType') request = client.factory.create('ns0:RequestType')
request.RequestOption = 'Rate' request.RequestOption = 'Rate'
if multi:
request.RequestOption = 'Shop'
classification = client.factory.create('ns2:CodeDescriptionType') classification = client.factory.create('ns2:CodeDescriptionType')
classification.Code = '00' # Get rates for the shipper account classification.Code = '00' # Get rates for the shipper account
@@ -70,7 +107,7 @@ def patched_get_shipping_price(self, shipment_info, packages, shipper, ship_from
else: else:
shipment.ShipmentServiceOptions = '' shipment.ShipmentServiceOptions = ''
shipment.ShipmentRatingOptions.NegotiatedRatesIndicator = 1 shipment.ShipmentRatingOptions.NegotiatedRatesIndicator = '1'
try: try:
# Get rate using for provided detail # Get rate using for provided detail
@@ -81,6 +118,62 @@ def patched_get_shipping_price(self, shipment_info, packages, shipper, ship_from
return self.get_error_message(response.Response.ResponseStatus.Code, return self.get_error_message(response.Response.ResponseStatus.Code,
response.Response.ResponseStatus.Description) response.Response.ResponseStatus.Description)
if multi:
result = []
tnt_response = None
tnt_ready = False
for rated_shipment in response.RatedShipment:
res = {}
res['service_code'] = rated_shipment.Service.Code
res['currency_code'] = rated_shipment.TotalCharges.CurrencyCode
negotiated_rate = 'NegotiatedRateCharges' in rated_shipment and rated_shipment.NegotiatedRateCharges.TotalCharge.MonetaryValue or None
res['price'] = negotiated_rate or rated_shipment.TotalCharges.MonetaryValue
# Hibou Delivery
if hasattr(rated_shipment, 'GuaranteedDelivery') and hasattr(rated_shipment.GuaranteedDelivery, 'BusinessDaysInTransit'):
res['transit_days'] = int(rated_shipment.GuaranteedDelivery.BusinessDaysInTransit)
if not res.get('transit_days') and date_planned:
if not tnt_response:
try:
tnt_client = self._set_client(self.tnt_wsdl, 'TimeInTransit', 'TimeInTransitRequest')
tnt_request = tnt_client.factory.create('tnt:TimeInTransitRequest')
tnt_request.Request.RequestOption = 'TNT'
tnt_request.ShipFrom.Address.City = ship_from.city or ''
tnt_request.ShipFrom.Address.CountryCode = ship_from.country_id.code or ''
tnt_request.ShipFrom.Address.PostalCode = ship_from.zip or ''
if ship_from.country_id.code in ('US', 'CA', 'IE'):
tnt_request.ShipFrom.Address.StateProvinceCode = ship_from.state_id.code or ''
tnt_request.ShipTo.Address.City = ship_to.city or ''
tnt_request.ShipTo.Address.CountryCode = ship_to.country_id.code or ''
tnt_request.ShipTo.Address.PostalCode = ship_to.zip or ''
if ship_to.country_id.code in ('US', 'CA', 'IE'):
tnt_request.ShipTo.Address.StateProvinceCode = ship_to.state_id.code or ''
tnt_request.Pickup.Date = date_planned.split(' ')[0].replace('-', '')
tnt_request.Pickup.Time = date_planned.split(' ')[1].replace(':', '')
# tnt_request_transit_from = tnt_client.factory.create('ns1:TransitFrom')
tnt_response = tnt_client.service.ProcessTimeInTransit(Request=tnt_request.Request,
ShipFrom=tnt_request.ShipFrom,
ShipTo=tnt_request.ShipTo,
Pickup=tnt_request.Pickup)
tnt_ready = tnt_response.Response.ResponseStatus.Code == "1"
except Exception as e:
_logger.warning('exception during the UPS Time In Transit request. ' + str(e))
tnt_ready = False
tnt_response = '-1'
if tnt_ready:
for service in tnt_response.TransitResponse.ServiceSummary:
if TNT_CODE_MAP.get(service.Service.Code) == service_type:
if hasattr(service, 'EstimatedArrival') and hasattr(service.EstimatedArrival, 'BusinessDaysInTransit'):
res['transit_days'] = int(service.EstimatedArrival.BusinessDaysInTransit)
break
# use TNT API to
result.append(res)
else:
result = {} result = {}
result['currency_code'] = response.RatedShipment[0].TotalCharges.CurrencyCode result['currency_code'] = response.RatedShipment[0].TotalCharges.CurrencyCode
@@ -89,10 +182,14 @@ def patched_get_shipping_price(self, shipment_info, packages, shipper, ship_from
0].NegotiatedRateCharges.TotalCharge.MonetaryValue or None 0].NegotiatedRateCharges.TotalCharge.MonetaryValue or None
result['price'] = negotiated_rate or response.RatedShipment[0].TotalCharges.MonetaryValue result['price'] = negotiated_rate or response.RatedShipment[0].TotalCharges.MonetaryValue
# Hibou Delivery # Hibou Delivery
if hasattr(response.RatedShipment[0], 'GuaranteedDelivery') and hasattr(response.RatedShipment[0].GuaranteedDelivery, 'BusinessDaysInTransit'): if hasattr(response.RatedShipment[0], 'GuaranteedDelivery') and hasattr(response.RatedShipment[0].GuaranteedDelivery, 'BusinessDaysInTransit'):
result['transit_days'] = int(response.RatedShipment[0].GuaranteedDelivery.BusinessDaysInTransit) result['transit_days'] = int(response.RatedShipment[0].GuaranteedDelivery.BusinessDaysInTransit)
if not result.get('transit_days') and date_planned:
# use TNT API to
_logger.warning(' We would now use the TNT service. But who would show the transit days? 2')
# End # End
return result return result
@@ -109,4 +206,189 @@ def patched_get_shipping_price(self, shipment_info, packages, shipper, ship_from
return self.get_error_message('0', 'UPS Server Not Found:\n%s' % e) return self.get_error_message('0', 'UPS Server Not Found:\n%s' % e)
def patched_send_shipping(self, shipment_info, packages, shipper, ship_from, ship_to, packaging_type, service_type, saturday_delivery, cod_info=None, label_file_type='GIF', ups_carrier_account=False):
client = self._set_client(self.ship_wsdl, 'Ship', 'ShipmentRequest')
request = client.factory.create('ns0:RequestType')
request.RequestOption = 'nonvalidate'
namespace = 'ns3'
label = client.factory.create('{}:LabelSpecificationType'.format(namespace))
label.LabelImageFormat.Code = label_file_type
label.LabelImageFormat.Description = label_file_type
if label_file_type != 'GIF':
label.LabelStockSize.Height = '6'
label.LabelStockSize.Width = '4'
shipment = client.factory.create('{}:ShipmentType'.format(namespace))
shipment.Description = shipment_info.get('description')
for package in self.set_package_detail(client, packages, packaging_type, namespace, ship_from, ship_to, cod_info):
shipment.Package.append(package)
shipment.Shipper.AttentionName = shipper.name or ''
shipment.Shipper.Name = shipper.parent_id.name or shipper.name or ''
shipment.Shipper.Address.AddressLine = [l for l in [shipper.street or '', shipper.street2 or ''] if l]
shipment.Shipper.Address.City = shipper.city or ''
shipment.Shipper.Address.PostalCode = shipper.zip or ''
shipment.Shipper.Address.CountryCode = shipper.country_id.code or ''
if shipper.country_id.code in ('US', 'CA', 'IE'):
shipment.Shipper.Address.StateProvinceCode = shipper.state_id.code or ''
shipment.Shipper.ShipperNumber = self.shipper_number or ''
shipment.Shipper.Phone.Number = self._clean_phone_number(shipper.phone)
shipment.ShipFrom.AttentionName = ship_from.name or ''
shipment.ShipFrom.Name = ship_from.parent_id.name or ship_from.name or ''
shipment.ShipFrom.Address.AddressLine = [l for l in [ship_from.street or '', ship_from.street2 or ''] if l]
shipment.ShipFrom.Address.City = ship_from.city or ''
shipment.ShipFrom.Address.PostalCode = ship_from.zip or ''
shipment.ShipFrom.Address.CountryCode = ship_from.country_id.code or ''
if ship_from.country_id.code in ('US', 'CA', 'IE'):
shipment.ShipFrom.Address.StateProvinceCode = ship_from.state_id.code or ''
shipment.ShipFrom.Phone.Number = self._clean_phone_number(ship_from.phone)
shipment.ShipTo.AttentionName = ship_to.name or ''
shipment.ShipTo.Name = ship_to.parent_id.name or ship_to.name or ''
shipment.ShipTo.Address.AddressLine = [l for l in [ship_to.street or '', ship_to.street2 or ''] if l]
shipment.ShipTo.Address.City = ship_to.city or ''
shipment.ShipTo.Address.PostalCode = ship_to.zip or ''
shipment.ShipTo.Address.CountryCode = ship_to.country_id.code or ''
if ship_to.country_id.code in ('US', 'CA', 'IE'):
shipment.ShipTo.Address.StateProvinceCode = ship_to.state_id.code or ''
shipment.ShipTo.Phone.Number = self._clean_phone_number(shipment_info['phone'])
if not ship_to.commercial_partner_id.is_company:
shipment.ShipTo.Address.ResidentialAddressIndicator = suds.null()
shipment.Service.Code = service_type or ''
shipment.Service.Description = 'Service Code'
if service_type == "96":
shipment.NumOfPiecesInShipment = int(shipment_info.get('total_qty'))
shipment.ShipmentRatingOptions.NegotiatedRatesIndicator = 1
# Shipments from US to CA or PR require extra info
if ship_from.country_id.code == 'US' and ship_to.country_id.code in ['CA', 'PR']:
shipment.InvoiceLineTotal.CurrencyCode = shipment_info.get('itl_currency_code')
shipment.InvoiceLineTotal.MonetaryValue = shipment_info.get('ilt_monetary_value')
# set the default method for payment using shipper account
payment_info = client.factory.create('ns3:PaymentInformation')
shipcharge = client.factory.create('ns3:ShipmentCharge')
shipcharge.Type = '01'
# Bill Recevier 'Bill My Account'
if ups_carrier_account:
shipcharge.BillReceiver.AccountNumber = ups_carrier_account
shipcharge.BillReceiver.Address.PostalCode = ship_to.zip
else:
shipcharge.BillShipper.AccountNumber = self.shipper_number or ''
payment_info.ShipmentCharge = shipcharge
shipment.PaymentInformation = payment_info
if saturday_delivery:
shipment.ShipmentServiceOptions.SaturdayDeliveryIndicator = saturday_delivery
else:
shipment.ShipmentServiceOptions = ''
try:
response = client.service.ProcessShipment(
Request=request, Shipment=shipment,
LabelSpecification=label)
# Check if shipment is not success then return reason for that
if response.Response.ResponseStatus.Code != "1":
return self.get_error_message(response.Response.ResponseStatus.Code, response.Response.ResponseStatus.Description)
result = {}
result['label_binary_data'] = {}
for package in response.ShipmentResults.PackageResults:
result['label_binary_data'][package.TrackingNumber] = self.save_label(package.ShippingLabel.GraphicImage, label_file_type=label_file_type)
result['tracking_ref'] = response.ShipmentResults.ShipmentIdentificationNumber
result['currency_code'] = response.ShipmentResults.ShipmentCharges.TotalCharges.CurrencyCode
# Some users are qualified to receive negotiated rates
negotiated_rate = 'NegotiatedRateCharges' in response.ShipmentResults and response.ShipmentResults.NegotiatedRateCharges.TotalCharge.MonetaryValue or None
result['price'] = negotiated_rate or response.ShipmentResults.ShipmentCharges.TotalCharges.MonetaryValue
return result
except suds.WebFault as e:
# childAtPath behaviour is changing at version 0.6
prefix = ''
if SUDS_VERSION >= "0.6":
prefix = '/Envelope/Body/Fault'
return self.get_error_message(e.document.childAtPath(prefix + '/detail/Errors/ErrorDetail/PrimaryErrorCode/Code').getText(),
e.document.childAtPath(prefix + '/detail/Errors/ErrorDetail/PrimaryErrorCode/Description').getText())
except IOError as e:
return self.get_error_message('0', 'UPS Server Not Found:\n%s' % e)
def patched_set_package_detail(self, client, packages, packaging_type, namespace, ship_from, ship_to, cod_info):
Packages = []
for i, p in enumerate(packages):
package = client.factory.create('{}:PackageType'.format(namespace))
if hasattr(package, 'Packaging'):
package.Packaging.Code = p.packaging_type or packaging_type or ''
elif hasattr(package, 'PackagingType'):
package.PackagingType.Code = p.packaging_type or packaging_type or ''
# Hibou Insurance & Signature Requirement
if p.insurance_value:
package.PackageServiceOptions.DeclaredValue.MonetaryValue = p.insurance_value
package.PackageServiceOptions.DeclaredValue.CurrencyCode = p.insurance_currency_code
if p.signature_required:
package.PackageServiceOptions.DeliveryConfirmation.DCISType = p.signature_required
if p.dimension_unit and any(p.dimension.values()):
package.Dimensions.UnitOfMeasurement.Code = p.dimension_unit or ''
package.Dimensions.Length = p.dimension['length'] or ''
package.Dimensions.Width = p.dimension['width'] or ''
package.Dimensions.Height = p.dimension['height'] or ''
if cod_info:
package.PackageServiceOptions.COD.CODFundsCode = str(cod_info['funds_code'])
package.PackageServiceOptions.COD.CODAmount.MonetaryValue = cod_info['monetary_value']
package.PackageServiceOptions.COD.CODAmount.CurrencyCode = cod_info['currency']
package.PackageWeight.UnitOfMeasurement.Code = p.weight_unit or ''
package.PackageWeight.Weight = p.weight or ''
# Package and shipment reference text is only allowed for shipments within
# the USA and within Puerto Rico. This is a UPS limitation.
if (p.name and ship_from.country_id.code in ('US') and ship_to.country_id.code in ('US')):
reference_number = client.factory.create('ns3:ReferenceNumberType')
reference_number.Code = 'PM'
reference_number.Value = p.name
reference_number.BarCodeIndicator = p.name
package.ReferenceNumber = reference_number
Packages.append(package)
return Packages
UPSRequest.__init__ = patched__init__
UPSRequest.get_shipping_price = patched_get_shipping_price UPSRequest.get_shipping_price = patched_get_shipping_price
UPSRequest.send_shipping = patched_send_shipping
UPSRequest.set_package_detail = patched_set_package_detail
def patched__init__2(self, carrier, weight, quant_pack=False, name='',
insurance_value=False, insurance_currency_code=False, signature_required=False):
self.weight = self._convert_weight(weight, carrier.ups_package_weight_unit)
self.weight_unit = carrier.ups_package_weight_unit
self.name = name
self.dimension_unit = carrier.ups_package_dimension_unit
if quant_pack:
self.dimension = {'length': quant_pack.length, 'width': quant_pack.width, 'height': quant_pack.height}
else:
self.dimension = {'length': carrier.ups_default_packaging_id.length,
'width': carrier.ups_default_packaging_id.width,
'height': carrier.ups_default_packaging_id.height}
self.packaging_type = quant_pack and quant_pack.shipper_package_code or False
self.insurance_value = insurance_value
self.insurance_currency_code = insurance_currency_code
self.signature_required = signature_required
Package.__init__ = patched__init__2

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="ups_view_warehouse" model="ir.ui.view">
<field name="name">stock.warehouse</field>
<field name="model">stock.warehouse</field>
<field name="inherit_id" ref="stock.view_warehouse" />
<field name="arch" type="xml">
<xpath expr="//field[@name='partner_id']" position="after">
<field name="ups_shipper_number"/>
</xpath>
</field>
</record>
</odoo>

View File

@@ -1,2 +1,4 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
from . import wizard from . import wizard
from . import models from . import models

View File

@@ -1,10 +1,10 @@
{ {
'name': 'Sale Order Planner', 'name': 'Sale Order Planner',
'summary': 'Plans order dates and warehouses.', 'summary': 'Plans order dates and warehouses.',
'version': '11.0.1.0.0', 'version': '11.0.2.0.0',
'author': "Hibou Corp.", 'author': "Hibou Corp.",
'category': 'Sale', 'category': 'Sale',
'license': 'AGPL-3', 'license': 'OPL-1',
'complexity': 'expert', 'complexity': 'expert',
'images': [], 'images': [],
'website': "https://hibou.io", 'website': "https://hibou.io",
@@ -37,6 +37,7 @@ on the specific method's characteristics. (e.g. Do they deliver on Saturday?)
'views/stock.xml', 'views/stock.xml',
'views/delivery.xml', 'views/delivery.xml',
'views/product.xml', 'views/product.xml',
'views/res_config_settings_views.xml',
], ],
'auto_install': False, 'auto_install': False,
'installable': True, 'installable': True,

View File

@@ -1,6 +1,9 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
from . import sale from . import sale
from . import stock from . import stock
from . import delivery from . import delivery
from . import product from . import product
from . import planning from . import planning
from . import partner from . import partner
from . import res_config_settings

View File

@@ -1,3 +1,5 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
from datetime import timedelta from datetime import timedelta
from odoo import api, fields, models from odoo import api, fields, models
@@ -61,6 +63,9 @@ class DeliveryCarrier(models.Model):
def calculate_transit_days(self, date_planned, date_delivered): def calculate_transit_days(self, date_planned, date_delivered):
self.ensure_one() self.ensure_one()
if not self.delivery_calendar_id:
return 0
if isinstance(date_planned, str): if isinstance(date_planned, str):
date_planned = fields.Datetime.from_string(date_planned) date_planned = fields.Datetime.from_string(date_planned)
if isinstance(date_delivered, str): if isinstance(date_delivered, str):

View File

@@ -1,3 +1,5 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
from odoo import api, fields, models from odoo import api, fields, models
try: try:

View File

@@ -1,3 +1,5 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
from odoo import api, fields, models from odoo import api, fields, models

View File

@@ -1,3 +1,5 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
from odoo import api, fields, models from odoo import api, fields, models

View File

@@ -0,0 +1,79 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
from odoo import api, fields, models
def sale_planner_warehouse_ids(env, company):
get_param = env['ir.config_parameter'].sudo().get_param
warehouse_ids = get_param('sale.planner.warehouse_ids.%s' % (company.id, )) or []
if warehouse_ids and isinstance(warehouse_ids, str):
try:
warehouse_ids = [int(i) for i in warehouse_ids.split(',')]
except:
warehouse_ids = []
return warehouse_ids
def sale_planner_carrier_ids(env, company):
get_param = env['ir.config_parameter'].sudo().get_param
carrier_ids = get_param('sale.planner.carrier_ids.%s' % (company.id, )) or []
if carrier_ids and isinstance(carrier_ids, str):
try:
carrier_ids = [int(c) for c in carrier_ids.split(',')]
except:
carrier_ids = []
return carrier_ids
class ResConfigSettings(models.TransientModel):
_inherit = 'res.config.settings'
sale_planner_warehouse_ids = fields.Many2many('stock.warehouse',
string='Sale Order Planner Warehouses',
compute='_compute_sale_planner_warehouse_ids',
inverse='_inverse_sale_planner_warehouse_ids')
sale_planner_carrier_ids = fields.Many2many('delivery.carrier',
string='Sale Order Planner Carriers',
compute='_compute_sale_planner_carrier_ids',
inverse='_inverse_sale_planner_carrier_ids')
def _compute_sale_planner_warehouse_ids_ids(self):
company = self.company_id or self.env.user.company_id
return sale_planner_warehouse_ids(self.env, company)
def _compute_sale_planner_carrier_ids_ids(self):
company = self.company_id or self.env.user.company_id
return sale_planner_carrier_ids(self.env, company)
def _compute_sale_planner_warehouse_ids(self):
for settings in self:
warehouse_ids = settings._compute_sale_planner_warehouse_ids_ids()
warehouses = self.env['stock.warehouse'].browse(warehouse_ids)
settings.sale_planner_warehouse_ids = warehouses
def _compute_sale_planner_carrier_ids(self):
for settings in self:
carrier_ids = settings._compute_sale_planner_carrier_ids_ids()
carriers = self.env['delivery.carrier'].browse(carrier_ids)
settings.sale_planner_carrier_ids = carriers
def _inverse_sale_planner_warehouse_ids(self):
set_param = self.env['ir.config_parameter'].sudo().set_param
company_id = self.company_id.id or self.env.user.company_id.id
for settings in self:
warehouse_ids = ','.join(str(i) for i in settings.sale_planner_warehouse_ids.ids)
set_param('sale.planner.warehouse_ids.%s' % (company_id, ), warehouse_ids)
def _inverse_sale_planner_carrier_ids(self):
set_param = self.env['ir.config_parameter'].sudo().set_param
company_id = self.company_id.id or self.env.user.company_id.id
for settings in self:
carrier_ids = ','.join(str(i) for i in settings.sale_planner_carrier_ids.ids)
set_param('sale.planner.carrier_ids.%s' % (company_id, ), carrier_ids)
@api.model
def get_values(self):
res = super(ResConfigSettings, self).get_values()
res['sale_planner_warehouse_ids'] = [(6, 0, self._compute_sale_planner_warehouse_ids_ids())]
res['sale_planner_carrier_ids'] = [(6, 0, self._compute_sale_planner_carrier_ids_ids())]
return res

View File

@@ -1,3 +1,5 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
from odoo import api, fields, models from odoo import api, fields, models

View File

@@ -1,3 +1,5 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
from odoo import api, fields, models from odoo import api, fields, models
@@ -7,3 +9,7 @@ class Warehouse(models.Model):
shipping_calendar_id = fields.Many2one( shipping_calendar_id = fields.Many2one(
'resource.calendar', 'Shipping Calendar', 'resource.calendar', 'Shipping Calendar',
help="This calendar represents shipping availability from the warehouse.") help="This calendar represents shipping availability from the warehouse.")
sale_planner_carrier_ids = fields.Many2many('delivery.carrier',
relation='sale_planner_carrier_wh_rel',
string='Sale Order Planner Base Carriers',
help='Overrides the global carriers.')

View File

@@ -1 +1,3 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
from . import test_planner from . import test_planner

View File

@@ -1,3 +1,5 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
from odoo.tests import common from odoo.tests import common
from datetime import datetime, timedelta from datetime import datetime, timedelta
from json import loads as json_decode from json import loads as json_decode

View File

@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="res_config_settings_view_form" model="ir.ui.view">
<field name="name">res.config.settings.view.form.inherit</field>
<field name="model">res.config.settings</field>
<field name="priority" eval="49"/>
<field name="inherit_id" ref="delivery.res_config_settings_view_form"/>
<field name="arch" type="xml">
<xpath expr="//div[@id='sale_ebay']" position="after">
<h2>Sale Order Planner</h2>
<div class="col-lg-6 col-12 o_setting_box" id="sale_planner_carriers">
<div class="o_setting_left_pane"/>
<div class="o_setting_right_pane">
<label for="sale_planner_carrier_ids" />
<div class="text-muted">
Add a carrier that represents the 'base rate' for a carrier's type. <br/>
For example, you should add 1 FedEx carrier here and let us build up the
rates for your other FedEx shipping methods.
</div>
<field name="sale_planner_carrier_ids" class="oe_inline" options="{'no_create_edit': True, 'no_create': True}" />
</div>
</div>
<div class="col-lg-6 col-12 o_setting_box" id="sale_planner_warehouses">
<div class="o_setting_left_pane"/>
<div class="o_setting_right_pane">
<label for="sale_planner_warehouse_ids" />
<div class="text-muted">
Warehouses you typically ship inventory out of that you want to
include in the planning of sale orders.
</div>
<field name="sale_planner_warehouse_ids" class="oe_inline" options="{'no_create_edit': True, 'no_create': True}" />
</div>
</div>
</xpath>
</field>
</record>
</odoo>

View File

@@ -7,6 +7,7 @@
<field name="arch" type="xml"> <field name="arch" type="xml">
<xpath expr="//field[@name='partner_id']" position="after"> <xpath expr="//field[@name='partner_id']" position="after">
<field name="shipping_calendar_id" /> <field name="shipping_calendar_id" />
<field name="sale_planner_carrier_ids" options="{'no_create_edit': True, 'no_create': True}" />
</xpath> </xpath>
</field> </field>
</record> </record>

View File

@@ -1 +1,3 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
from . import order_planner from . import order_planner

View File

@@ -1,3 +1,5 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
from math import sin, cos, sqrt, atan2, radians from math import sin, cos, sqrt, atan2, radians
from json import dumps, loads from json import dumps, loads
from copy import deepcopy from copy import deepcopy
@@ -15,6 +17,7 @@ except ImportError:
from odoo import api, fields, models, tools from odoo import api, fields, models, tools
from odoo.addons.base_geolocalize.models.res_partner import geo_find, geo_query_address from odoo.addons.base_geolocalize.models.res_partner import geo_find, geo_query_address
from ..models.res_config_settings import sale_planner_warehouse_ids, sale_planner_carrier_ids
class FakeCollection(): class FakeCollection():
@@ -26,10 +29,17 @@ class FakeCollection():
yield v yield v
def filtered(self, f): def filtered(self, f):
return filter(f, self.vals) return self.__class__([v for v in self.vals if f(v)])
def mapped(self, s):
# note this only maps to one level and doesn't really support recordset
return [v[s] for v in self.vals]
def sudo(self, *args, **kwargs):
return self
class FakePartner(): class FakePartner(FakeCollection):
def __init__(self, **kwargs): def __init__(self, **kwargs):
""" """
'delivery.carrier'.verify_carrier(contact) -> 'delivery.carrier'.verify_carrier(contact) ->
@@ -92,7 +102,7 @@ class FakePartner():
return getattr(self, item) return getattr(self, item)
class FakeOrderLine(): class FakeOrderLine(FakeCollection):
def __init__(self, **kwargs): def __init__(self, **kwargs):
""" """
'delivery.carrier'.get_price_available(order) -> 'delivery.carrier'.get_price_available(order) ->
@@ -130,7 +140,7 @@ class FakeOrderLine():
return getattr(self, item) return getattr(self, item)
class FakeSaleOrder(): class FakeSaleOrder(FakeCollection):
""" """
partner_id :: used in shipping partner_id :: used in shipping
partner_shipping_id :: is used in several places partner_shipping_id :: is used in several places
@@ -294,19 +304,18 @@ class SaleOrderMakePlan(models.TransientModel):
if domain: if domain:
if not isinstance(domain, (list, tuple)): if not isinstance(domain, (list, tuple)):
domain = tools.safe_eval(domain) domain = tools.safe_eval(domain)
else:
domain = []
if self.env.context.get('warehouse_domain'): if self.env.context.get('warehouse_domain'):
if not domain:
domain = []
domain.extend(self.env.context.get('warehouse_domain')) domain.extend(self.env.context.get('warehouse_domain'))
if domain:
irconfig_parameter = self.env['ir.config_parameter'].sudo()
if irconfig_parameter.get_param('sale.order.planner.warehouse_domain'):
domain.extend(tools.safe_eval(irconfig_parameter.get_param('sale.order.planner.warehouse_domain')))
return warehouse.search(domain) return warehouse.search(domain)
def get_shipping_carriers(self, carrier_id=None, domain=None): # no domain, use global
warehouse_ids = sale_planner_warehouse_ids(self.env, self.env.user.company_id)
return warehouse.browse(warehouse_ids)
def get_shipping_carriers(self, carrier_id=None, domain=None, warehouse_id=None):
Carrier = self.env['delivery.carrier'].sudo() Carrier = self.env['delivery.carrier'].sudo()
if carrier_id: if carrier_id:
return Carrier.browse(carrier_id) return Carrier.browse(carrier_id)
@@ -314,19 +323,21 @@ class SaleOrderMakePlan(models.TransientModel):
if domain: if domain:
if not isinstance(domain, (list, tuple)): if not isinstance(domain, (list, tuple)):
domain = tools.safe_eval(domain) domain = tools.safe_eval(domain)
else:
domain = []
if self.env.context.get('carrier_domain'): if self.env.context.get('carrier_domain'):
# potential bug here if this is textual if not domain:
domain = []
domain.extend(self.env.context.get('carrier_domain')) domain.extend(self.env.context.get('carrier_domain'))
if domain:
irconfig_parameter = self.env['ir.config_parameter'].sudo()
if irconfig_parameter.get_param('sale.order.planner.carrier_domain'):
domain.extend(tools.safe_eval(irconfig_parameter.get_param('sale.order.planner.carrier_domain')))
return Carrier.search(domain) return Carrier.search(domain)
# no domain, use global
if warehouse_id:
warehouse = self.env['stock.warehouse'].sudo().browse(warehouse_id)
if warehouse.sale_planner_carrier_ids:
return warehouse.sale_planner_carrier_ids.sudo()
carrier_ids = sale_planner_carrier_ids(self.env, self.env.user.company_id)
return Carrier.browse(carrier_ids)
def _generate_base_option(self, order_fake, policy_group): def _generate_base_option(self, order_fake, policy_group):
policy = False policy = False
flag_force_closest = False flag_force_closest = False
@@ -609,6 +620,8 @@ class SaleOrderMakePlan(models.TransientModel):
return self._find_closest_warehouse(warehouses, partner.partner_latitude, partner.partner_longitude) return self._find_closest_warehouse(warehouses, partner.partner_latitude, partner.partner_longitude)
def _find_closest_warehouse(self, warehouses, latitude, longitude): def _find_closest_warehouse(self, warehouses, latitude, longitude):
if not warehouses:
return warehouses
distances = {distance(latitude, longitude, wh.partner_id.partner_latitude, wh.partner_id.partner_longitude): wh.id for wh in warehouses} distances = {distance(latitude, longitude, wh.partner_id.partner_latitude, wh.partner_id.partner_longitude): wh.id for wh in warehouses}
wh_id = distances[min(distances)] wh_id = distances[min(distances)]
return warehouses.filtered(lambda wh: wh.id == wh_id) return warehouses.filtered(lambda wh: wh.id == wh_id)
@@ -659,19 +672,19 @@ class SaleOrderMakePlan(models.TransientModel):
policy = line.product_id.product_tmpl_id.get_planning_policy() policy = line.product_id.product_tmpl_id.get_planning_policy()
if policy and policy.carrier_filter_id: if policy and policy.carrier_filter_id:
domain.extend(tools.safe_eval(policy.carrier_filter_id.domain)) domain.extend(tools.safe_eval(policy.carrier_filter_id.domain))
carriers = self.get_shipping_carriers(base_option.get('carrier_id'), domain=domain) carriers = self.get_shipping_carriers(base_option.get('carrier_id'), domain=domain, warehouse_id=base_option.get('warehouse_id'))
_logger.info('generate_shipping_options:: base_optoin: ' + str(base_option) + ' order_fake: ' + str(order_fake) + ' carriers: ' + str(carriers)) _logger.info('generate_shipping_options:: base_option: ' + str(base_option) + ' order_fake: ' + str(order_fake) + ' carriers: ' + str(carriers))
if not carriers: if not carriers:
return base_option return [base_option]
if not base_option.get('sub_options'): if not base_option.get('sub_options'):
options = [] options = []
# this locic comes from "delivery.models.sale_order.SaleOrder" # this locic comes from "delivery.models.sale_order.SaleOrder"
for carrier in carriers: for carrier in carriers:
option = self._generate_shipping_carrier_option(base_option, order_fake, carrier) carrier_options = self._generate_shipping_carrier_option(base_option, order_fake, carrier)
if option: if carrier_options:
options.append(option) options += carrier_options
if options: if options:
return options return options
return [base_option] return [base_option]
@@ -683,26 +696,40 @@ class SaleOrderMakePlan(models.TransientModel):
for carrier in carriers: for carrier in carriers:
new_base_option = deepcopy(base_option) new_base_option = deepcopy(base_option)
has_error = False has_error = False
found_carrier_ids = set()
for wh_id, wh_vals in base_option['sub_options'].items(): for wh_id, wh_vals in base_option['sub_options'].items():
if has_error: if has_error:
continue continue
order_fake.warehouse_id = warehouses.filtered(lambda wh: wh.id == wh_id) order_fake.warehouse_id = warehouses.filtered(lambda wh: wh.id == wh_id)
order_fake.order_line = FakeCollection(filter(lambda line: line.product_id.id in wh_vals['product_ids'], original_order_fake_order_line)) order_fake.order_line = FakeCollection(list(filter(lambda line: line.product_id.id in wh_vals['product_ids'], original_order_fake_order_line)))
wh_option = self._generate_shipping_carrier_option(wh_vals, order_fake, carrier) wh_carrier_options = self._generate_shipping_carrier_option(wh_vals, order_fake, carrier)
if not wh_option: if not wh_carrier_options:
has_error = True has_error = True
else: else:
new_base_option['sub_options'][wh_id] = wh_option for _option in wh_carrier_options:
if _option.get('carrier_id'):
found_carrier_ids.add(_option['carrier_id'])
new_base_option['sub_options'][wh_id] = wh_carrier_options
if has_error: if has_error:
continue continue
# now that we've collected, we can roll up some details.
new_base_option['carrier_id'] = carrier.id # now that we've collected details for this carrier, we likely have more than one carrier's rates
new_base_option['shipping_price'] = self._get_shipping_price_for_options(new_base_option['sub_options']) _logger.info(' from ' + str(carrier) + ' found ' + str(found_carrier_ids))
new_base_option['requested_date'] = self._get_max_requested_date(new_base_option['sub_options']) for carrier_id in found_carrier_ids:
new_base_option['transit_days'] = self._get_max_transit_days(new_base_option['sub_options']) carrier_option = deepcopy(base_option)
options.append(new_base_option) carrier_option['carrier_id'] = False
for wh_id, wh_vals in base_option['sub_options'].items():
for co in new_base_option['sub_options'].get(wh_id, []):
if co.get('carrier_id') == carrier_id:
# we have found the rate!
carrier_option['carrier_id'] = carrier_id
carrier_option['sub_options'][wh_id] = co
if carrier_option['carrier_id']:
carrier_option['shipping_price'] = self._get_shipping_price_for_options(carrier_option['sub_options'])
carrier_option['requested_date'] = self._get_max_requested_date(carrier_option['sub_options'])
carrier_option['transit_days'] = self._get_max_transit_days(carrier_option['sub_options'])
options.append(carrier_option)
#restore values in case more processing occurs #restore values in case more processing occurs
order_fake.warehouse_id = original_order_fake_warehouse_id order_fake.warehouse_id = original_order_fake_warehouse_id
@@ -735,6 +762,8 @@ class SaleOrderMakePlan(models.TransientModel):
def _generate_shipping_carrier_option(self, base_option, order_fake, carrier): def _generate_shipping_carrier_option(self, base_option, order_fake, carrier):
# some carriers look at the order carrier_id # some carriers look at the order carrier_id
order_fake.carrier_id = carrier order_fake.carrier_id = carrier
date_planned = base_option.get('date_planned')
order_fake.date_planned = date_planned
# this logic comes from "delivery.models.sale_order.SaleOrder" # this logic comes from "delivery.models.sale_order.SaleOrder"
try: try:
@@ -742,7 +771,9 @@ class SaleOrderMakePlan(models.TransientModel):
date_delivered = None date_delivered = None
transit_days = 0 transit_days = 0
if carrier.delivery_type not in ['fixed', 'base_on_rule']: if carrier.delivery_type not in ['fixed', 'base_on_rule']:
if hasattr(carrier, 'rate_shipment_date_planned'): if hasattr(carrier, 'rate_shipment_multi'):
result = carrier.rate_shipment_multi(order=order_fake)
elif hasattr(carrier, 'rate_shipment_date_planned'):
# New API # New API
result = carrier.rate_shipment_date_planned(order_fake, base_option.get('date_planned')) result = carrier.rate_shipment_date_planned(order_fake, base_option.get('date_planned'))
if result: if result:
@@ -752,7 +783,8 @@ class SaleOrderMakePlan(models.TransientModel):
elif hasattr(carrier, 'get_shipping_price_for_plan'): elif hasattr(carrier, 'get_shipping_price_for_plan'):
# Old API # Old API
result = carrier.get_shipping_price_for_plan(order_fake, base_option.get('date_planned')) result = carrier.get_shipping_price_for_plan(order_fake, base_option.get('date_planned'))
if result and isinstance(result, list): if result and isinstance(result, list) and not isinstance(result[0], dict):
# this detects the above only if it isn't a list of dictionaries (aka multi-rating result)
price_unit, transit_days, date_delivered = result[0] price_unit, transit_days, date_delivered = result[0]
elif not result: elif not result:
rate = carrier.rate_shipment(order_fake) rate = carrier.rate_shipment(order_fake)
@@ -779,13 +811,38 @@ class SaleOrderMakePlan(models.TransientModel):
if order_fake.company_id.currency_id.id != order_fake.pricelist_id.currency_id.id: if order_fake.company_id.currency_id.id != order_fake.pricelist_id.currency_id.id:
price_unit = order_fake.company_id.currency_id.with_context(date=order_fake.date_order).compute(price_unit, order_fake.pricelist_id.currency_id) price_unit = order_fake.company_id.currency_id.with_context(date=order_fake.date_order).compute(price_unit, order_fake.pricelist_id.currency_id)
if result and isinstance(result, list):
res = []
for rate in result:
rate_carrier = rate.get('carrier')
if not rate_carrier:
continue
price_unit = rate['price']
date_delivered = rate.get('date_delivered')
transit_days = rate.get('transit_days')
if date_planned and transit_days and not date_delivered:
# compute from planned date anc current rate carrier
date_delivered = rate_carrier.calculate_date_delivered(date_planned, transit_days)
elif date_planned and date_delivered and not transit_days:
transit_days = rate_carrier.calculate_transit_days(date_planned, date_delivered)
final_price = float(price_unit) * (1.0 + (float(rate_carrier.margin) / 100.0))
option = deepcopy(base_option)
option['carrier_id'] = rate_carrier.id
option['shipping_price'] = final_price
option['requested_date'] = fields.Datetime.to_string(date_delivered) if (date_delivered and isinstance(date_delivered, datetime)) else date_delivered
option['transit_days'] = transit_days
res.append(option)
return res
else:
final_price = float(price_unit) * (1.0 + (float(carrier.margin) / 100.0)) final_price = float(price_unit) * (1.0 + (float(carrier.margin) / 100.0))
option = deepcopy(base_option) option = deepcopy(base_option)
option['carrier_id'] = carrier.id option['carrier_id'] = carrier.id
option['shipping_price'] = final_price option['shipping_price'] = final_price
option['requested_date'] = fields.Datetime.to_string(date_delivered) if (date_delivered and isinstance(date_delivered, datetime)) else date_delivered option['requested_date'] = fields.Datetime.to_string(date_delivered) if (date_delivered and isinstance(date_delivered, datetime)) else date_delivered
option['transit_days'] = transit_days option['transit_days'] = transit_days
return option return [option]
except Exception as e: except Exception as e:
_logger.info("Exception collecting carrier rates: " + str(e)) _logger.info("Exception collecting carrier rates: " + str(e))
# Want to see more? # Want to see more?
@@ -794,7 +851,6 @@ class SaleOrderMakePlan(models.TransientModel):
return None return None
class SaleOrderPlanningOption(models.TransientModel): class SaleOrderPlanningOption(models.TransientModel):
_name = 'sale.order.planning.option' _name = 'sale.order.planning.option'
_description = 'Order Planning Option' _description = 'Order Planning Option'

View File

@@ -0,0 +1,4 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
from . import models
from . import wizard

View File

@@ -0,0 +1,29 @@
{
'name': 'Stock Delivery Planner',
'summary': 'Get rates and choose carrier for delivery.',
'version': '11.0.1.0.0',
'author': "Hibou Corp.",
'category': 'Warehouse',
'license': 'OPL-1',
'website': "https://hibou.io",
'description': """
Stock Delivery Planner
======================
Re-rate deliveries at packing time to find lowest-priced delivery method that still meets the expected delivery date.
""",
'depends': [
'delivery_hibou',
'sale_planner',
'stock',
],
'data': [
'security/ir.model.access.csv',
'views/res_config_settings_views.xml',
'views/stock_views.xml',
'wizard/stock_delivery_planner_views.xml',
],
'auto_install': False,
'installable': True,
}

View File

@@ -0,0 +1,4 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
from . import res_config_settings
from . import stock

View File

@@ -0,0 +1,43 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
from odoo import api, fields, models
class ResConfigSettings(models.TransientModel):
_inherit = 'res.config.settings'
stock_delivery_planner_base_carrier_ids = fields.Many2many('delivery.carrier',
string='Delivery Planner Base Carriers',
compute='_compute_stock_delivery_planner_base_carrier_ids',
inverse='_inverse_stock_delivery_planner_base_carrier_ids')
def _compute_stock_delivery_planner_base_carrier_ids_ids(self):
# used to compute the field and update in get_values
get_param = self.env['ir.config_parameter'].sudo().get_param
company_id = self.company_id.id or self.env.user.company_id.id
carrier_ids = get_param('stock.delivery.planner.carrier_ids.%s' % (company_id,)) or []
if carrier_ids and isinstance(carrier_ids, str):
try:
carrier_ids = [int(c) for c in carrier_ids.split(',')]
except:
carrier_ids = []
return carrier_ids
def _compute_stock_delivery_planner_base_carrier_ids(self):
for settings in self:
carrier_ids = settings._compute_stock_delivery_planner_base_carrier_ids_ids()
carriers = self.env['delivery.carrier'].browse(carrier_ids)
settings.stock_delivery_planner_base_carrier_ids = carriers
def _inverse_stock_delivery_planner_base_carrier_ids(self):
set_param = self.env['ir.config_parameter'].sudo().set_param
company_id = self.company_id.id or self.env.user.company_id.id
for settings in self:
carrier_ids = ','.join(str(i) for i in settings.stock_delivery_planner_base_carrier_ids.ids)
set_param('stock.delivery.planner.carrier_ids.%s' % (company_id, ), carrier_ids)
@api.model
def get_values(self):
res = super(ResConfigSettings, self).get_values()
res['stock_delivery_planner_base_carrier_ids'] = [(6, 0, self._compute_stock_delivery_planner_base_carrier_ids_ids())]
return res

View File

@@ -0,0 +1,59 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
from odoo import api, fields, models, tools, _
from odoo.exceptions import UserError
class StockPicking(models.Model):
_inherit = 'stock.picking'
@api.multi
def action_plan_delivery(self):
context = dict(self.env.context or {})
planner_model = self.env['stock.delivery.planner']
for picking in self:
planner = planner_model.create({
'picking_id': picking.id,
})
return {
'name': _('Plan Delivery'),
'type': 'ir.actions.act_window',
'res_model': 'stock.delivery.planner',
'res_id': planner.id,
'view_type': 'form',
'view_mode': 'form',
'view_id': self.env.ref('stock_delivery_planner.view_stock_delivery_planner').id,
'target': 'new',
'context': context,
}
# def get_shipping_carriers(self, carrier_id=None, domain=None):
def get_shipping_carriers(self):
Carrier = self.env['delivery.carrier'].sudo()
# if carrier_id:
# return Carrier.browse(carrier_id)
#
# if domain:
# if not isinstance(domain, (list, tuple)):
# domain = tools.safe_eval(domain)
# else:
domain = []
if self.env.context.get('carrier_domain'):
# potential bug here if this is textual
domain.extend(self.env.context.get('carrier_domain'))
irconfig_parameter = self.env['ir.config_parameter'].sudo()
if irconfig_parameter.get_param('sale.order.planner.carrier_domain'):
domain.extend(tools.safe_eval(irconfig_parameter.get_param('sale.order.planner.carrier_domain')))
return Carrier.search(domain)
class Warehouse(models.Model):
_inherit = 'stock.warehouse'
delivery_planner_carrier_ids = fields.Many2many('delivery.carrier',
relation='delivery_planner_carrier_wh_rel',
string='Delivery Planner Base Carriers',
help='Overrides the global carriers.')

View File

@@ -0,0 +1,3 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_stock_delivery_planner_user,stock.delivery.planner user,model_stock_delivery_planner,stock.group_stock_user,1,1,1,0
access_stock_delivery_planner_option_user,stock.delivery.planner.option user,model_stock_delivery_planner_option,stock.group_stock_user,1,1,1,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_stock_delivery_planner_user stock.delivery.planner user model_stock_delivery_planner stock.group_stock_user 1 1 1 0
3 access_stock_delivery_planner_option_user stock.delivery.planner.option user model_stock_delivery_planner_option stock.group_stock_user 1 1 1 0

View File

@@ -0,0 +1 @@
from . import test_stock_delivery_planner

View File

@@ -0,0 +1,139 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
from odoo import fields
from odoo.tests.common import Form, TransactionCase
class TestStockDeliveryPlanner(TransactionCase):
def setUp(self):
"""
NOTE: demo Fedex credentials may not work. Test credentials may not return all service types.
Configuring production credentials may be necessary for this test to run.
"""
super(TestStockDeliveryPlanner, self).setUp()
try:
self.fedex_ground = self.browse_ref('delivery_fedex.delivery_carrier_fedex_us')
except ValueError:
self.skipTest('FedEx Shipping Connector demo data is required to run this test.')
self.env['ir.config_parameter'].sudo().set_param('sale.order.planner.carrier_domain',
"[('id', 'in', (%d,))]" % self.fedex_ground.id)
self.env['ir.config_parameter'].sudo().set_param('stock.delivery.planner.carrier_ids.%s' % (self.env.user.company_id.id, ),
"%d" % self.fedex_ground.id)
# Does it make sense to set default package in fedex_rate_shipment_multi
# instead of relying on a correctly configured delivery method?
self.fedex_package = self.browse_ref('delivery_fedex.fedex_packaging_FEDEX_25KG_BOX')
self.default_package = self.browse_ref('delivery_fedex.fedex_packaging_YOUR_PACKAGING')
self.fedex_ground.fedex_default_packaging_id = self.default_package
self.fedex_ground.fedex_service_type = 'FEDEX_GROUND'
self.fedex_2_day = self.fedex_ground.copy()
self.fedex_2_day.name = 'Test FedEx Delivery'
self.fedex_2_day.fedex_service_type = 'FEDEX_2_DAY'
delivery_calendar = self.env['resource.calendar'].create({
'name': 'Test Delivery Calendar',
'tz': 'US/Central',
'attendance_ids': [
(0, 0, {'name': 'Monday', 'dayofweek': '0', 'hour_from': 0, 'hour_to': 23.99, 'day_period': 'morning'}),
(0, 0, {'name': 'Tuesday', 'dayofweek': '1', 'hour_from': 0, 'hour_to': 23.99, 'day_period': 'morning'}),
(0, 0, {'name': 'Wednesday', 'dayofweek': '2', 'hour_from': 0, 'hour_to': 23.99, 'day_period': 'morning'}),
(0, 0, {'name': 'Thursday', 'dayofweek': '3', 'hour_from': 0, 'hour_to': 23.99, 'day_period': 'morning'}),
(0, 0, {'name': 'Friday', 'dayofweek': '4', 'hour_from': 0, 'hour_to': 23.99, 'day_period': 'morning'}),
],
})
self.fedex_ground.delivery_calendar_id = delivery_calendar
self.fedex_2_day.delivery_calendar_id = delivery_calendar
self.env['stock.warehouse'].search([]).write({'shipping_calendar_id': delivery_calendar.id})
# needs a valid address for sender and recipient
self.country_usa = self.env['res.country'].search([('name', '=', 'United States')], limit=1)
self.state_wa = self.env['res.country.state'].search([('name', '=', 'Washington')], limit=1)
self.state_ia = self.env['res.country.state'].search([('name', '=', 'Iowa')], limit=1)
self.env.user.company_id.partner_id.write({
'street': '321 1st St',
'city': 'Ames',
'state_id': self.state_ia.id,
'zip': '50010',
'country_id': self.country_usa.id,
})
self.partner = self.env['res.partner'].create({
'name': 'Test Customer',
'street': '1234 Test Street',
'city': 'Marysville',
'state_id': self.state_wa.id,
'zip': '98270',
'country_id': self.country_usa.id,
'is_company': True,
# 'partner_latitude': 48.05636,
# 'partner_longitude': -122.14922,
'customer': True,
})
# self.product = self.browse_ref('product.product_product_27') # [FURN_8855] Drawer
# self.product.weight = 5.0
# self.product.volume = 0.1
self.env['ir.config_parameter'].sudo().set_param('product.weight_in_lbs', '1')
self.product = self.env['product.product'].create({
'name': 'Test Ship Product',
'type': 'product',
'weight': 1.0,
})
self.env['stock.change.product.qty'].create({
'product_id': self.product.id,
'new_quantity': 10.0,
}).change_product_qty()
so = Form(self.env['sale.order'])
so.partner_id = self.partner
so.carrier_id = self.env['delivery.carrier'].browse()
with so.order_line.new() as line:
line.product_id = self.product
line.product_uom_qty = 5.0
line.price_unit = 100.0
self.sale_order = so.save()
order_plan_action = self.sale_order.action_planorder()
order_plan = self.env[order_plan_action['res_model']].browse(order_plan_action['res_id'])
order_plan.planning_option_ids.filtered(lambda o: o.carrier_id == self.fedex_ground).select_plan()
self.sale_order.action_confirm()
self.picking = self.sale_order.picking_ids
def test_00_test_one_package(self):
"""Delivery is packed in one package"""
self.assertTrue(self.sale_order.requested_date, 'Order has not been planned')
self.assertEqual(len(self.picking), 1)
grp_pack = self.env.ref('stock.group_tracking_lot')
self.env.user.write({'groups_id': [(4, grp_pack.id)]})
self.assertEqual(self.picking.carrier_id, self.fedex_ground, 'Carrier did not carry over to Delivery Order')
self.assertEqual(self.picking.weight, 5.0)
self.assertEqual(self.picking.shipping_weight, 0.0)
self.picking.move_line_ids.filtered(lambda ml: ml.product_id == self.product).qty_done = 5.0
packing_action = self.picking.action_put_in_pack()
packing_wizard = Form(self.env[packing_action['res_model']].with_context(packing_action['context']))
packing_wizard.delivery_packaging_id = self.fedex_package
choose_delivery_package = packing_wizard.save()
choose_delivery_package.action_put_in_pack()
self.assertEqual(self.picking.shipping_weight, 5.0)
action = self.picking.action_plan_delivery()
planner = self.env[action['res_model']].browse(action['res_id'])
self.assertEqual(planner.picking_id, self.picking)
self.assertGreater(len(planner.plan_option_ids), 1)
plan_option = planner.plan_option_ids.filtered(lambda o: o.carrier_id == self.fedex_2_day)
self.assertEqual(len(plan_option), 1)
self.assertGreater(plan_option.price, 0.0)
self.assertEqual(plan_option.date_planned.date(), fields.Date().today())
self.assertTrue(plan_option.requested_date)
self.assertEqual(plan_option.transit_days, 2)
self.assertEqual(plan_option.sale_requested_date, self.sale_order.requested_date)
# Order Planner expects to ship tomorrow: we are shipping a day early and using
# 2-day shipping instead of 3, giving us 2 days difference
self.assertEqual(plan_option.days_different, -2.0)
plan_option.select_plan()
self.assertEqual(self.picking.carrier_id, self.fedex_2_day)

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="res_config_settings_view_form" model="ir.ui.view">
<field name="name">res.config.settings.view.form.inherit</field>
<field name="model">res.config.settings</field>
<field name="priority" eval="50"/>
<field name="inherit_id" ref="delivery.res_config_settings_view_form"/>
<field name="arch" type="xml">
<xpath expr="//div[@id='sale_ebay']" position="after">
<h2>Delivery Planner</h2>
<div class="col-lg-6 col-12 o_setting_box" id="stock_delivery_planner">
<div class="o_setting_left_pane"/>
<div class="o_setting_right_pane">
<label for="stock_delivery_planner_base_carrier_ids" />
<div class="text-muted">
Add a carrier that represents the 'base rate' for a carrier's type. <br/>
For example, you should add 1 FedEx carrier here and let us build up the
rates for your other FedEx shipping methods.
</div>
<field name="stock_delivery_planner_base_carrier_ids" class="oe_inline" options="{'no_create_edit': True, 'no_create': True}" />
</div>
</div>
</xpath>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="view_picking_form" model="ir.ui.view">
<field name="name">stock.picking.form.inherit.delivery.planner</field>
<field name="model">stock.picking</field>
<field name="inherit_id" ref="stock.view_picking_form"/>
<field name="arch" type="xml">
<xpath expr="//header" position="inside">
<button name="action_plan_delivery" type="object" string="Plan Shipment" class="oe_highlight" attrs="{'invisible': [('picking_type_code', '!=', 'outgoing')]}"/>
</xpath>
</field>
</record>
<record id="view_warehouse_delivery_carriers" model="ir.ui.view">
<field name="name">stock.warehouse.delivery.carriers</field>
<field name="model">stock.warehouse</field>
<field name="inherit_id" ref="stock.view_warehouse" />
<field name="arch" type="xml">
<xpath expr="//field[@name='partner_id']" position="after">
<field name="delivery_planner_carrier_ids" options="{'no_create_edit': True, 'no_create': True}" />
</xpath>
</field>
</record>
</odoo>

View File

@@ -0,0 +1 @@
from . import stock_delivery_planner

View File

@@ -0,0 +1,142 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
from odoo import api, fields, models, _
from odoo.exceptions import UserError, ValidationError
import logging
_logger = logging.getLogger(__name__)
class StockDeliveryPlanner(models.TransientModel):
_name = 'stock.delivery.planner'
_description = 'Stock Delivery Planner Wizard'
picking_id = fields.Many2one('stock.picking', 'Transfer')
plan_option_ids = fields.One2many('stock.delivery.planner.option', 'plan_id', 'Options')
packages_planned = fields.Boolean(compute='_compute_packages_planned')
@api.depends('plan_option_ids.selection')
def _compute_packages_planned(self):
for wiz in self:
packages = wiz.picking_id.package_ids
if not packages:
wiz.packages_planned = False
selected_options = wiz.plan_option_ids.filtered(lambda p: p.selection == 'selected')
wiz.packages_planned = len(selected_options) == len(packages)
def create(self, values):
planner = super(StockDeliveryPlanner, self).create(values)
base_carriers = planner.picking_id.picking_type_id.warehouse_id.delivery_planner_carrier_ids
if not base_carriers:
carrier_ids = self.env['ir.config_parameter'].sudo().get_param('stock.delivery.planner.carrier_ids.%s' % (self.env.user.company_id.id, ))
if carrier_ids:
try:
carrier_ids = [int(c) for c in carrier_ids.split(',')]
base_carriers = base_carriers.browse(carrier_ids)
except:
pass
base_carriers = base_carriers.sudo()
for carrier in base_carriers:
try:
rates = carrier.rate_shipment_multi(picking=planner.picking_id)
for rate in filter(lambda r: not r.get('success'), rates):
_logger.warning(rate.get('error_message'))
for rate in filter(lambda r: r.get('success'), rates):
rate = self.calculate_delivery_window(rate)
# added late in API dev cycle
package = rate.get('package') or self.env['stock.quant.package'].browse()
planner.plan_option_ids |= planner.plan_option_ids.create({
'plan_id': self.id,
'carrier_id': rate['carrier'].id,
'package_id': package.id,
'price': rate['price'],
'date_planned': rate['date_planned'],
'requested_date': rate.get('date_delivered', False),
'transit_days': rate.get('transit_days', 0),
})
except (UserError, ValidationError) as e:
_logger.warning('Exception during delivery planning. %s' % str(e))
return planner
@api.model
def calculate_delivery_window(self, rate):
carrier = rate['carrier']
date_planned = rate['date_planned']
if rate.get('date_delivered'):
date_delivered = rate['date_delivered']
transit_days = carrier.calculate_transit_days(date_planned, date_delivered)
if not rate.get('transit_days') or transit_days < rate.get('transit_days'):
rate['transit_days'] = transit_days
elif rate.get('transit_days'):
rate['date_delivered'] = carrier.calculate_date_delivered(date_planned, rate.get('transit_days'))
return rate
def action_plan(self):
self.ensure_one()
selected_package_options = self.plan_option_ids.filtered(lambda o: o.package_id and o.selection == 'selected')
selected_package_options._plan()
return {"type": "ir.actions.act_window_close"}
class StockDeliveryOption(models.TransientModel):
_name = 'stock.delivery.planner.option'
_description = 'Stock Delivery Planner Option'
plan_id = fields.Many2one('stock.delivery.planner', 'Plan', ondelete='cascade')
carrier_id = fields.Many2one('delivery.carrier', 'Delivery Method')
package_id = fields.Many2one('stock.quant.package', 'Package')
price = fields.Float('Shipping Price')
date_planned = fields.Datetime('Planned Date')
requested_date = fields.Datetime('Expected Delivery Date')
transit_days = fields.Integer('Transit Days')
sale_requested_date = fields.Datetime('Sale Order Delivery Date', related='plan_id.picking_id.sale_id.requested_date')
days_different = fields.Float('Days Different', compute='_compute_days_different') # use carrier calendar
selection = fields.Selection([
('', 'None'),
('selected', 'Selected'),
('deselected', 'De-selected')
])
def _plan(self):
# this is intended to be used during selecting a whole plan
for option in self:
option.package_id.write({
'carrier_id': option.carrier_id.id,
})
@api.multi
def select_plan(self):
self.ensure_one()
self.selection = 'selected'
if self.package_id:
# need to deselect other options for this package
deselected = self.plan_id.plan_option_ids.filtered(lambda o: o.package_id == self.package_id and o != self)
deselected.write({'selection': 'deselected'})
return {
'name': _('Delivery Rate Planner'),
'type': 'ir.actions.act_window',
'view_mode': 'form',
'res_model': 'stock.delivery.planner',
'res_id': self.plan_id.id,
'target': 'new',
}
else:
# Select plan for whole shipment
self.plan_id.picking_id.carrier_id = self.carrier_id
return {"type": "ir.actions.act_window_close"}
@api.depends('requested_date', 'sale_requested_date', 'carrier_id')
def _compute_days_different(self):
for option in self:
if not option.requested_date or not option.sale_requested_date:
option.days_different = 0
else:
request_date = option.requested_date.split(' ')[0]
sale_request_date = option.sale_requested_date.split(' ')[0]
if request_date == sale_request_date:
option.days_different = 0
elif request_date < sale_request_date:
option.days_different = -1 * option.carrier_id.calculate_transit_days(option.requested_date, option.sale_requested_date)
else:
option.days_different = option.carrier_id.calculate_transit_days(option.sale_requested_date, option.requested_date)

View File

@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="view_stock_delivery_planner" model="ir.ui.view">
<field name="name">view.stock.delivery.planner</field>
<field name="model">stock.delivery.planner</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form>
<field name="packages_planned" invisible="1" />
<group>
<field name="plan_option_ids" nolabel="1">
<tree decoration-info="selection == 'selected'"
decoration-muted="selection == 'deselected'"
decoration-bf="days_different == 0.0"
default_order="package_id, price"
create="false" edit="false" delete="false">
<field name="package_id" />
<field name="carrier_id" />
<field name="date_planned" invisible="1" />
<field name="requested_date" decoration-danger="days_different &gt; 1.0" decoration-warning="days_different &gt; 0.0" decoration-success="days_different &lt; 0.0" />
<field name="transit_days" decoration-warning="transit_days > 2.0" decoration-success="transit_days == 1.0"/>
<field name="sale_requested_date" decoration-danger="days_different &gt; 1.0" decoration-warning="days_different &gt; 0.0" decoration-success="days_different &lt; 0.0" />
<field name="days_different" decoration-danger="days_different &gt; 1.0" decoration-warning="days_different &gt; 0.0" decoration-success="days_different &lt; 0.0" />
<field name="price" decoration-success="price &lt; 10.99" decoration-warning="price > 31.00 and price &lt; 50.00" decoration-danger="price >= 50.00" />
<field name="selection" invisible="1" />
<button class="eo_highlight"
name="select_plan"
string="Select"
type="object" />
</tree>
</field>
</group>
<footer>
<button type="object" name="action_plan" string="Plan" class="btn-primary" attrs="{'invisible': [('packages_planned', '=', False)]}"/>
<button string="Discard" special="cancel" class="btn-secondary"/>
</footer>
</form>
</field>
</record>
</odoo>