Merge branch 'mig/12.0-test/delivery_hibou' into 12.0-test

This commit is contained in:
Jared Kipe
2019-02-18 09:12:45 -08:00
16 changed files with 620 additions and 0 deletions

View File

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

View File

@@ -0,0 +1,24 @@
{
'name': 'Delivery Hibou',
'summary': 'Adds underlying pinnings for things like "RMA Return Labels"',
'version': '11.0.1.0.0',
'author': "Hibou Corp.",
'category': 'Stock',
'license': 'AGPL-3',
'images': [],
'website': "https://hibou.io",
'description': """
This is a collection of "typical" carrier needs, and a bridge into Hibou modules like `delivery_partner` and `sale_planner`.
""",
'depends': [
'delivery',
'delivery_partner',
],
'demo': [],
'data': [
'views/delivery_views.xml',
'views/stock_views.xml',
],
'auto_install': False,
'installable': True,
}

View File

@@ -0,0 +1,2 @@
from . import delivery
from . import stock

View File

@@ -0,0 +1,152 @@
from odoo import fields, models
from odoo.addons.stock.models.stock_move import PROCUREMENT_PRIORITIES
class DeliveryCarrier(models.Model):
_inherit = 'delivery.carrier'
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.')
procurement_priority = fields.Selection(PROCUREMENT_PRIORITIES,
string='Procurement Priority',
help='Priority for this carrier. Will affect pickings '
'and procurements related to this carrier.')
def get_insurance_value(self, order=None, picking=None):
value = 0.0
if order:
if order.order_line:
value = sum(order.order_line.filtered(lambda l: l.type != 'service').mapped('price_subtotal'))
else:
return value
if picking:
value = picking.declared_value()
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_third_party_account(self, order=None, picking=None):
if order and order.shipping_account_id:
return order.shipping_account_id
if picking and picking.shipping_account_id:
return picking.shipping_account_id
return None
def get_order_name(self, order=None, picking=None):
if order:
return order.name
if picking:
if picking.sale_id:
return picking.sale_id.name # + ' - ' + picking.name
return picking.name
return ''
def get_attn(self, order=None, picking=None):
if order:
return order.client_order_ref
if picking and picking.sale_id:
return picking.sale_id.client_order_ref
# If Picking has a reference, decide what it is.
return False
def _classify_picking(self, picking):
if picking.picking_type_id.code == 'incoming' and picking.location_id.usage == 'supplier' and picking.location_dest_id.usage == 'customer':
return 'dropship'
elif picking.picking_type_id.code == 'incoming' and picking.location_id.usage == 'customer' and picking.location_dest_id.usage == 'supplier':
return 'dropship_in'
elif picking.picking_type_id.code == 'incoming':
return 'in'
return 'out'
# Shipper Company
def get_shipper_company(self, order=None, picking=None):
"""
Shipper Company: The `res.partner` that provides the name of where the shipment is coming from.
"""
if order:
return order.company_id.partner_id
if picking:
return getattr(self, ('_get_shipper_company_%s' % (self._classify_picking(picking),)),
self._get_shipper_company_out)(picking)
return None
def _get_shipper_company_dropship(self, picking):
return picking.company_id.partner_id
def _get_shipper_company_dropship_in(self, picking):
return picking.company_id.partner_id
def _get_shipper_company_in(self, picking):
return picking.company_id.partner_id
def _get_shipper_company_out(self, picking):
return picking.company_id.partner_id
# Shipper Warehouse
def get_shipper_warehouse(self, order=None, picking=None):
"""
Shipper Warehouse: The `res.partner` that is basically the physical address a shipment is coming from.
"""
if order:
return order.warehouse_id.partner_id
if picking:
return getattr(self, ('_get_shipper_warehouse_%s' % (self._classify_picking(picking),)),
self._get_shipper_warehouse_out)(picking)
return None
def _get_shipper_warehouse_dropship(self, picking):
return picking.partner_id
def _get_shipper_warehouse_dropship_in(self, picking):
if picking.sale_id:
picking.sale_id.partner_shipping_id
return self._get_shipper_warehouse_dropship_in_no_sale(picking)
def _get_shipper_warehouse_dropship_in_no_sale(self, picking):
return picking.company_id.partner_id
def _get_shipper_warehouse_in(self, picking):
return picking.partner_id
def _get_shipper_warehouse_out(self, picking):
return picking.picking_type_id.warehouse_id.partner_id
# Recipient
def get_recipient(self, order=None, picking=None):
"""
Recipient: The `res.partner` receiving the shipment.
"""
if order:
return order.partner_shipping_id
if picking:
return getattr(self, ('_get_recipient_%s' % (self._classify_picking(picking),)),
self._get_recipient_out)(picking)
return None
def _get_recipient_dropship(self, picking):
if picking.sale_id:
return picking.sale_id.partner_shipping_id
return picking.sale_id.partner_shipping_id
def _get_recipient_dropship_no_sale(self, picking):
return picking.company_id.partner_id
def _get_recipient_dropship_in(self, picking):
return picking.picking_type_id.warehouse_id.partner_id
def _get_recipient_in(self, picking):
return picking.picking_type_id.warehouse_id.partner_id
def _get_recipient_out(self, picking):
return picking.partner_id

