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

View File

@@ -1,9 +1,9 @@
{
'name': 'Hibou Fedex Shipping',
'version': '11.0.1.0.0',
'version': '11.0.1.1.0',
'category': 'Stock',
'author': "Hibou Corp.",
'license': 'AGPL-3',
'license': 'OPL-1',
'website': 'https://hibou.io/',
'depends': [
'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 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 time
from odoo import fields, models, tools, _
@@ -17,6 +19,10 @@ class DeliveryFedex(models.Model):
('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):
third_party_account = self.get_third_party_account(order=order, picking=picking)
if third_party_account:
@@ -36,6 +42,8 @@ class DeliveryFedex(models.Model):
if not third_party_account.delivery_type == 'fedex':
raise ValidationError('Non-FedEx Shipping Account indicated during FedEx shipment.')
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
def _get_fedex_account_number(self, order=None, picking=None):
@@ -112,6 +120,8 @@ class DeliveryFedex(models.Model):
date_planned = None
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
@@ -205,6 +215,15 @@ class DeliveryFedex(models.Model):
srm = FedexRequest(self.log_xml, request_type="shipping", prod_environment=self.prod_environment)
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_warehouse = superself.get_shipper_warehouse(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)
order_name = superself.get_order_name(picking=picking)
attn = superself.get_attn(picking=picking)
insurance_value = superself.get_insurance_value(picking=picking)
residential = self._get_fedex_recipient_is_residential(recipient)
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.
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.set_currency(_convert_curr_iso_fdx(picking.company_id.currency_id.name))
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.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
@@ -280,13 +298,24 @@ class DeliveryFedex(models.Model):
package_labels = []
carrier_tracking_ref = ""
for sequence, package in enumerate(picking.package_ids, start=1):
package_weight = _convert_weight(package.shipping_weight, self.fedex_weight_unit)
for sequence, package in enumerate(picking_packages, start=1):
package_weight = self._fedex_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
# 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)
request = srm.process_shipment()
package_name = package.name or sequence
@@ -350,9 +379,18 @@ class DeliveryFedex(models.Model):
# One package #
###############
elif package_count == 1:
# Hibou Delivery
# Add more details to package.
srm.add_package(net_weight, ref=order_name, insurance=insurance_value)
packaging = picking_packages[:1].packaging_id or self.fedex_default_packaging_id
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,
# 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)
# Ask the shipping to fedex
@@ -394,3 +432,265 @@ class DeliveryFedex(models.Model):
raise UserError(_('No packages for this picking'))
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
from odoo.addons.delivery_fedex.models import fedex_request
from datetime import datetime
from copy import deepcopy
from pprint import pformat
import logging
_logger = logging.getLogger(__name__)
# logging.getLogger('suds.client').setLevel(logging.DEBUG)
STATECODE_REQUIRED_COUNTRIES = fedex_request.STATECODE_REQUIRED_COUNTRIES
@@ -31,6 +35,7 @@ class FedexRequest(fedex_request.FedexRequest):
_service_transit_days = {
'FEDEX_2_DAY': 2,
'FEDEX_2_DAY_AM': 2,
'FEDEX_3_DAY_FREIGHT': 3,
'FIRST_OVERNIGHT': 1,
'PRIORITY_OVERNIGHT': 1,
'STANDARD_OVERNIGHT': 1,
@@ -76,7 +81,7 @@ class FedexRequest(fedex_request.FedexRequest):
self.RequestedShipment.Recipient.Contact = Contact
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.
:param weight_value: default
@@ -84,6 +89,7 @@ class FedexRequest(fedex_request.FedexRequest):
:param mode: default
:param ref: NEW add CUSTOMER_REFERENCE object
:param insurance: NEW add Insurance amount
:param signature_required: NEW add signature required
:return:
"""
package = self.client.factory.create('RequestedPackageLineItem')
@@ -104,6 +110,12 @@ class FedexRequest(fedex_request.FedexRequest):
insured.Currency = 'USD'
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.Weight = package_weight
if mode == 'rating':
@@ -130,20 +142,44 @@ class FedexRequest(fedex_request.FedexRequest):
Payor.ResponsibleParty.AccountNumber = shipping_charges_payment_account
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
def rate(self, date_planned=None):
def rate(self, date_planned=None, multi=False):
"""
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.
:return:
"""
if multi:
multi_result = []
if date_planned:
self.RequestedShipment.ShipTimestamp = date_planned
formatted_response = {'price': {}}
del self.ClientDetail.Region
if date_planned:
# 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')
if self.hasCommodities:
self.RequestedShipment.CustomsClearanceDetail.Commodities = self.listCommodities
try:
self.response = self.client.service.getRates(WebAuthenticationDetail=self.WebAuthenticationDetail,
@@ -152,7 +188,12 @@ class FedexRequest(fedex_request.FedexRequest):
Version=self.VersionId,
RequestedShipment=self.RequestedShipment,
ReturnTransitAndCommit=True) # New ReturnTransitAndCommit for CommitDetails in response
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:
formatted_response['price'][rating.ShipmentRateDetail.TotalNetFedExCharge.Currency] = rating.ShipmentRateDetail.TotalNetFedExCharge.Amount
if len(self.response.RateReplyDetails[0].RatedShipmentDetails) == 1:
@@ -162,13 +203,42 @@ class FedexRequest(fedex_request.FedexRequest):
# Hibou Delivery Planning
if hasattr(self.response.RateReplyDetails[0], 'DeliveryTimestamp') and self.response.RateReplyDetails[0].DeliveryTimestamp:
formatted_response['date_delivered'] = self.response.RateReplyDetails[0].DeliveryTimestamp
elif 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[0], 'TransitTime'):
if 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
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:
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
except IOError:
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

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

View File

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

View File

@@ -1,10 +1,10 @@
{
'name': 'Golden State Overnight (gso.com) Shipping',
'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.",
'category': 'Warehouse',
'license': 'AGPL-3',
'license': 'OPL-1',
'images': [],
'website': "https://hibou.io",
'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

View File

@@ -1,3 +1,5 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
import pytz
from math import ceil
from requests import HTTPError
@@ -160,7 +162,7 @@ class ProviderGSO(models.Model):
company = self.get_shipper_company(picking=picking)
from_ = self.get_shipper_warehouse(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 = {
'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_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
labels = {
'thermal': [],
'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
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'].update(self._gso_get_package_dimensions(package))
request_body['Shipment']['ShipmentReference'] = package.name
@@ -207,10 +221,12 @@ class ProviderGSO(models.Model):
cost += response['ShipmentCharges']['TotalCharge']
except HTTPError as 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'].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)
try:
response = service.post_shipment(request_body)
@@ -224,6 +240,8 @@ class ProviderGSO(models.Model):
cost += response['ShipmentCharges']['TotalCharge']
except HTTPError as e:
raise ValidationError(e)
else:
continue
# Handle results
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(','):
request_body['TrackingNumber'] = tracking
_ = service.delete_shipment(request_body)
cancel_res = service.delete_shipment(request_body)
except HTTPError as e:
raise ValidationError(e)
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()
from_ = sudoself.get_shipper_warehouse(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(
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:
res.append('https://www.gso.com/Tracking')
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
from json import dumps

View File

@@ -1,7 +1,7 @@
{
'name': 'Delivery Hibou',
'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.",
'category': 'Stock',
'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.exceptions import UserError
class DeliveryCarrier(models.Model):
@@ -8,6 +9,9 @@ class DeliveryCarrier(models.Model):
automatic_insurance_value = fields.Float(string='Automatic Insurance Value',
help='Will be used during shipping to determine if the '
'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,
string='Procurement Priority',
help='Priority for this carrier. Will affect pickings '
@@ -15,21 +19,42 @@ class DeliveryCarrier(models.Model):
# Utility
def get_insurance_value(self, order=None, picking=None):
def get_insurance_value(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.type != 'service').mapped('price_subtotal'))
value = sum(order.order_line.filtered(lambda l: l.product_id.type != 'service').mapped('price_subtotal'))
else:
return value
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':
value = 0.0
elif picking.require_insurance == 'auto' and self.automatic_insurance_value and self.automatic_insurance_value > value:
value = 0.0
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):
if order and order.shipping_account_id:
return order.shipping_account_id
@@ -157,3 +182,133 @@ class DeliveryCarrier(models.Model):
def _get_recipient_out(self, picking):
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):
@@ -11,6 +40,28 @@ class StockPicking(models.Model):
('no', 'No'),
], string='Require Insurance', default='auto',
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.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)
if so and so.shipping_account_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)
return res
def declared_value(self):
def declared_value(self, package=None):
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])
if not cost:
# Assume Full Value
cost = sum([(l.product_id.standard_price * l.product_uom_qty) for l in self.move_lines] or [0.0])
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):

View File

@@ -24,12 +24,14 @@ class TestDeliveryHibou(common.TransactionCase):
def test_delivery_hibou(self):
# 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
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_sig_req_value = test_sig_req_value
self.carrier.procurement_priority = test_procurement_priority
@@ -71,7 +73,9 @@ class TestDeliveryHibou(common.TransactionCase):
def test_carrier_hibou_out(self):
test_insurance_value = 4000
test_sig_req_value = 4000
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')
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.
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.assertTrue(picking_out.carrier_id.get_signature_required(picking=picking_out))
# Workflow where user explicitly opts out of insurance on the picking level.
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.assertFalse(picking_out.carrier_id.get_signature_required(picking=picking_out))
picking_out.require_insurance = 'auto'
picking_out.require_signature = 'auto'
# Lets choose to only delivery one piece at the moment.
# This does not meet the minimum on the carrier to have insurance value.
picking_out.move_line_ids.qty_done = 1.0
self.assertEqual(picking_out.declared_value(), 3300.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.
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.assertTrue(picking_out.carrier_id.get_signature_required(picking=picking_out))
picking_out.require_insurance = 'auto'
picking_out.require_signature = 'auto'
# Test with picking having 3rd party account.
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">
<xpath expr="//field[@name='integration_level']" position="after">
<field name="automatic_insurance_value"/>
<field name="automatic_sig_req_value"/>
<field name="procurement_priority"/>
</xpath>
</field>
</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>

View File

@@ -1,14 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<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">
<field name="name">hibou.delivery.stock.picking_withcarrier.form.view</field>
<field name="model">stock.picking</field>
<field name="inherit_id" ref="delivery.view_picking_withcarrier_out_form" />
<field name="priority" eval="200" />
<field name="arch" type="xml">
<xpath expr="//field[@name='carrier_id']" position="before">
<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>
</field>
</record>

View File

@@ -1 +1,3 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
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',
'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.",
'category': 'Warehouse',
'license': 'AGPL-3',
'license': 'OPL-1',
'images': [],
'website': "https://hibou.io",
'description': """
@@ -15,7 +17,6 @@ Send your shippings through Stamps.com and track them online.
""",
'depends': [
'delivery',
'delivery_hibou',
],
'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

View File

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

View File

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

View File

@@ -145,12 +145,12 @@ class StampsService(BaseService):
def create_add_on(self):
"""Create a new add-on object.
"""
return self.create("AddOnV15")
return self.create("AddOnV17")
def create_customs(self):
"""Create a new customs object.
"""
return self.create("CustomsV3")
return self.create("CustomsV7")
def create_array_of_customs_lines(self):
"""Create a new array of customs objects.
@@ -188,7 +188,7 @@ class StampsService(BaseService):
def create_shipping(self):
"""Create a new shipping object.
"""
return self.create("RateV31")
return self.create("RateV40")
def get_address(self, address):
"""Get a shipping address.
@@ -202,7 +202,7 @@ class StampsService(BaseService):
"""
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):
"""Get a shipping label.
@@ -215,7 +215,7 @@ class StampsService(BaseService):
:param sample: Default ``False``. Get a sample label without postage.
"""
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)
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 logging import getLogger
from urllib.request import urlopen
@@ -31,6 +34,13 @@ STAMPS_PACKAGE_TYPES = [
'Regional Rate Box C',
]
STAMPS_CONTENT_TYPES = {
'Letter': 'Document',
'Postcard': 'Document',
}
STAMPS_INTEGRATION_ID = 'f62cb4f0-aa07-4701-a1dd-f0e7c853ed3c'
class ProductPackaging(models.Model):
_inherit = 'product.packaging'
@@ -44,12 +54,14 @@ class ProviderStamps(models.Model):
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_password = fields.Char(string='Stamps.com Password', groups='base.group_system')
stamps_service_type = fields.Selection([('US-FC', 'First-Class'),
('US-FCI', 'First-Class International'),
('US-PM', 'Priority'),
('US-PMI', 'Priority Mail International'),
('US-EMI', ' Priority Mail Express International'),
],
required=True, string="Service Type", default="US-PM")
stamps_default_packaging_id = fields.Many2one('product.packaging', string='Default Package Type')
@@ -67,12 +79,21 @@ class ProviderStamps(models.Model):
('BZpl', 'BZPL'),
],
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):
if not package:
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'
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):
if not package:
return self.stamps_default_packaging_id.stamps_cubic_pricing
@@ -87,15 +108,21 @@ class ProviderStamps(models.Model):
def _get_stamps_service(self):
sudoself = self.sudo()
config = StampsConfiguration(integration_id=sudoself.stamps_integration_id,
config = StampsConfiguration(integration_id=STAMPS_INTEGRATION_ID,
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)
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:
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
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))
ret_val = service.create_shipping()
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]
ret_val.ToZIPCode = order.partner_shipping_id.zip.split('-')[0]
ret_val.ShipDate = date_planned.split(' ')[0] if date_planned else date.today().isoformat()
shipper_partner = self.get_shipper_warehouse(order=order)
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.ServiceType = self.stamps_service_type
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'
return ret_val
@@ -123,20 +175,51 @@ class ProviderStamps(models.Model):
to = self.get_recipient(picking=picking)
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):
ret = []
company, from_partner, to_partner = self._stamps_get_addresses_for_picking(picking)
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)
l, w, h = self._stamps_package_dimensions(package=package)
ret_val = service.create_shipping()
ret_val.ShipDate = date.today().isoformat()
ret_val.FromZIPCode = from_partner.zip.split('-')[0]
ret_val.ToZIPCode = to_partner.zip.split('-')[0]
ret_val.From = self._stamps_address(service, from_partner)
ret_val.To = self._stamps_address(service, to_partner)
ret_val.PackageType = self._stamps_package_type(package=package)
ret_val.CubicPricing = self._stamps_package_is_cubic_pricing(package=package)
ret_val.Length = l
@@ -144,16 +227,16 @@ class ProviderStamps(models.Model):
ret_val.Height = h
ret_val.ServiceType = self.stamps_service_type
ret_val.WeightLb = weight
ret_val.ContentType = 'Merchandise'
ret.append((package.name + ret_val.ShipDate + str(ret_val.WeightLb), ret_val))
if not ret:
ret_val.ContentType = self._stamps_content_type(package=package)
ret.append((package.name + ret_val.ShipDate + str(ret_val.WeightLb) + self._stamps_hash_partner(to_partner), ret_val))
if not ret and not package_carriers:
weight = self._stamps_convert_weight(picking.shipping_weight)
l, w, h = self._stamps_package_dimensions()
ret_val = service.create_shipping()
ret_val.ShipDate = date.today().isoformat()
ret_val.FromZIPCode = from_partner.zip.split('-')[0]
ret_val.ToZIPCode = to_partner.zip.split('-')[0]
ret_val.From = self._stamps_address(service, from_partner)
ret_val.To = self._stamps_address(service, to_partner)
ret_val.PackageType = self._stamps_package_type()
ret_val.CubicPricing = self._stamps_package_is_cubic_pricing()
ret_val.Length = l
@@ -161,8 +244,8 @@ class ProviderStamps(models.Model):
ret_val.Height = h
ret_val.ServiceType = self.stamps_service_type
ret_val.WeightLb = weight
ret_val.ContentType = 'Merchandise'
ret.append((picking.name + ret_val.ShipDate + str(ret_val.WeightLb), ret_val))
ret_val.ContentType = self._stamps_content_type()
ret.append((picking.name + ret_val.ShipDate + str(ret_val.WeightLb) + self._stamps_hash_partner(to_partner), ret_val))
return ret
@@ -224,6 +307,9 @@ class ProviderStamps(models.Model):
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):
res = []
service = self._get_stamps_service()
@@ -232,33 +318,13 @@ class ProviderStamps(models.Model):
package_labels = []
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)
from_address = service.create_address()
from_address.FullName = company.name
from_address.Address1 = from_partner.street
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
customs = None
if self._stamps_needs_customs(from_partner, to_partner):
customs = service.create_customs()
try:
for txn_id, shipping in shippings:
@@ -270,20 +336,78 @@ class ProviderStamps(models.Model):
shipping.DeliverDays = rate.DeliverDays
if hasattr(rate, 'DimWeighting'):
shipping.DimWeighting = rate.DimWeighting
shipping.Zone = rate.Zone
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.AddOnType = 'US-A-DC'
add_on2 = service.create_add_on()
add_on2.AddOnType = 'SC-A-HP'
shipping.AddOns.AddOnV15 = [add_on, add_on2]
addons.append(add_on)
if self.stamps_addon_hp:
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()
if self.is_amazon(picking=picking):
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,
extended_postage_info=extended_postage_info)
extended_postage_info=extended_postage_info,
customs=customs)
package_labels.append((txn_id, label))
except WebFault as e:
_logger.warn(e)
@@ -307,9 +431,13 @@ class ProviderStamps(models.Model):
carrier_price += float(label.Rate.Amount)
url = label.URL
url_spaces = url.split(' ')
attachments = []
for i, url in enumerate(url_spaces, 1):
response = urlopen(url)
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)}
res = res + [shipping_data]
return res
@@ -324,9 +452,81 @@ class ProviderStamps(models.Model):
def stamps_cancel_shipment(self, picking):
service = self._get_stamps_service()
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.write({'carrier_tracking_ref': '',
'carrier_price': 0.0})
except WebFault as 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')]}">
<group>
<group>
<field name="stamps_integration_id" 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"/>
</group>
@@ -18,6 +17,9 @@
<field name="stamps_service_type" 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_addon_sc"/>
<field name="stamps_addon_dc"/>
<field name="stamps_addon_hp"/>
</group>
</group>
</page>
@@ -30,7 +32,7 @@
<field name="model">product.packaging</field>
<field name="inherit_id" ref="delivery.product_packaging_delivery_form"/>
<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"/>
</xpath>
</field>

