diff --git a/delivery_fedex_hibou/models/delivery_fedex.py b/delivery_fedex_hibou/models/delivery_fedex.py
index 521bea81..9e05dff6 100644
--- a/delivery_fedex_hibou/models/delivery_fedex.py
+++ b/delivery_fedex_hibou/models/delivery_fedex.py
@@ -38,6 +38,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):
@@ -242,7 +244,7 @@ class DeliveryFedex(models.Model):
recipient = superself.get_recipient(picking=picking)
acc_number = superself._get_fedex_account_number(picking=picking)
meter_number = superself._get_fedex_meter_number(picking=picking)
- payment_acc_number = superself._get_fedex_payment_account_number()
+ 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)
residential = self._get_fedex_recipient_is_residential(recipient)
@@ -712,3 +714,27 @@ class DeliveryFedex(models.Model):
('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'])
diff --git a/delivery_hibou/models/delivery.py b/delivery_hibou/models/delivery.py
index dcfb90df..9ef9cdf9 100644
--- a/delivery_hibou/models/delivery.py
+++ b/delivery_hibou/models/delivery.py
@@ -1,4 +1,5 @@
from odoo import api, fields, models
+from odoo.tools.float_utils import float_compare
from odoo.addons.stock.models.stock_move import PROCUREMENT_PRIORITIES
from odoo.exceptions import UserError
@@ -273,36 +274,46 @@ class DeliveryCarrier(models.Model):
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_declared_value = fields.Float(string='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.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.model
+ def default_get(self, fields_list):
+ defaults = super().default_get(fields_list)
+ if 'package_declared_value' in fields_list:
+ if self.env.context.get('default_picking_id'):
+ picking_id = self.env.context.get('default_picking_id')
+ package_id = self.env.context.get('default_stock_quant_package_id')
+ picking = self.env['stock.picking'].browse(picking_id)
+ move_line_ids = picking.move_line_ids.filtered(lambda m:
+ float_compare(m.qty_done, 0.0, precision_rounding=m.product_uom_id.rounding) > 0
+ and (not m.result_package_id or m.result_package_id.id == package_id)
+ )
+ total_value = 0.0
+ for ml in move_line_ids:
+ qty = ml.product_uom_id._compute_quantity(ml.qty_done, ml.product_id.uom_id)
+ total_value += qty * ml.product_id.standard_price
+ defaults['package_declared_value'] = total_value
+ elif 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'])
+ defaults['package_declared_value'] = stock_quant_package.declared_value
+ return defaults
@api.onchange('package_declared_value')
def _onchange_package_declared_value(self):
- picking = self.env['stock.picking'].browse(self.env.context['active_id'])
- 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'
+ picking_id = self.env.context.get('default_picking_id')
+ if picking_id:
+ picking = self.env['stock.picking'].browse(picking_id)
+ 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()
diff --git a/delivery_hibou/models/stock.py b/delivery_hibou/models/stock.py
index 1a620b32..3d5dfeb0 100644
--- a/delivery_hibou/models/stock.py
+++ b/delivery_hibou/models/stock.py
@@ -15,10 +15,8 @@ class StockQuantPackage(models.Model):
picking_id = self._context.get('active_id')
picking_model = self._context.get('active_model')
if not picking_id or picking_model != 'stock.picking':
- params = self._context.get('params')
- if params:
- picking_id = params.get('id')
- picking_model = params.get('model')
+ 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)
diff --git a/delivery_hibou/views/stock_views.xml b/delivery_hibou/views/stock_views.xml
index a1a53b30..8cbf343c 100644
--- a/delivery_hibou/views/stock_views.xml
+++ b/delivery_hibou/views/stock_views.xml
@@ -39,12 +39,12 @@
-
+
-
+
diff --git a/delivery_ups_hibou/models/ups_request_patch.py b/delivery_ups_hibou/models/ups_request_patch.py
index 86cbf337..5c44b90e 100644
--- a/delivery_ups_hibou/models/ups_request_patch.py
+++ b/delivery_ups_hibou/models/ups_request_patch.py
@@ -1,11 +1,41 @@
# 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, Package
+
+SUDS_VERSION = suds.__version__
+
import logging
_logger = logging.getLogger(__name__)
-SUDS_VERSION = suds.__version__
+# 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,
@@ -13,10 +43,10 @@ def patched_get_shipping_price(self, shipment_info, packages, shipper, ship_from
client = self._set_client(self.rate_wsdl, 'Rate', 'RateRequest')
request = client.factory.create('ns0:RequestType')
+
+ request.RequestOption = 'Rate'
if multi:
request.RequestOption = 'Shop'
- else:
- request.RequestOption = 'Rate'
classification = client.factory.create('ns2:CodeDescriptionType')
classification.Code = '00' # Get rates for the shipper account
@@ -67,11 +97,10 @@ def patched_get_shipping_price(self, shipment_info, packages, shipper, ship_from
if not ship_to.commercial_partner_id.is_company:
shipment.ShipTo.Address.ResidentialAddressIndicator = suds.null()
- if not multi:
- shipment.Service.Code = service_type or ''
- shipment.Service.Description = 'Service Code'
- if service_type == "96":
- shipment.NumOfPieces = int(shipment_info.get('total_qty'))
+ shipment.Service.Code = service_type or ''
+ shipment.Service.Description = 'Service Code'
+ if service_type == "96":
+ shipment.NumOfPieces = int(shipment_info.get('total_qty'))
if saturday_delivery:
shipment.ShipmentServiceOptions.SaturdayDeliveryIndicator = saturday_delivery
@@ -86,13 +115,65 @@ def patched_get_shipping_price(self, shipment_info, packages, shipper, ship_from
# Check if ProcessRate is not success then return reason for that
if response.Response.ResponseStatus.Code != "1":
- error_message = self.get_error_message(response.Response.ResponseStatus.Code,
- response.Response.ResponseStatus.Description)
- if multi:
- return [error_message]
- return error_message
+ return self.get_error_message(response.Response.ResponseStatus.Code,
+ response.Response.ResponseStatus.Description)
- if not multi:
+ 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
@@ -101,30 +182,16 @@ 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)
- # End
- else:
- result = []
- for rated_shipment in response.RatedShipment:
- rate = {}
- rate['currency_code'] = rated_shipment.TotalCharges.CurrencyCode
- # Some users are qualified to receive negotiated rates
- negotiated_rate = 'NegotiatedRateCharges' in rated_shipment and response.RatedShipment[
- 0].NegotiatedRateCharges.TotalCharge.MonetaryValue or None
+ 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')
- rate['price'] = negotiated_rate or rated_shipment.TotalCharges.MonetaryValue
+ # End
- # Hibou Delivery
- if hasattr(rated_shipment, 'GuaranteedDelivery') and hasattr(
- rated_shipment.GuaranteedDelivery, 'BusinessDaysInTransit'):
- rate['transit_days'] = int(rated_shipment.GuaranteedDelivery.BusinessDaysInTransit)
- # End
- rate['service_code'] = rated_shipment.Service.Code
- result.append(rate)
return result
except suds.WebFault as e:
@@ -132,17 +199,11 @@ def patched_get_shipping_price(self, shipment_info, packages, shipper, ship_from
prefix = ''
if SUDS_VERSION >= "0.6":
prefix = '/Envelope/Body/Fault'
- error_message = self.get_error_message(
+ 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())
- if multi:
- return [error_message]
- return error_message
except IOError as e:
- error_message = self.get_error_message('0', 'UPS Server Not Found:\n%s' % e)
- if multi:
- return [error_message]
- return error_message
+ 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):
@@ -306,6 +367,7 @@ def patched_set_package_detail(self, client, packages, packaging_type, namespace
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
@@ -313,16 +375,14 @@ 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 = carrier._ups_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.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
diff --git a/sale_planner/wizard/order_planner.py b/sale_planner/wizard/order_planner.py
index 28e1a73a..d4370497 100644
--- a/sale_planner/wizard/order_planner.py
+++ b/sale_planner/wizard/order_planner.py
@@ -29,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) ->
@@ -95,7 +102,7 @@ class FakePartner():
return getattr(self, item)
-class FakeOrderLine():
+class FakeOrderLine(FakeCollection):
def __init__(self, **kwargs):
"""
'delivery.carrier'.get_price_available(order) ->
@@ -324,18 +331,20 @@ 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'))
+ if domain:
+ return Carrier.search(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)
+ # 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):
flag_force_closest = False
@@ -691,7 +700,7 @@ class SaleOrderMakePlan(models.TransientModel):
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))
+ 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