View File

@@ -0,0 +1,50 @@
from odoo import api, fields, models
class StockPicking(models.Model):
_inherit = 'stock.picking'
shipping_account_id = fields.Many2one('partner.shipping.account', string='Shipping Account')
require_insurance = fields.Selection([
('auto', 'Automatic'),
('yes', 'Yes'),
('no', 'No'),
], string='Require Insurance', default='auto',
help='If your carrier supports it, auto should be calculated off of the "Automatic Insurance Value" field.')
@api.one
@api.depends('move_lines.priority', 'carrier_id')
def _compute_priority(self):
if self.carrier_id.procurement_priority:
self.priority = self.carrier_id.procurement_priority
else:
super(StockPicking, self)._compute_priority()
@api.model
def create(self, values):
origin = values.get('origin')
if origin and not values.get('shipping_account_id'):
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
res = super(StockPicking, self).create(values)
return res
def declared_value(self):
self.ensure_one()
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
class StockMove(models.Model):
_inherit = 'stock.move'
def _prepare_procurement_values(self):
res = super(StockMove, self)._prepare_procurement_values()
res['priority'] = self.picking_id.priority or self.priority
return res

View File

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

View File

@@ -0,0 +1,161 @@
from odoo.tests import common
class TestDeliveryHibou(common.TransactionCase):
def setUp(self):
super(TestDeliveryHibou, self).setUp()
self.partner = self.env.ref('base.res_partner_address_13')
self.product = self.env.ref('product.product_product_7')
# Create Shipping Account
self.shipping_account = self.env['partner.shipping.account'].create({
'name': '123123',
'delivery_type': 'other',
})
# Create Carrier
self.delivery_product = self.env['product.product'].create({
'name': 'Test Carrier1 Delivery',
'type': 'service',
})
self.carrier = self.env['delivery.carrier'].create({
'name': 'Test Carrier1',
'product_id': self.delivery_product.id,
})
def test_delivery_hibou(self):
# Assign a new shipping account
self.partner.shipping_account_id = self.shipping_account
# Assign values to new Carrier
test_insurance_value = 600
test_procurement_priority = '2'
self.carrier.automatic_insurance_value = test_insurance_value
self.carrier.procurement_priority = test_procurement_priority
sale_order = self.env['sale.order'].create({
'partner_id': self.partner.id,
'partner_shipping_id': self.partner.id,
'partner_invoice_id': self.partner.id,
'carrier_id': self.carrier.id,
'shipping_account_id': self.shipping_account.id,
'order_line': [(0, 0, {
'product_id': self.product.id,
})]
})
sale_order.get_delivery_price()
sale_order.set_delivery_line()
sale_order.action_confirm()
# Make sure 3rd party Shipping Account is set.
self.assertEqual(sale_order.shipping_account_id, self.shipping_account)
self.assertTrue(sale_order.picking_ids)
# Priority coming from Carrier procurement_priority
self.assertEqual(sale_order.picking_ids.priority, test_procurement_priority)
# 3rd party Shipping Account copied from Sale Order
self.assertEqual(sale_order.picking_ids.shipping_account_id, self.shipping_account)
self.assertEqual(sale_order.carrier_id.get_third_party_account(order=sale_order), self.shipping_account)
# Test attn
test_ref = 'TEST100'
self.assertEqual(sale_order.carrier_id.get_attn(order=sale_order), False)
sale_order.client_order_ref = test_ref
self.assertEqual(sale_order.carrier_id.get_attn(order=sale_order), test_ref)
# The picking should get this ref as well
self.assertEqual(sale_order.picking_ids.carrier_id.get_attn(picking=sale_order.picking_ids), test_ref)
# Test order_name
self.assertEqual(sale_order.carrier_id.get_order_name(order=sale_order), sale_order.name)
# The picking should get the same 'order_name'
self.assertEqual(sale_order.picking_ids.carrier_id.get_order_name(picking=sale_order.picking_ids), sale_order.name)
def test_carrier_hibou_out(self):
test_insurance_value = 4000
self.carrier.automatic_insurance_value = test_insurance_value
picking_out = self.env.ref('stock.outgoing_shipment_main_warehouse')
picking_out.action_assign()
self.assertEqual(picking_out.state, 'assigned')
picking_out.carrier_id = self.carrier
# This relies heavily on the 'stock' demo data.
# Should only have a single move_line_ids and it should not be done at all.
self.assertEqual(picking_out.move_line_ids.mapped('qty_done'), [0.0])
self.assertEqual(picking_out.move_line_ids.mapped('product_uom_qty'), [15.0])
self.assertEqual(picking_out.move_line_ids.mapped('product_id.standard_price'), [3300.0])
# 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())
# Workflow where user explicitly opts out of insurance on the picking level.
picking_out.require_insurance = 'no'
self.assertEqual(picking_out.carrier_id.get_insurance_value(picking=picking_out), 0.0)
picking_out.require_insurance = '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)
# Workflow where user opts in to insurance.
picking_out.require_insurance = 'yes'
self.assertEqual(picking_out.carrier_id.get_insurance_value(picking=picking_out), 3300.0)
picking_out.require_insurance = 'auto'
# Test with picking having 3rd party account.
self.assertEqual(picking_out.carrier_id.get_third_party_account(picking=picking_out), None)
picking_out.shipping_account_id = self.shipping_account
self.assertEqual(picking_out.carrier_id.get_third_party_account(picking=picking_out), self.shipping_account)
# Shipment Time Methods!
self.assertEqual(picking_out.carrier_id._classify_picking(picking=picking_out), 'out')
self.assertEqual(picking_out.carrier_id.get_shipper_company(picking=picking_out),
picking_out.company_id.partner_id)
self.assertEqual(picking_out.carrier_id.get_shipper_warehouse(picking=picking_out),
picking_out.picking_type_id.warehouse_id.partner_id)
self.assertEqual(picking_out.carrier_id.get_recipient(picking=picking_out),
picking_out.partner_id)
# This picking has no `sale_id`
# Right now ATTN requires a sale_id, which this picking doesn't have (none of the stock ones do)
self.assertEqual(picking_out.carrier_id.get_attn(picking=picking_out), False)
self.assertEqual(picking_out.carrier_id.get_order_name(picking=picking_out), picking_out.name)
def test_carrier_hibou_in(self):
picking_in = self.env.ref('stock.incomming_shipment1')
self.assertEqual(picking_in.state, 'assigned')
picking_in.carrier_id = self.carrier
# This relies heavily on the 'stock' demo data.
# Should only have a single move_line_ids and it should not be done at all.
self.assertEqual(picking_in.move_line_ids.mapped('qty_done'), [0.0])
self.assertEqual(picking_in.move_line_ids.mapped('product_uom_qty'), [35.0])
self.assertEqual(picking_in.move_line_ids.mapped('product_id.standard_price'), [55.0])
self.assertEqual(picking_in.carrier_id._classify_picking(picking=picking_in), 'in')
self.assertEqual(picking_in.carrier_id.get_shipper_company(picking=picking_in),
picking_in.company_id.partner_id)
self.assertEqual(picking_in.carrier_id.get_shipper_warehouse(picking=picking_in),
picking_in.partner_id)
self.assertEqual(picking_in.carrier_id.get_recipient(picking=picking_in),
picking_in.picking_type_id.warehouse_id.partner_id)

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="view_delivery_carrier_form" model="ir.ui.view">
<field name="name">hibou.delivery.carrier.form</field>
<field name="model">delivery.carrier</field>
<field name="inherit_id" ref="delivery.view_delivery_carrier_form" />
<field name="arch" type="xml">
<xpath expr="//field[@name='integration_level']" position="after">
<field name="automatic_insurance_value"/>
<field name="procurement_priority"/>
</xpath>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<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="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'))]}"/>
</xpath>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,29 @@
*********************************
Hibou - Partner Shipping Accounts
*********************************
Records shipping account numbers on partners.
For more information and add-ons, visit `Hibou.io <https://hibou.io/>`_.
=============
Main Features
=============
* New model: Customer Shipping Account
* Includes manager-level access permissions.
.. image:: https://user-images.githubusercontent.com/15882954/41176601-e40f8558-6b15-11e8-998e-6a7ee5709c0f.png
:alt: 'Register Payment Detail'
:width: 988
:align: left
=======
License
=======
Please see `LICENSE <https://github.com/hibou-io/hibou-odoo-suite/blob/11.0/LICENSE>`_.
Copyright Hibou Corp. 2018