View File

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

View File

@@ -1,15 +1,16 @@
{
'name': 'Hibou UPS Shipping',
'version': '11.0.1.0.0',
'version': '11.0.1.2.0',
'category': 'Stock',
'author': "Hibou Corp.",
'license': 'AGPL-3',
'license': 'OPL-1',
'website': 'https://hibou.io/',
'depends': [
'delivery_ups',
'delivery_hibou',
],
'data': [
'views/stock_views.xml',
],
'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 stock
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.exceptions import UserError, ValidationError
from odoo.addons.delivery_ups.models.ups_request import UPSRequest, Package
from odoo.tools import pdf
import logging
_logger = logging.getLogger(__name__)
class ProviderUPS(models.Model):
_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):
third_party_account = self.get_third_party_account(order=order, picking=picking)
if third_party_account:
@@ -17,6 +31,16 @@ class ProviderUPS(models.Model):
return True
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):
"""
Common hook to customize what UPS Account number to use.
@@ -30,6 +54,8 @@ class ProviderUPS(models.Model):
return third_party_account.name
if order and 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:
return picking.sale_id.ups_carrier_account
return self.ups_shipper_number
@@ -37,14 +63,20 @@ class ProviderUPS(models.Model):
def _get_ups_carrier_account(self, picking):
# 3rd party billing should return False if not used.
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.
"""
def ups_rate_shipment(self, order):
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']
max_weight = self.ups_default_packaging_id.max_weight
packages = []
@@ -59,11 +91,14 @@ class ProviderUPS(models.Model):
last_package_weight = total_weight % max_weight
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:
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:
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 = {
'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):
res = []
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']
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
shipper_company = superself.get_shipper_company(picking=picking)
shipper_warehouse = superself.get_shipper_warehouse(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 = []
package_names = []
if picking.package_ids:
if picking_packages:
# Create all packages
for package in picking.package_ids:
packages.append(Package(self, package.shipping_weight, quant_pack=package.packaging_id, name=package.name))
for package in picking_packages:
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)
# what is the point of weight_bulk?
# Create one package with the rest (the content that is not in a package)
if picking.weight_bulk:
packages.append(Package(self, picking.weight_bulk))
# if picking.weight_bulk:
# packages.append(Package(self, picking.weight_bulk))
invoice_line_total = 0
for move in picking.move_lines:
@@ -177,7 +226,7 @@ class ProviderUPS(models.Model):
if 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(
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,
@@ -216,3 +265,149 @@ class ProviderUPS(models.Model):
'tracking_number': carrier_tracking_ref}
res = res + [shipping_data]
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
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__
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,
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')
request = client.factory.create('ns0:RequestType')
request.RequestOption = 'Rate'
if multi:
request.RequestOption = 'Shop'
classification = client.factory.create('ns2:CodeDescriptionType')
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:
shipment.ShipmentServiceOptions = ''
shipment.ShipmentRatingOptions.NegotiatedRatesIndicator = 1
shipment.ShipmentRatingOptions.NegotiatedRatesIndicator = '1'
try:
# 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,
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['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
result['price'] = negotiated_rate or response.RatedShipment[0].TotalCharges.MonetaryValue
# Hibou Delivery
if hasattr(response.RatedShipment[0], 'GuaranteedDelivery') and hasattr(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
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)
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.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 models

View File

@@ -1,10 +1,10 @@
{
'name': 'Sale Order Planner',
'summary': 'Plans order dates and warehouses.',
'version': '11.0.1.0.0',
'version': '11.0.2.0.0',
'author': "Hibou Corp.",
'category': 'Sale',
'license': 'AGPL-3',
'license': 'OPL-1',
'complexity': 'expert',
'images': [],
'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/delivery.xml',
'views/product.xml',
'views/res_config_settings_views.xml',
],
'auto_install': False,
'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 stock
from . import delivery
from . import product
from . import planning
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 odoo import api, fields, models
@@ -61,6 +63,9 @@ class DeliveryCarrier(models.Model):
def calculate_transit_days(self, date_planned, date_delivered):
self.ensure_one()
if not self.delivery_calendar_id:
return 0
if isinstance(date_planned, str):
date_planned = fields.Datetime.from_string(date_planned)
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
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

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

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

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
@@ -7,3 +9,7 @@ class Warehouse(models.Model):
shipping_calendar_id = fields.Many2one(
'resource.calendar', 'Shipping Calendar',
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

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 datetime import datetime, timedelta
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">
<xpath expr="//field[@name='partner_id']" position="after">
<field name="shipping_calendar_id" />
<field name="sale_planner_carrier_ids" options="{'no_create_edit': True, 'no_create': True}" />
</xpath>
</field>
</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

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 json import dumps, loads
from copy import deepcopy
@@ -15,6 +17,7 @@ except ImportError:
from odoo import api, fields, models, tools
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():
@@ -26,10 +29,17 @@ class FakeCollection():
yield v
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):
"""
'delivery.carrier'.verify_carrier(contact) ->
@@ -92,7 +102,7 @@ class FakePartner():
return getattr(self, item)
class FakeOrderLine():
class FakeOrderLine(FakeCollection):
def __init__(self, **kwargs):
"""
'delivery.carrier'.get_price_available(order) ->
@@ -130,7 +140,7 @@ class FakeOrderLine():
return getattr(self, item)
class FakeSaleOrder():
class FakeSaleOrder(FakeCollection):
"""
partner_id :: used in shipping
partner_shipping_id :: is used in several places
@@ -294,19 +304,18 @@ class SaleOrderMakePlan(models.TransientModel):
if domain:
if not isinstance(domain, (list, tuple)):
domain = tools.safe_eval(domain)
else:
domain = []
if self.env.context.get('warehouse_domain'):
if not domain:
domain = []
domain.extend(self.env.context.get('warehouse_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')))
if 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()
if carrier_id:
return Carrier.browse(carrier_id)
@@ -314,19 +323,21 @@ class SaleOrderMakePlan(models.TransientModel):
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
if not domain:
domain = []
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')))
if 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):
policy = 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)
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}
wh_id = distances[min(distances)]
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()
if policy and policy.carrier_filter_id:
domain.extend(tools.safe_eval(policy.carrier_filter_id.domain))
carriers = self.get_shipping_carriers(base_option.get('carrier_id'), domain=domain)
_logger.info('generate_shipping_options:: base_optoin: ' + str(base_option) + ' order_fake: ' + str(order_fake) + ' carriers: ' + str(carriers))
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_option: ' + str(base_option) + ' order_fake: ' + str(order_fake) + ' carriers: ' + str(carriers))
if not carriers:
return base_option
return [base_option]
if not base_option.get('sub_options'):
options = []
# this locic comes from "delivery.models.sale_order.SaleOrder"
for carrier in carriers:
option = self._generate_shipping_carrier_option(base_option, order_fake, carrier)
if option:
options.append(option)
carrier_options = self._generate_shipping_carrier_option(base_option, order_fake, carrier)
if carrier_options:
options += carrier_options
if options:
return options
return [base_option]
@@ -683,26 +696,40 @@ class SaleOrderMakePlan(models.TransientModel):
for carrier in carriers:
new_base_option = deepcopy(base_option)
has_error = False
found_carrier_ids = set()
for wh_id, wh_vals in base_option['sub_options'].items():
if has_error:
continue
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))
wh_option = self._generate_shipping_carrier_option(wh_vals, order_fake, carrier)
if not wh_option:
order_fake.order_line = FakeCollection(list(filter(lambda line: line.product_id.id in wh_vals['product_ids'], original_order_fake_order_line)))
wh_carrier_options = self._generate_shipping_carrier_option(wh_vals, order_fake, carrier)
if not wh_carrier_options:
has_error = True
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:
continue
# now that we've collected, we can roll up some details.
new_base_option['carrier_id'] = carrier.id
new_base_option['shipping_price'] = self._get_shipping_price_for_options(new_base_option['sub_options'])
new_base_option['requested_date'] = self._get_max_requested_date(new_base_option['sub_options'])
new_base_option['transit_days'] = self._get_max_transit_days(new_base_option['sub_options'])
options.append(new_base_option)
# now that we've collected details for this carrier, we likely have more than one carrier's rates
_logger.info(' from ' + str(carrier) + ' found ' + str(found_carrier_ids))
for carrier_id in found_carrier_ids:
carrier_option = deepcopy(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
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):
# some carriers look at the order carrier_id
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"
try:
@@ -742,7 +771,9 @@ class SaleOrderMakePlan(models.TransientModel):
date_delivered = None
transit_days = 0
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
result = carrier.rate_shipment_date_planned(order_fake, base_option.get('date_planned'))
if result:
@@ -752,7 +783,8 @@ class SaleOrderMakePlan(models.TransientModel):
elif hasattr(carrier, 'get_shipping_price_for_plan'):
# Old API
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]
elif not result:
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:
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))
option = deepcopy(base_option)
option['carrier_id'] = 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
return option
return [option]
except Exception as e:
_logger.info("Exception collecting carrier rates: " + str(e))
# Want to see more?
@@ -794,7 +851,6 @@ class SaleOrderMakePlan(models.TransientModel):
return None
class SaleOrderPlanningOption(models.TransientModel):
_name = 'sale.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>