View File

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

View File

@@ -0,0 +1,24 @@
{
'name': 'Partner Shipping Accounts',
'author': 'Hibou Corp. <hello@hibou.io>',
'version': '11.0.1.0.0',
'category': 'Stock',
'sequence': 95,
'summary': 'Record shipping account numbers on partners.',
'description': """
Record shipping account numbers on partners.
* Customer Shipping Account Model
""",
'website': 'https://hibou.io/',
'depends': [
'delivery',
'contacts',
],
'data': [
'security/ir.model.access.csv',
'views/delivery_views.xml',
],
'installable': True,
'application': False,
}

View File

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

View File

@@ -0,0 +1,48 @@
from odoo import api, fields, models
class Partner(models.Model):
_inherit = 'res.partner'
shipping_account_ids = fields.One2many('partner.shipping.account', 'partner_id', string='Shipping Accounts')
class SaleOrder(models.Model):
_inherit = 'sale.order'
shipping_account_id = fields.Many2one('partner.shipping.account', string='Shipping Account')
class PartnerShippingAccount(models.Model):
_name = 'partner.shipping.account'
name = fields.Char(string='Account Num.', required=True)
description = fields.Char(string='Description')
partner_id = fields.Many2one('res.partner', string='Partner', help='Leave blank to allow as a generic 3rd party shipper.')
delivery_type = fields.Selection([
('other', 'Other'),
], string='Carrier', required=True)
note = fields.Text(string='Internal Note')
@api.multi
def name_get(self):
delivery_types = self._fields['delivery_type']._description_selection(self.env)
def get_name(value):
name = [n for v, n in delivery_types if v == value]
return name[0] if name else 'Undefined'
res = []
for acc in self:
if acc.description:
res.append((acc.id, acc.description))
else:
res.append((acc.id, '%s: %s' % (get_name(acc.delivery_type), acc.name)))
return res
@api.constrains('name', 'delivery_type')
def _check_validity(self):
for acc in self:
check = getattr(acc, acc.delivery_type + '_check_validity', None)
if check:
return check()

View File

@@ -0,0 +1,2 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_partner_shipping_account,partner.shipping.account,model_partner_shipping_account,base.group_partner_manager,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_partner_shipping_account partner.shipping.account model_partner_shipping_account base.group_partner_manager 1 1 1 1

View File

@@ -0,0 +1,94 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="partner_shipping_account_view_tree" model="ir.ui.view">
<field name="name">partner.shipping.account.tree</field>
<field name="model">partner.shipping.account</field>
<field name="arch" type="xml">
<tree string="Shipping Accounts">
<field name="description"/>
<field name="name"/>
<field name="delivery_type"/>
<field name="partner_id"/>
</tree>
</field>
</record>
<record id="partner_shipping_account_view_form" model="ir.ui.view">
<field name="name">partner.shipping.account.form</field>
<field name="model">partner.shipping.account</field>
<field name="arch" type="xml">
<form string="Shipping Account">
<sheet>
<group>
<group>
<field name="partner_id"/>
<field name="description"/>
<field name="name"/>
<field name="delivery_type"/>
</group>
<group name="carrier"/>
</group>
<field name="note" placeholder="Any additional notes..."/>
</sheet>
</form>
</field>
</record>
<record id="partner_shipping_account_view_search" model="ir.ui.view">
<field name="name">partner.shipping.account.search</field>
<field name="model">partner.shipping.account</field>
<field name="arch" type="xml">
<search string="Shipping Account Search">
<field name="description"/>
<field name="name"/>
<field name="partner_id"/>
<field name="delivery_type"/>
</search>
</field>
</record>
<record id="partner_shipping_account_action_main" model="ir.actions.act_window">
<field name="name">Shipping Accounts</field>
<field name="res_model">partner.shipping.account</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="help" type="html">
<p>
No accounts
</p>
</field>
</record>
<menuitem id="partner_shipping_account_menu_main" name="Partner Shipping Accounts"
action="delivery_partner.partner_shipping_account_action_main"
sequence="20" parent="contacts.res_partner_menu_config"/>
<!-- Inherited -->
<record id="view_partner_property_form_inherit" model="ir.ui.view">
<field name="name">res.partner.carrier.property.form.inherit</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="delivery.view_partner_property_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='property_delivery_carrier_id']" position="after">
<field name="shipping_account_ids" context="{'default_partner_id': active_id}">
<tree>
<field name="description"/>
<field name="name"/>
<field name="delivery_type"/>
</tree>
</field>
</xpath>
</field>
</record>
<record id="view_order_form_with_carrier_inherit" model="ir.ui.view">
<field name="name">delivery.sale.order.form.view.with_carrier.inherit</field>
<field name="model">sale.order</field>
<field name="inherit_id" ref="delivery.view_order_form_with_carrier"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='id']" position="before">
<field name="shipping_account_id" options="{'no_create': True, 'no_open': True}" domain="['|', ('partner_id', '=', False), ('partner_id', '=', partner_id)]"/>
</xpath>
</field>
</record>
</odoo>