summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMahendra barad <mba@openerp.com>2015-06-19 15:26:26 +0530
committerDavid Monjoie <dmo@odoo.com>2015-09-01 08:14:16 +0200
commita223111b922d3c3d8957f894bf20a156eb96d324 (patch)
tree00589dd65e96dbc4172364c101ebdf920b5bad45
parentbb1200adc781efc1318e502a84cc1e77315aabc4 (diff)
[IMP] delivery, website_sale_delivery: refactoring and usability
* migration to new API * removed delivery.grid object, has been merged with delivery.carrier * change view of delivery.carrier (price rules, etc.) * tests from yml to py * website : restrict the list of countries for delivery address in the shop
-rw-r--r--addons/delivery/__init__.py4
-rw-r--r--addons/delivery/__openerp__.py13
-rw-r--r--addons/delivery/data/delivery_data.xml (renamed from addons/delivery/delivery_data.xml)27
-rw-r--r--addons/delivery/data/delivery_demo.xml (renamed from addons/delivery/delivery_demo.xml)44
-rw-r--r--addons/delivery/delivery.py244
-rw-r--r--addons/delivery/models/__init__.py24
-rw-r--r--addons/delivery/models/delivery_carrier.py196
-rw-r--r--addons/delivery/models/delivery_price_rule.py34
-rw-r--r--addons/delivery/models/partner.py10
-rw-r--r--addons/delivery/models/product_template.py19
-rw-r--r--addons/delivery/models/sale_order.py74
-rw-r--r--addons/delivery/models/stock_move.py39
-rw-r--r--addons/delivery/models/stock_picking.py93
-rw-r--r--addons/delivery/partner.py14
-rw-r--r--addons/delivery/sale.py79
-rw-r--r--addons/delivery/security/ir.model.access.csv9
-rw-r--r--addons/delivery/stock.py164
-rw-r--r--addons/delivery/test/delivery_cost.yml80
-rw-r--r--addons/delivery/test/stock_move_values_with_invoice_before_delivery.yml73
-rw-r--r--addons/delivery/tests/__init__.py4
-rw-r--r--addons/delivery/tests/test_delivery_cost.py125
-rw-r--r--addons/delivery/tests/test_delivery_stock_move.py75
-rw-r--r--addons/delivery/views/delivery_view.xml (renamed from addons/delivery/delivery_view.xml)223
-rw-r--r--addons/delivery/views/partner_view.xml (renamed from addons/delivery/partner_view.xml)8
-rw-r--r--addons/delivery/views/report_shipping.xml6
-rw-r--r--addons/website_sale/static/src/js/website_sale.js8
-rw-r--r--addons/website_sale_delivery/__openerp__.py1
-rw-r--r--addons/website_sale_delivery/controllers/main.py4
-rw-r--r--addons/website_sale_delivery/demo/website_sale_delivery_data.xml6
-rw-r--r--addons/website_sale_delivery/models/sale_order.py28
-rw-r--r--addons/website_sale_delivery/static/src/js/website_sale_delivery.js15
-rw-r--r--addons/website_sale_delivery/views/website_sale_delivery.xml34
-rw-r--r--addons/website_sale_delivery/views/website_sale_delivery_view.xml8
33 files changed, 776 insertions, 1009 deletions
diff --git a/addons/delivery/__init__.py b/addons/delivery/__init__.py
index 02d8c7c70af..81fc81d29c9 100644
--- a/addons/delivery/__init__.py
+++ b/addons/delivery/__init__.py
@@ -1,8 +1,4 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
-import delivery
-import partner
-import sale
-import stock
import models
diff --git a/addons/delivery/__openerp__.py b/addons/delivery/__openerp__.py
index 85823d5449d..efa69dbfdf5 100644
--- a/addons/delivery/__openerp__.py
+++ b/addons/delivery/__openerp__.py
@@ -10,25 +10,22 @@
Allows you to add delivery methods in sale orders and picking.
==============================================================
-You can define your own carrier and delivery grids for prices. When creating
+You can define your own carrier for prices. When creating
invoices from picking, the system is able to add and compute the shipping line.
""",
'depends': ['sale_stock'],
'data': [
'security/ir.model.access.csv',
- 'delivery_view.xml',
- 'partner_view.xml',
- 'delivery_data.xml',
+ 'views/delivery_view.xml',
+ 'views/partner_view.xml',
+ 'data/delivery_data.xml',
'views/report_shipping.xml',
'views/report_deliveryslip.xml'
],
- 'demo': ['delivery_demo.xml'],
+ 'demo': ['data/delivery_demo.xml'],
'test': [
'../account/test/account_minimal_test.xml',
- 'test/delivery_cost.yml',
- 'test/stock_move_values_with_invoice_before_delivery.yml',
],
'installable': True,
- 'auto_install': False,
}
diff --git a/addons/delivery/delivery_data.xml b/addons/delivery/data/delivery_data.xml
index d567a8aff86..a26475af993 100644
--- a/addons/delivery/delivery_data.xml
+++ b/addons/delivery/data/delivery_data.xml
@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
-<openerp>
+<odoo>
<data noupdate="1">
-
<!-- Create a transporter -->
<record id="res_partner_delivery" model="res.partner">
<field name="name">Transporter (Delivery provider)</field>
@@ -10,31 +9,21 @@
<field name="customer" eval="False"/>
</record>
- <!-- Create a service product -->
- <record id="product_product_delivery" model="product.product">
- <field name="name">Standard delivery</field>
- <field name="default_code">Delivery</field>
- <field name="type">service</field>
- <field name="categ_id" ref="product.product_category_all"/>
- <field name="standard_price">10.0</field>
- <field name="list_price">10.0</field>
- <field name="image" type="base64" file="delivery/static/img/product_product_delivery-image.jpg"/>
- </record>
-
<record id="free_delivery_carrier" model="delivery.carrier">
<field name="name">Free delivery charges</field>
- <field name="normal_price">0</field>
+ <field name="fixed_price">0</field>
<field name="free_if_more_than" eval="True"/>
<field name="amount">1000</field>
<field name="sequence">1</field>
+ <field name="delivery_type">fixed</field>
+ <field name="product_type">service</field>
+ <field name="product_sale_ok" eval="False"/>
<field name="partner_id" ref="res_partner_delivery"/>
- <field name="product_id" ref="product_product_delivery"/>
</record>
<record forcecreate="True" id="property_delivery_carrier" model="ir.property">
- <field name="name">property_delivery_carrier</field>
- <field name="fields_id" search="[('model','=','res.partner'),('name','=','property_delivery_carrier')]"/>
+ <field name="name">property_delivery_carrier_id</field>
+ <field name="fields_id" search="[('model','=','res.partner'),('name','=','property_delivery_carrier_id')]"/>
</record>
-
</data>
-</openerp>
+</odoo>
diff --git a/addons/delivery/delivery_demo.xml b/addons/delivery/data/delivery_demo.xml
index 93a1957170c..44b693baada 100644
--- a/addons/delivery/delivery_demo.xml
+++ b/addons/delivery/data/delivery_demo.xml
@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
-<openerp>
+<odoo>
<data noupdate="1">
-
<!-- Update a transporter -->
<record id="res_partner_delivery" model="res.partner">
<field name="city">Manchester</field>
@@ -14,39 +13,33 @@
<record id="delivery_carrier" model="delivery.carrier">
<field name="name">The Poste</field>
- <field name="normal_price">20</field>
+ <field name="fixed_price">20</field>
<field name="sequence">2</field>
<field name="partner_id" ref="res_partner_delivery"/>
- <field name="product_id" ref="product_product_delivery"/>
- <field name="use_detailed_pricelist" eval="True"/>
+ <field name="delivery_type">base_on_rule</field>
+ <field name="product_type">service</field>
+ <field name="product_sale_ok" eval="False"/>
</record>
<record id="normal_delivery_carrier" model="delivery.carrier">
<field name="name">Normal Delivery Charges</field>
- <field name="normal_price">10</field>
+ <field name="fixed_price">10</field>
<field name="sequence">3</field>
<field name="partner_id" ref="res_partner_delivery"/>
- <field name="product_id" ref="product_product_delivery"/>
+ <field name="delivery_type">fixed</field>
+ <field name="product_type">service</field>
+ <field name="product_sale_ok" eval="False"/>
</record>
- <!-- Carrier Grids -->
-
- <record id="delivery_grid" model="delivery.grid">
- <field name="name">The Poste - Pricelist</field>
+ <record id="delivery_price_rule1" model="delivery.price.rule">
<field name="carrier_id" ref="delivery_carrier"/>
- </record>
- <!-- delivery charge of product if weight less than and equal 5kg-->
- <record id="delivery_grid_line1" model="delivery.grid.line">
- <field name="name">Weight &lt;= 5kg</field>
- <field name="grid_id" ref="delivery_grid"/>
<field eval="5" name="max_value"/>
<field eval="20" name="list_base_price"/>
<field eval="10" name="standard_price"/>
</record>
<!-- delivery charge of product if weight more than 5kg-->
- <record id="delivery_grid_line2" model="delivery.grid.line">
- <field name="name">Weight &gt; 5kg</field>
- <field name="grid_id" ref="delivery_grid"/>
+ <record id="delivery_price_rule2" model="delivery.price.rule">
+ <field name="carrier_id" ref="delivery_carrier"/>
<field name="operator">&gt;=</field>
<field eval="5" name="max_value"/>
<field eval="50" name="list_base_price"/>
@@ -54,20 +47,19 @@
</record>
<!-- free delivery charge if price more than 300-->
- <record id="delivery_grid_line3" model="delivery.grid.line">
- <field name="name">Free if price &gt;= 300</field>
- <field name="grid_id" ref="delivery_grid"/>
+ <record id="delivery_price_rule3" model="delivery.price.rule">
+ <field name="carrier_id" ref="delivery_carrier"/>
<field eval="300" name="max_value"/>
<field name="operator">&gt;=</field>
- <field name="type">price</field>
+ <field name="variable">price</field>
<field eval="0" name="list_base_price"/>
<field eval="0" name="standard_price"/>
</record>
<record forcecreate="True" id="property_delivery_carrier" model="ir.property">
- <field name="name">property_delivery_carrier</field>
- <field name="fields_id" search="[('model','=','res.partner'),('name','=','property_delivery_carrier')]"/>
+ <field name="name">property_delivery_carrier_id</field>
+ <field name="fields_id" search="[('model','=','res.partner'),('name','=','property_delivery_carrier_id')]"/>
<field name="value" eval="'delivery.carrier,'+str(ref('normal_delivery_carrier'))"/>
</record>
</data>
-</openerp>
+</odoo>
diff --git a/addons/delivery/delivery.py b/addons/delivery/delivery.py
deleted file mode 100644
index e508db3e282..00000000000
--- a/addons/delivery/delivery.py
+++ /dev/null
@@ -1,244 +0,0 @@
-# -*- coding: utf-8 -*-
-# Part of Odoo. See LICENSE file for full copyright and licensing details.
-
-import logging
-import time
-from openerp.osv import fields,osv
-from openerp.tools.translate import _
-import openerp.addons.decimal_precision as dp
-from openerp.tools.safe_eval import safe_eval as eval
-from openerp.exceptions import UserError
-
-_logger = logging.getLogger(__name__)
-
-class delivery_carrier(osv.osv):
- _name = "delivery.carrier"
- _description = "Carrier"
- _order = 'sequence, id'
-
- def get_price(self, cr, uid, ids, field_name, arg=None, context=None):
- res={}
- if context is None:
- context = {}
- sale_obj=self.pool.get('sale.order')
- grid_obj=self.pool.get('delivery.grid')
- for carrier in self.browse(cr, uid, ids, context=context):
- order_id=context.get('order_id',False)
- price=False
- available = False
- if order_id:
- order = sale_obj.browse(cr, uid, order_id, context=context)
- carrier_grid=self.grid_get(cr,uid,[carrier.id],order.partner_shipping_id.id,context)
- if carrier_grid:
- try:
- price=grid_obj.get_price(cr, uid, carrier_grid, order, time.strftime('%Y-%m-%d'), context)
- available = True
- except UserError, e:
- # no suitable delivery method found, probably configuration error
- _logger.info("Carrier %s: %s", carrier.name, e.name)
- price = 0.0
- else:
- price = 0.0
- res[carrier.id] = {
- 'price': price,
- 'available': available
- }
- return res
-
- _columns = {
- 'name': fields.char('Delivery Method', required=True, translate=True),
- 'sequence': fields.integer('Sequence', help="Determine the display order"),
- 'partner_id': fields.many2one('res.partner', 'Transport Company', required=True, help="The partner that is doing the delivery service."),
- 'product_id': fields.many2one('product.product', 'Delivery Product', required=True),
- 'grids_id': fields.one2many('delivery.grid', 'carrier_id', 'Delivery Grids'),
- 'available' : fields.function(get_price, string='Available',type='boolean', multi='price',
- help="Is the carrier method possible with the current order."),
- 'price' : fields.function(get_price, string='Price', multi='price'),
- 'active': fields.boolean('Active', help="If the active field is set to False, it will allow you to hide the delivery carrier without removing it."),
- 'normal_price': fields.float('Normal Price', help="Keep empty if the pricing depends on the advanced pricing per destination"),
- 'free_if_more_than': fields.boolean('Free If Order Total Amount Is More Than', help="If the order is more expensive than a certain amount, the customer can benefit from a free shipping"),
- 'amount': fields.float('Amount', help="Amount of the order to benefit from a free shipping, expressed in the company currency"),
- 'use_detailed_pricelist': fields.boolean('Advanced Pricing per Destination', help="Check this box if you want to manage delivery prices that depends on the destination, the weight, the total of the order, etc."),
- 'pricelist_ids': fields.one2many('delivery.grid', 'carrier_id', 'Advanced Pricing'),
- }
-
- _defaults = {
- 'active': 1,
- 'free_if_more_than': False,
- 'sequence': 10,
- }
-
- def grid_get(self, cr, uid, ids, contact_id, context=None):
- contact = self.pool.get('res.partner').browse(cr, uid, contact_id, context=context)
- for carrier in self.browse(cr, uid, ids, context=context):
- for grid in carrier.grids_id:
- get_id = lambda x: x.id
- country_ids = map(get_id, grid.country_ids)
- state_ids = map(get_id, grid.state_ids)
- if country_ids and not contact.country_id.id in country_ids:
- continue
- if state_ids and not contact.state_id.id in state_ids:
- continue
- if grid.zip_from and (contact.zip or '')< grid.zip_from:
- continue
- if grid.zip_to and (contact.zip or '')> grid.zip_to:
- continue
- return grid.id
- return False
-
- def create_grid_lines(self, cr, uid, ids, vals, context=None):
- if context is None:
- context = {}
- grid_line_pool = self.pool.get('delivery.grid.line')
- grid_pool = self.pool.get('delivery.grid')
- for record in self.browse(cr, uid, ids, context=context):
- # if using advanced pricing per destination: do not change
- if record.use_detailed_pricelist:
- continue
-
- # not using advanced pricing per destination: override grid
- grid_id = grid_pool.search(cr, uid, [('carrier_id', '=', record.id)], context=context)
- if grid_id and not (record.normal_price is not False or record.free_if_more_than):
- grid_pool.unlink(cr, uid, grid_id, context=context)
- grid_id = None
-
- if not (record.normal_price is not False or record.free_if_more_than):
- continue
-
- if not grid_id:
- grid_data = {
- 'name': record.name,
- 'carrier_id': record.id,
- 'sequence': 10,
- }
- grid_id = [grid_pool.create(cr, uid, grid_data, context=context)]
-
- lines = grid_line_pool.search(cr, uid, [('grid_id','in',grid_id)], context=context)
- if lines:
- grid_line_pool.unlink(cr, uid, lines, context=context)
-
- #create the grid lines
- if record.free_if_more_than:
- line_data = {
- 'grid_id': grid_id and grid_id[0],
- 'name': _('Free if more than %.2f') % record.amount,
- 'type': 'price',
- 'operator': '>=',
- 'max_value': record.amount,
- 'standard_price': 0.0,
- 'list_base_price': 0.0,
- }
- grid_line_pool.create(cr, uid, line_data, context=context)
- if record.normal_price is not False:
- line_data = {
- 'grid_id': grid_id and grid_id[0],
- 'name': _('Default price'),
- 'type': 'price',
- 'operator': '>=',
- 'max_value': 0.0,
- 'standard_price': record.normal_price,
- 'list_base_price': record.normal_price,
- }
- grid_line_pool.create(cr, uid, line_data, context=context)
- return True
-
- def write(self, cr, uid, ids, vals, context=None):
- if isinstance(ids, (int,long)):
- ids = [ids]
- res = super(delivery_carrier, self).write(cr, uid, ids, vals, context=context)
- self.create_grid_lines(cr, uid, ids, vals, context=context)
- return res
-
- def create(self, cr, uid, vals, context=None):
- res_id = super(delivery_carrier, self).create(cr, uid, vals, context=context)
- self.create_grid_lines(cr, uid, [res_id], vals, context=context)
- return res_id
-
-
-class delivery_grid(osv.osv):
- _name = "delivery.grid"
- _description = "Delivery Grid"
- _columns = {
- 'name': fields.char('Grid Name', required=True),
- 'sequence': fields.integer('Sequence', required=True, help="Gives the sequence order when displaying a list of delivery grid."),
- 'carrier_id': fields.many2one('delivery.carrier', 'Carrier', required=True, ondelete='cascade'),
- 'country_ids': fields.many2many('res.country', 'delivery_grid_country_rel', 'grid_id', 'country_id', 'Countries'),
- 'state_ids': fields.many2many('res.country.state', 'delivery_grid_state_rel', 'grid_id', 'state_id', 'States'),
- 'zip_from': fields.char('Start Zip', size=12),
- 'zip_to': fields.char('To Zip', size=12),
- 'line_ids': fields.one2many('delivery.grid.line', 'grid_id', 'Grid Line', copy=True),
- 'active': fields.boolean('Active', help="If the active field is set to False, it will allow you to hide the delivery grid without removing it."),
- }
- _defaults = {
- 'active': lambda *a: 1,
- 'sequence': lambda *a: 1,
- }
- _order = 'sequence'
-
- def get_price(self, cr, uid, id, order, dt, context=None):
- total = 0
- weight = 0
- volume = 0
- quantity = 0
- total_delivery = 0.0
- product_uom_obj = self.pool.get('product.uom')
- for line in order.order_line:
- if line.state == 'cancel':
- continue
- if line.is_delivery:
- total_delivery += line.price_total
- if not line.product_id or line.is_delivery:
- continue
- q = product_uom_obj._compute_qty(cr, uid, line.product_uom.id, line.product_uom_qty, line.product_id.uom_id.id)
- weight += (line.product_id.weight or 0.0) * q
- volume += (line.product_id.volume or 0.0) * q
- quantity += q
- total = (order.amount_total or 0.0) - total_delivery
-
- return self.get_price_from_picking(cr, uid, id, total,weight, volume, quantity, context=context)
-
- def get_price_from_picking(self, cr, uid, id, total, weight, volume, quantity, context=None):
- grid = self.browse(cr, uid, id, context=context)
- price = 0.0
- ok = False
- price_dict = {'price': total, 'volume':volume, 'weight': weight, 'wv':volume*weight, 'quantity': quantity}
- for line in grid.line_ids:
- test = eval(line.type+line.operator+str(line.max_value), price_dict)
- if test:
- price = line.list_base_price + line.list_price * price_dict[line.variable_factor]
- ok = True
- break
- if not ok:
- raise UserError(_("Selected product in the delivery method doesn't fulfill any of the delivery grid(s) criteria."))
-
- return price
-
-
-class delivery_grid_line(osv.osv):
- _name = "delivery.grid.line"
- _description = "Delivery Grid Line"
- _columns = {
- 'name': fields.char('Name', required=True),
- 'sequence': fields.integer('Sequence', required=True, help="Gives the sequence order when calculating delivery grid."),
- 'grid_id': fields.many2one('delivery.grid', 'Grid',required=True, ondelete='cascade'),
- 'type': fields.selection([('weight','Weight'),('volume','Volume'),\
- ('wv','Weight * Volume'), ('price','Price'), ('quantity','Quantity')],\
- 'Variable', required=True),
- 'operator': fields.selection([('==','='),('<=','<='),('<','<'),('>=','>='),('>','>')], 'Operator', required=True),
- 'max_value': fields.float('Maximum Value', required=True),
- 'variable_factor': fields.selection([('weight','Weight'),('volume','Volume'),('wv','Weight * Volume'), ('price','Price'), ('quantity','Quantity')], 'Variable Factor', required=True),
- 'list_base_price': fields.float('Sale Base Price', digits_compute=dp.get_precision('Product Price'), required=True),
- 'list_price': fields.float('Sale Price', digits_compute= dp.get_precision('Product Price'), required=True),
- 'standard_price': fields.float('Shipper Price', digits_compute= dp.get_precision('Product Price'), required=True),
- }
-
- _defaults = {
- 'sequence': lambda *args: 10,
- 'type': lambda *args: 'weight',
- 'operator': lambda *args: '<=',
- 'variable_factor': lambda *args: 'weight',
- 'list_price': 0.0,
- 'list_base_price': 0.0,
- 'standard_price': 0.0,
- }
- _order = 'sequence, list_price'
diff --git a/addons/delivery/models/__init__.py b/addons/delivery/models/__init__.py
index a023a5ca67b..d593c2c3957 100644
--- a/addons/delivery/models/__init__.py
+++ b/addons/delivery/models/__init__.py
@@ -1,24 +1,10 @@
# -*- coding: utf-8 -*-
-##############################################################################
-#
-# Odoo, Open Source Business Applications
-# Copyright (c) 2015 Odoo S.A. <http://openerp.com>
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-#
-##############################################################################
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
import delivery_carrier
+import delivery_price_rule
+import product_template
import sale_order
+import partner
import stock_picking
+import stock_move
diff --git a/addons/delivery/models/delivery_carrier.py b/addons/delivery/models/delivery_carrier.py
index 1a59f182fcb..e9031a01390 100644
--- a/addons/delivery/models/delivery_carrier.py
+++ b/addons/delivery/models/delivery_carrier.py
@@ -1,33 +1,19 @@
# -*- coding: utf-8 -*-
-##############################################################################
-#
-# Odoo, Open Source Business Applications
-# Copyright (c) 2015 Odoo S.A. <http://openerp.com>
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-#
-##############################################################################
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
import logging
-from openerp import models, fields, api
+from openerp import api, fields, models, _
from openerp.exceptions import UserError
_logger = logging.getLogger(__name__)
class DeliveryCarrier(models.Model):
- _inherit = 'delivery.carrier'
+ _name = 'delivery.carrier'
+ _inherits = {'product.product': 'product_id'}
+ _description = "Carrier"
+ _order = 'sequence, id'
+
''' A Shipping Provider
In order to add your own external provider, follow these steps:
@@ -47,10 +33,40 @@ class DeliveryCarrier(models.Model):
# Internals for shipping providers #
# -------------------------------- #
- delivery_type = fields.Selection([('grid', 'Default Delivery')], string="Delivery Type", default='grid', required='True')
-
+ sequence = fields.Integer(help="Determine the display order", default=10)
+ # This field will be overwritten by internal shipping providers by adding their own type (ex: 'fedex')
+ delivery_type = fields.Selection([('fixed', 'Fixed Price'), ('base_on_rule', 'Based on Rules')], string='Price Computation', default='fixed', required=True)
+ product_type = fields.Selection(related='product_id.type', default='service')
+ product_sale_ok = fields.Boolean(related='product_id.sale_ok', default=False)
+ partner_id = fields.Many2one('res.partner', string='Transporter Company', required=True, help="The partner that is doing the delivery service.")
+ product_id = fields.Many2one('product.product', string='Delivery Product', required=True, ondelete="cascade")
price = fields.Float(compute='get_price')
available = fields.Boolean(compute='get_price')
+ free_if_more_than = fields.Boolean('Free if Order total is more than', help="If the order is more expensive than a certain amount, the customer can benefit from a free shipping", default=False)
+ amount = fields.Float(string='Amount', help="Amount of the order to benefit from a free shipping, expressed in the company currency")
+ country_ids = fields.Many2many('res.country', 'delivery_carrier_country_rel', 'carrier_id', 'country_id', 'Countries')
+ state_ids = fields.Many2many('res.country.state', 'delivery_carrier_state_rel', 'carrier_id', 'state_id', 'States')
+ zip_from = fields.Char('Zip From')
+ zip_to = fields.Char('Zip To')
+ price_rule_ids = fields.One2many('delivery.price.rule', 'carrier_id', 'Pricing Rules', copy=True)
+ state = fields.Selection([('test', 'Testing'), ('production', 'Production')], 'State')
+ fixed_price = fields.Float(compute='_compute_fixed_price', inverse='_set_product_fixed_price', store=True, string='Fixed Price',help="Keep empty if the pricing depends on the advanced pricing per destination")
+
+ @api.depends('product_id.list_price', 'product_id.product_tmpl_id.list_price')
+ def _compute_fixed_price(self):
+ for carrier in self:
+ carrier.fixed_price = carrier.product_id.list_price
+
+ def _set_product_fixed_price(self):
+ for carrier in self:
+ carrier.product_id.list_price = carrier.fixed_price
+
+ @api.onchange('delivery_type')
+ def onchange_delivery_type(self):
+ if self.delivery_type in ["fixed", "base_on_rule"]:
+ self.state = 'production'
+ else:
+ self.state = 'test'
@api.one
def get_price(self):
@@ -63,19 +79,27 @@ class DeliveryCarrier(models.Model):
if order_id:
# FIXME: temporary hack until we refactor the delivery API in master
- if self.delivery_type != 'grid':
+ order = SaleOrder.browse(order_id)
+ if self.delivery_type not in ['fixed', 'base_on_rule']:
try:
- order = SaleOrder.browse(order_id)
self.price = self.get_shipping_price_from_so(order)[0]
self.available = True
except UserError as e:
- # no suitable delivery method found, probably configuration error
- _logger.info("Carrier %s: %s, not found", self.name, e.name)
- self.price = 0.0
+ # No suitable delivery method found, probably configuration error
+ _logger.info("Carrier %s: %s, not found", self.name, e.name)
+ self.price = 0.0
else:
- res = super(DeliveryCarrier, self).get_price('price', [])
- self.available = res[self.id]['available']
- self.price = res[self.id]['price']
+ carrier = self.verify_carrier(order.partner_shipping_id)
+ if carrier:
+ try:
+ self.price = carrier.get_price_available(order)
+ self.available = True
+ except UserError, e:
+ # No suitable delivery method found, probably configuration error
+ _logger.info("Carrier %s: %s", carrier.name, e.name)
+ self.price = 0.0
+ else:
+ self.price = 0.0
# -------------------------- #
# API for external providers #
@@ -123,3 +147,113 @@ class DeliveryCarrier(models.Model):
self.ensure_one()
if hasattr(self, '%s_cancel_shipment' % self.delivery_type):
return getattr(self, '%s_cancel_shipment' % self.delivery_type)(pickings)
+
+ @api.onchange('state_ids')
+ def onchange_states(self):
+ self.country_ids = [(6, 0, self.country_ids.ids + self.state_ids.mapped('country_id.id'))]
+
+ @api.onchange('country_ids')
+ def onchange_countries(self):
+ self.state_ids = [(6, 0, self.state_ids.filtered(lambda state: state.id in self.country_ids.mapped('state_ids').ids).ids)]
+
+ @api.multi
+ def verify_carrier(self, contact):
+ self.ensure_one()
+ if self.country_ids and contact.country_id not in self.country_ids:
+ return False
+ if self.state_ids and contact.state_id not in self.state_ids:
+ return False
+ if self.zip_from and (contact.zip or '') < self.zip_from:
+ return False
+ if self.zip_to and (contact.zip or '') > self.zip_to:
+ return False
+ return self
+
+ @api.multi
+ def create_price_rules(self):
+ PriceRule = self.env['delivery.price.rule']
+ for record in self:
+ # If using advanced pricing per destination: do not change
+ if record.delivery_type == 'base_on_rule':
+ continue
+
+ # Not using advanced pricing per destination: override lines
+ if record.delivery_type == 'base_on_rule' and not (record.fixed_price is not False or record.free_if_more_than):
+ record.price_rule_ids.unlink()
+
+ # Check that float, else 0.0 is False
+ if not (record.fixed_price is not False or record.free_if_more_than):
+ continue
+
+ if record.delivery_type == 'fixed':
+ PriceRule.search([('carrier_id', '=', record.id)]).unlink()
+
+ line_data = {
+ 'carrier_id': record.id,
+ 'variable': 'price',
+ 'operator': '>=',
+ }
+ # Create the delivery price rules
+ if record.free_if_more_than:
+ line_data.update({
+ 'max_value': record.amount,
+ 'standard_price': 0.0,
+ 'list_base_price': 0.0,
+ })
+ PriceRule.create(line_data)
+ if record.fixed_price is not False:
+ line_data.update({
+ 'max_value': 0.0,
+ 'standard_price': record.fixed_price,
+ 'list_base_price': record.fixed_price,
+ })
+ PriceRule.create(line_data)
+ return True
+
+ @api.model
+ def create(self, vals):
+ res = super(DeliveryCarrier, self).create(vals)
+ res.create_price_rules()
+ return res
+
+ @api.multi
+ def write(self, vals):
+ res = super(DeliveryCarrier, self).write(vals)
+ self.create_price_rules()
+ return res
+
+ @api.multi
+ def get_price_available(self, order):
+ self.ensure_one()
+ total = weight = volume = quantity = 0
+ total_delivery = 0.0
+ ProductUom = self.env['product.uom']
+ for line in order.order_line:
+ if line.state == 'cancel':
+ continue
+ if line.is_delivery:
+ total_delivery += line.price_total
+ if not line.product_id or line.is_delivery:
+ continue
+ qty = ProductUom._compute_qty(line.product_uom.id, line.product_uom_qty, line.product_id.uom_id.id)
+ weight += (line.product_id.weight or 0.0) * qty
+ volume += (line.product_id.volume or 0.0) * qty
+ quantity += qty
+ total = (order.amount_total or 0.0) - total_delivery
+
+ return self.get_price_from_picking(total, weight, volume, quantity)
+
+ def get_price_from_picking(self, total, weight, volume, quantity):
+ price = 0.0
+ criteria_found = False
+ price_dict = {'price': total, 'volume': volume, 'weight': weight, 'wv': volume * weight, 'quantity': quantity}
+ for line in self.price_rule_ids:
+ test = eval(line.variable + line.operator + str(line.max_value), price_dict)
+ if test:
+ price = line.list_base_price + line.list_price * price_dict[line.variable_factor]
+ criteria_found = True
+ break
+ if not criteria_found:
+ raise UserError(_("Selected product in the delivery method doesn't fulfill any of the delivery carrier(s) criteria."))
+
+ return price
diff --git a/addons/delivery/models/delivery_price_rule.py b/addons/delivery/models/delivery_price_rule.py
new file mode 100644
index 00000000000..1d0dc49f031
--- /dev/null
+++ b/addons/delivery/models/delivery_price_rule.py
@@ -0,0 +1,34 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from openerp import models, fields, api
+import openerp.addons.decimal_precision as dp
+
+
+class PriceRule(models.Model):
+ _name = "delivery.price.rule"
+ _description = "Delivery Price Rules"
+ _order = 'sequence, list_price'
+
+ @api.depends('variable', 'operator', 'max_value', 'list_base_price', 'list_price', 'variable_factor')
+ def _get_name(self):
+ for rule in self:
+ name = 'if %s %s %s then' % (rule.variable, rule.operator, rule.max_value)
+ if rule.list_base_price and not rule.list_price:
+ name = '%s fixed price %s' % (name, rule.list_base_price)
+ elif rule.list_price and not rule.list_base_price:
+ name = '%s %s times %s' % (name, rule.list_price, rule.variable_factor)
+ else:
+ name = '%s fixed price %s and %s times %s Extra' % (name, rule.list_base_price, rule.list_price, rule.variable_factor)
+ rule.name = name
+
+ name = fields.Char(compute='_get_name')
+ sequence = fields.Integer(required=True, help="Gives the sequence order when calculating delivery carrier.", default=10)
+ carrier_id = fields.Many2one('delivery.carrier', 'Carrier', required=True, ondelete='cascade')
+ variable = fields.Selection([('weight', 'Weight'), ('volume', 'Volume'), ('wv', 'Weight * Volume'), ('price', 'Price'), ('quantity', 'Quantity')], 'Variable', required=True, default='weight')
+ operator = fields.Selection([('==', '='), ('<=', '<='), ('<', '<'), ('>=', '>='), ('>', '>')], 'Operator', required=True, default='<=')
+ max_value = fields.Float('Maximum Value', required=True)
+ variable_factor = fields.Selection([('weight', 'Weight'), ('volume', 'Volume'), ('wv', 'Weight * Volume'), ('price', 'Price'), ('quantity', 'Quantity')], 'Variable Factor', required=True, default='weight')
+ list_base_price = fields.Float(string='Sale Base Price', digits_compute=dp.get_precision('Product Price'), required=True, default=0.0)
+ list_price = fields.Float('Sale Price', digits_compute=dp.get_precision('Product Price'), required=True, default=0.0)
+ standard_price = fields.Float('Cost Price', digits_compute=dp.get_precision('Product Price'), required=True, default=0.0)
diff --git a/addons/delivery/models/partner.py b/addons/delivery/models/partner.py
new file mode 100644
index 00000000000..ca558b98216
--- /dev/null
+++ b/addons/delivery/models/partner.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from openerp import fields, models
+
+
+class ResPartner(models.Model):
+ _inherit = 'res.partner'
+
+ property_delivery_carrier_id = fields.Many2one('delivery.carrier', company_dependent=True, string="Delivery Method", help="This delivery method will be used when invoicing from picking.")
diff --git a/addons/delivery/models/product_template.py b/addons/delivery/models/product_template.py
new file mode 100644
index 00000000000..228dea5f149
--- /dev/null
+++ b/addons/delivery/models/product_template.py
@@ -0,0 +1,19 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from openerp import api, models
+
+
+class ProductTemplate(models.Model):
+ _inherit = 'product.template'
+
+ @api.multi
+ def write(self, vals):
+ res = super(ProductTemplate, self).write(vals)
+ if vals.get('list_price'):
+ DeliveryCarrier = self.env['delivery.carrier']
+ for template in self:
+ carrier = DeliveryCarrier.search([('product_id', 'in', template.product_variant_ids.ids)])
+ if carrier:
+ carrier.create_price_rules()
+ return res
diff --git a/addons/delivery/models/sale_order.py b/addons/delivery/models/sale_order.py
index 2fbf68d86e7..36f5b24c5b8 100644
--- a/addons/delivery/models/sale_order.py
+++ b/addons/delivery/models/sale_order.py
@@ -1,23 +1,5 @@
# -*- coding: utf-8 -*-
-##############################################################################
-#
-# Odoo, Open Source Business Applications
-# Copyright (c) 2015 Odoo S.A. <http://openerp.com>
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-#
-##############################################################################
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
from openerp import models, fields, api, _
from openerp.exceptions import UserError
@@ -27,19 +9,29 @@ class SaleOrder(models.Model):
_inherit = 'sale.order'
delivery_price = fields.Float(string='Estimated Delivery Price', compute='_compute_delivery_price', store=True)
+ carrier_id = fields.Many2one("delivery.carrier", string="Delivery Method", help="Fill this field if you plan to invoice the shipping based on picking.")
@api.depends('carrier_id', 'partner_id', 'order_line')
def _compute_delivery_price(self):
for order in self:
if order.state != 'draft':
- # we do not want to recompute the shipping price of an already validated/done SO
+ # We do not want to recompute the shipping price of an already validated/done SO
continue
elif order.carrier_id.delivery_type != 'grid' and not order.order_line:
- # prevent SOAP call to external shipping provider when SO has no lines yet
+ # Prevent SOAP call to external shipping provider when SO has no lines yet
continue
else:
order.delivery_price = order.carrier_id.with_context(order_id=order.id).price
+ @api.onchange('partner_id')
+ def onchange_partner_id_dtype(self):
+ if self.partner_id:
+ self.carrier_id = self.partner_id.property_delivery_carrier_id
+
+ @api.multi
+ def _delivery_unset(self):
+ self.env['sale.order.line'].search([('order_id', 'in', self.ids), ('is_delivery', '=', True)]).unlink()
+
@api.multi
def delivery_set(self):
@@ -49,16 +41,26 @@ class SaleOrder(models.Model):
self._delivery_unset()
for order in self:
- if order.carrier_id and order.carrier_id.delivery_type != 'grid':
- # Shipping providers are used when delivery_type is other than 'grid'
-
+ carrier = order.carrier_id
+ if carrier:
if order.state not in ('draft', 'sent'):
raise UserError(_('The order state have to be draft to add delivery lines.'))
- carrier = order.carrier_id
- account_id = carrier.product_id.property_account_income.id
+ if carrier.delivery_type not in ['fixed', 'base_on_rule']:
+ # Shipping providers are used when delivery_type is other than 'fixed' or 'base_on_rule'
+ price_unit = order.carrier_id.get_shipping_price_from_so(order)[0]
+ else:
+ # Classic grid-based carriers
+ carrier = order.carrier_id.verify_carrier(order.partner_shipping_id)
+ if not carrier:
+ raise UserError(_('No carrier matching.'))
+ price_unit = carrier.get_price_available(order)
+ if order.company_id.currency_id.id != order.pricelist_id.currency_id.id:
+ price_unit = order.company_id.currency_id.with_context(date=order.date_order).compute(order.pricelist_id.currency_id.id, price_unit)
+
+ account_id = carrier.product_id.property_account_income_id.id
if not account_id:
- account_id = carrier.product_id.categ_id.property_account_income_categ.id
+ account_id = carrier.product_id.categ_id.property_account_income_categ_id.id
# Apply fiscal position
taxes = carrier.product_id.taxes_id
@@ -67,17 +69,23 @@ class SaleOrder(models.Model):
account_id = order.fiscal_position_id.map_account(account_id)
taxes_ids = order.fiscal_position_id.map_tax(taxes).ids
+ # Create the sale order line
SaleOrderLine.create({
'order_id': order.id,
- 'name': order.carrier_id.name,
+ 'name': carrier.name,
'product_uom_qty': 1,
- 'product_uom': order.carrier_id.product_id.uom_id.id,
- 'product_id': order.carrier_id.product_id.id,
- 'price_unit': order.carrier_id.get_shipping_price_from_so(order)[0],
+ 'product_uom': carrier.product_id.uom_id.id,
+ 'product_id': carrier.product_id.id,
+ 'price_unit': price_unit,
'tax_id': [(6, 0, taxes_ids)],
'is_delivery': True
})
else:
- # Classic grid-based carriers
- super(SaleOrder, self).delivery_set()
+ raise UserError(_('No carrier set for this order.'))
+
+
+class SaleOrderLine(models.Model):
+ _inherit = 'sale.order.line'
+
+ is_delivery = fields.Boolean(string="Is a Delivery", default=False)
diff --git a/addons/delivery/models/stock_move.py b/addons/delivery/models/stock_move.py
new file mode 100644
index 00000000000..06590501419
--- /dev/null
+++ b/addons/delivery/models/stock_move.py
@@ -0,0 +1,39 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from openerp import api, fields, models
+
+import openerp.addons.decimal_precision as dp
+
+
+class StockMove(models.Model):
+ _inherit = 'stock.move'
+
+ def _default_uom(self):
+ uom_categ_id = self.env.ref('product.product_uom_categ_kgm').id
+ return self.env['product.uom'].search([('category_id', '=', uom_categ_id), ('factor', '=', 1)], limit=1)
+
+ weight = fields.Float(compute='_cal_move_weight', digits_compute=dp.get_precision('Stock Weight'), store=True)
+ weight_uom_id = fields.Many2one('product.uom', string='Unit of Measure', required=True, readonly=True, help="Unit of Measure (Unit of Measure) is the unit of measurement for Weight", default=_default_uom)
+
+ @api.depends('product_id', 'product_uom_qty', 'product_uom')
+ def _cal_move_weight(self):
+ for move in self.filtered(lambda moves: moves.product_id.weight > 0.00):
+ move.weight = (move.product_qty * move.product_id.weight)
+
+ @api.multi
+ def action_confirm(self):
+ """
+ Pass the carrier to the picking from the sales order
+ (Should also work in case of Phantom BoMs when on explosion the original move is deleted)
+ """
+ procs_to_check = []
+ for move in self:
+ if move.procurement_id and move.procurement_id.sale_line_id and move.procurement_id.sale_line_id.order_id.carrier_id:
+ procs_to_check += [move.procurement_id]
+ res = super(StockMove, self).action_confirm()
+ for proc in procs_to_check:
+ pickings = (proc.move_ids.mapped('picking_id')).filtered(lambda record: not record.carrier_id)
+ if pickings:
+ pickings.write({'carrier_id': proc.sale_line_id.order_id.carrier_id.id})
+ return res
diff --git a/addons/delivery/models/stock_picking.py b/addons/delivery/models/stock_picking.py
index c8a34b490a9..b6b9f291d71 100644
--- a/addons/delivery/models/stock_picking.py
+++ b/addons/delivery/models/stock_picking.py
@@ -1,47 +1,48 @@
# -*- coding: utf-8 -*-
-##############################################################################
-#
-# Odoo, Open Source Business Applications
-# Copyright (c) 2015 Odoo S.A. <http://openerp.com>
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-#
-##############################################################################
-
-from openerp import models, fields, api
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from openerp import models, fields, api, _
+from openerp.exceptions import UserError
+
+import openerp.addons.decimal_precision as dp
+
class StockPicking(models.Model):
_inherit = 'stock.picking'
+ def _default_uom(self):
+ uom_categ_id = self.env.ref('product.product_uom_categ_kgm').id
+ return self.env['product.uom'].search([('category_id', '=', uom_categ_id), ('factor', '=', 1)], limit=1)
+
carrier_price = fields.Float(string="Shipping Cost", readonly=True)
delivery_type = fields.Selection(related='carrier_id.delivery_type', readonly=True)
+ carrier_id = fields.Many2one("delivery.carrier", string="Carrier")
+ volume = fields.Float(copy=False)
+ weight = fields.Float(compute='_cal_weight', digits_compute=dp.get_precision('Stock Weight'), store=True)
+ carrier_tracking_ref = fields.Char(string='Carrier Tracking Ref', copy=False)
+ number_of_packages = fields.Integer(string='Number of Packages', copy=False)
+ weight_uom_id = fields.Many2one('product.uom', string='Unit of Measure', required=True, readonly="1", help="Unit of measurement for Weight", default=_default_uom)
+
+ @api.depends('product_id', 'move_lines')
+ def _cal_weight(self):
+ for picking in self:
+ picking.weight = sum(move.weight for move in picking.move_lines if move.state != 'cancel')
@api.multi
def do_transfer(self):
+ self.ensure_one()
res = super(StockPicking, self).do_transfer()
- if self.carrier_id and self.carrier_id.delivery_type != 'grid':
+ if self.carrier_id and self.carrier_id.delivery_type not in ['fixed', 'base_on_rule']:
self.send_to_shipper()
return res
- # Signature due to strange old api methods
- @api.model
- def _prepare_shipping_invoice_line(self, picking, invoice):
- picking.ensure_one()
+ @api.multi
+ def _prepare_shipping_invoice_line(self, invoice):
+ self.ensure_one()
invoice.ensure_one()
- carrier = picking.carrier_id
+ carrier = self.carrier_id
# No carrier
if not carrier:
@@ -51,12 +52,18 @@ class StockPicking(models.Model):
return None
# Classic carrier
- if carrier.delivery_type == 'grid':
- return super(StockPicking, self)._prepare_shipping_invoice_line(picking, invoice)
-
- # Shipping provider
- price = picking.carrier_price
-
+ if carrier.delivery_type in ['fixed', 'base_on_rule']:
+ carrier = carrier.verify_carrier(self.partner_id)
+ if not carrier:
+ raise UserError(_('The carrier %s (id: %d) has no delivery method!') % (self.carrier_id.name, self.carrier_id.id))
+ quantity = sum([line.product_uom_qty for line in self.move_lines])
+ price = carrier.get_price_from_picking(invoice.amount_untaxed, self.weight, self.volume, quantity)
+ else:
+ # Shipping provider
+ price = self.carrier_price
+
+ if invoice.company_id.currency_id.id != invoice.currency_id.id:
+ price = invoice.company_id.currency_id.with_context(date=invoice.date_invoice).compute(invoice.currency_id.id, price)
account_id = carrier.product_id.property_account_income.id
if not account_id:
account_id = carrier.product_id.categ_id.property_account_income_categ.id
@@ -65,8 +72,8 @@ class StockPicking(models.Model):
taxes_ids = taxes.ids
# Apply original SO fiscal position
- if picking.sale_id.fiscal_position_id:
- fpos = picking.sale_id.fiscal_position_id
+ if self.sale_id.fiscal_position_id:
+ fpos = self.sale_id.fiscal_position_id
account_id = fpos.map_account(account_id)
taxes_ids = fpos.map_tax(taxes).ids
@@ -83,12 +90,24 @@ class StockPicking(models.Model):
return res
- @api.one
+ @api.model
+ def _invoice_create_line(self, moves, journal_id, inv_type='out_invoice'):
+ InvoiceLine = self.env['account.invoice.line']
+ invoice_ids = super(StockPicking, self)._invoice_create_line(moves, journal_id, inv_type=inv_type)
+ for move in moves:
+ for invoice in move.picking_id.sale_id.invoice_ids.filtered(lambda invoice: invoice.id in invoice_ids):
+ invoice_line = move.picking_id._prepare_shipping_invoice_line(invoice)
+ if invoice_line:
+ InvoiceLine.create(invoice_line)
+ return invoice_ids
+
+ @api.multi
def send_to_shipper(self):
+ self.ensure_one()
res = self.carrier_id.send_shipping(self)[0]
self.carrier_price = res['exact_price']
self.carrier_tracking_ref = res['tracking_number']
- msg = "Shipment sent to carrier %s for expedition with tracking number %s" % (self.carrier_id.name, self.carrier_tracking_ref)
+ msg = _("Shipment sent to carrier %s for expedition with tracking number %s") % (self.carrier_id.name, self.carrier_tracking_ref)
self.message_post(body=msg)
@api.multi
diff --git a/addons/delivery/partner.py b/addons/delivery/partner.py
deleted file mode 100644
index 2efe058527f..00000000000
--- a/addons/delivery/partner.py
+++ /dev/null
@@ -1,14 +0,0 @@
-# -*- coding: utf-8 -*-
-# Part of Odoo. See LICENSE file for full copyright and licensing details.
-
-from openerp.osv import fields, osv
-
-class res_partner(osv.osv):
- _inherit = 'res.partner'
- _columns = {
- 'property_delivery_carrier': fields.property(
- type='many2one',
- relation='delivery.carrier',
- string="Delivery Method",
- help="This delivery method will be used when invoicing from picking."),
- }
diff --git a/addons/delivery/sale.py b/addons/delivery/sale.py
deleted file mode 100644
index 629670990c7..00000000000
--- a/addons/delivery/sale.py
+++ /dev/null
@@ -1,79 +0,0 @@
-# -*- coding: utf-8 -*-
-# Part of Odoo. See LICENSE file for full copyright and licensing details.
-
-import time
-from openerp import api
-from openerp.osv import fields, osv
-from openerp.tools.translate import _
-from openerp.exceptions import UserError
-
-
-class sale_order_line(osv.Model):
- _inherit = 'sale.order.line'
-
- _columns = {
- 'is_delivery': fields.boolean("Is a Delivery"),
- }
-
- _defaults = {
- 'is_delivery': False
- }
-
-
-class sale_order(osv.Model):
- _inherit = 'sale.order'
- _columns = {
- 'carrier_id': fields.many2one(
- "delivery.carrier", string="Delivery Method",
- help="Complete this field if you plan to invoice the shipping based on picking."),
- }
-
- @api.onchange('partner_id')
- def onchange_partner_id_dtype(self):
- if self.partner_id:
- self.carrier_id = self.partner_id.property_delivery_carrier
-
-
- def _delivery_unset(self, cr, uid, ids, context=None):
- sale_obj = self.pool['sale.order.line']
- line_ids = sale_obj.search(cr, uid, [('order_id', 'in', ids), ('is_delivery', '=', True)],context=context)
- sale_obj.unlink(cr, uid, line_ids, context=context)
-
- def delivery_set(self, cr, uid, ids, context=None):
- line_obj = self.pool.get('sale.order.line')
- grid_obj = self.pool.get('delivery.grid')
- carrier_obj = self.pool.get('delivery.carrier')
- acc_fp_obj = self.pool.get('account.fiscal.position')
- self._delivery_unset(cr, uid, ids, context=context)
- currency_obj = self.pool.get('res.currency')
- line_ids = []
- for order in self.browse(cr, uid, ids, context=context):
- grid_id = carrier_obj.grid_get(cr, uid, [order.carrier_id.id], order.partner_shipping_id.id)
- if not grid_id:
- raise UserError(_('No grid matching for this carrier!'))
-
- if order.state not in ('draft', 'sent'):
- raise UserError(_('The order state have to be draft to add delivery lines.'))
-
- grid = grid_obj.browse(cr, uid, grid_id, context=context)
-
- taxes = grid.carrier_id.product_id.taxes_id
- fpos = order.fiscal_position_id or False
- taxes_ids = acc_fp_obj.map_tax(cr, uid, fpos, taxes)
- price_unit = grid_obj.get_price(cr, uid, grid.id, order, time.strftime('%Y-%m-%d'), context)
- if order.company_id.currency_id.id != order.pricelist_id.currency_id.id:
- price_unit = currency_obj.compute(cr, uid, order.company_id.currency_id.id, order.pricelist_id.currency_id.id,
- price_unit, context=dict(context or {}, date=order.date_order))
- #create the sale order line
- line_id = line_obj.create(cr, uid, {
- 'order_id': order.id,
- 'name': grid.carrier_id.name,
- 'product_uom_qty': 1,
- 'product_uom': grid.carrier_id.product_id.uom_id.id,
- 'product_id': grid.carrier_id.product_id.id,
- 'price_unit': price_unit,
- 'tax_id': [(6, 0, taxes_ids)],
- 'is_delivery': True
- }, context=context)
- line_ids.append(line_id)
- return line_ids
diff --git a/addons/delivery/security/ir.model.access.csv b/addons/delivery/security/ir.model.access.csv
index 8a8bc9c2ea8..00b2d70a432 100644
--- a/addons/delivery/security/ir.model.access.csv
+++ b/addons/delivery/security/ir.model.access.csv
@@ -1,12 +1,9 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_delivery_carrier,delivery.carrier,model_delivery_carrier,base.group_sale_salesman,1,0,0,0
-access_delivery_grid,delivery.grid,model_delivery_grid,base.group_sale_salesman,1,0,0,0
-access_delivery_grid_line,delivery.grid.line,model_delivery_grid_line,base.group_sale_salesman,1,0,0,0
+access_delivery_price_rule,delivery.price.rule,model_delivery_price_rule,base.group_sale_salesman,1,0,0,0
access_delivery_carrier_manager,delivery.carrier,model_delivery_carrier,base.group_sale_manager,1,1,1,1
-access_delivery_grid_manager,delivery.grid,model_delivery_grid,base.group_sale_manager,1,1,1,1
-access_delivery_grid_line_manager,delivery.grid.line,model_delivery_grid_line,base.group_sale_manager,1,1,1,1
+access_delivery_price_rule_manager,delivery.price.rule,model_delivery_price_rule,base.group_sale_manager,1,1,1,1
access_delivery_carrier_partner_manager,delivery.carrier partner_manager,model_delivery_carrier,base.group_partner_manager,1,0,0,0
access_delivery_carrier_stock_user,delivery.carrier stock_user,model_delivery_carrier,stock.group_stock_user,1,0,0,0
access_delivery_carrier_stock_manager,delivery.carrier,model_delivery_carrier,stock.group_stock_manager,1,1,1,1
-access_delivery_grid_stock_manager,delivery.grid,model_delivery_grid,stock.group_stock_manager,1,1,1,1
-access_delivery_grid_line_stock_manager,delivery.grid.line,model_delivery_grid_line,stock.group_stock_manager,1,1,1,1
+access_delivery_price_rule_stock_manager,delivery.price.rule,model_delivery_price_rule,stock.group_stock_manager,1,1,1,1
diff --git a/addons/delivery/stock.py b/addons/delivery/stock.py
deleted file mode 100644
index 3967733ea9e..00000000000
--- a/addons/delivery/stock.py
+++ /dev/null
@@ -1,164 +0,0 @@
-# -*- coding: utf-8 -*-
-# Part of Odoo. See LICENSE file for full copyright and licensing details.
-
-from openerp.osv import fields,osv
-from openerp.tools.translate import _
-from openerp.exceptions import UserError
-
-import openerp.addons.decimal_precision as dp
-
-# Overloaded stock_picking to manage carriers :
-class stock_picking(osv.osv):
- _inherit = 'stock.picking'
-
- def _cal_weight(self, cr, uid, ids, name, args, context=None):
- res = {}
- for picking in self.browse(cr, uid, ids, context=context):
- res[picking.id] = sum(move.weight for move in picking.move_lines if move.state != 'cancel')
- return res
-
- def _get_picking_line(self, cr, uid, ids, context=None):
- result = {}
- for line in self.pool.get('stock.move').browse(cr, uid, ids, context=context):
- result[line.picking_id.id] = True
- return result.keys()
-
-
- _columns = {
- 'carrier_id':fields.many2one("delivery.carrier","Carrier"),
- 'volume': fields.float('Volume', copy=False),
- 'weight': fields.function(_cal_weight, type='float', string='Weight', digits_compute= dp.get_precision('Stock Weight'),
- store={
- 'stock.picking': (lambda self, cr, uid, ids, c={}: ids, ['move_lines'], 40),
- 'stock.move': (_get_picking_line, ['state', 'picking_id', 'product_id','product_uom_qty','product_uom'], 40),
- }),
- 'carrier_tracking_ref': fields.char('Carrier Tracking Ref', copy=False),
- 'number_of_packages': fields.integer('Number of Packages', copy=False),
- 'weight_uom_id': fields.many2one('product.uom', 'Unit of Measure', required=True,readonly="1",help="Unit of measurement for Weight",),
- }
-
- def _prepare_shipping_invoice_line(self, cr, uid, picking, invoice, context=None):
- """Prepare the invoice line to add to the shipping costs to the shipping's
- invoice.
-
- :param browse_record picking: the stock picking being invoiced
- :param browse_record invoice: the stock picking's invoice
- :return: dict containing the values to create the invoice line,
- or None to create nothing
- """
- carrier_obj = self.pool.get('delivery.carrier')
- grid_obj = self.pool.get('delivery.grid')
- fpos_obj = self.pool['account.fiscal.position']
- currency_obj = self.pool.get('res.currency')
- if not picking.carrier_id or \
- any(inv_line.product_id.id == picking.carrier_id.product_id.id
- for inv_line in invoice.invoice_line_ids):
- return None
- grid_id = carrier_obj.grid_get(cr, uid, [picking.carrier_id.id],
- picking.partner_id.id, context=context)
- if not grid_id:
- raise UserError(_('The carrier %s (id: %d) has no delivery grid!') % (picking.carrier_id.name,picking.carrier_id.id))
- quantity = sum([line.product_uom_qty for line in picking.move_lines])
- price = grid_obj.get_price_from_picking(cr, uid, grid_id,
- invoice.amount_untaxed, picking.weight, picking.volume,
- quantity, context=context)
- if invoice.company_id.currency_id.id != invoice.currency_id.id:
- price = currency_obj.compute(cr, uid, invoice.company_id.currency_id.id, invoice.currency_id.id,
- price, context=dict(context or {}, date=invoice.date_invoice))
- account_id = picking.carrier_id.product_id.property_account_income_id.id
- if not account_id:
- account_id = picking.carrier_id.product_id.categ_id\
- .property_account_income_categ_id.id
-
- taxes = picking.carrier_id.product_id.taxes_id
- taxes_ids = [x.id for x in taxes]
- partner = picking.partner_id or False
- fpos = None
- if picking.sale_id and picking.sale_id.fiscal_position_id:
- fpos = picking.sale_id.fiscal_position_id
- elif picking.partner_id:
- fpos = partner.property_account_position_id
- # TDE FIXME: spotted undefined variable
- account_id = fpos_obj.map_account(cr, uid, fpos, account_id, context=context)
- taxes_ids = fpos_obj.map_tax(cr, uid, fpos, taxes, context=context)
-
- return {
- 'name': picking.carrier_id.name,
- 'invoice_id': invoice.id,
- 'uom_id': picking.carrier_id.product_id.uom_id.id,
- 'product_id': picking.carrier_id.product_id.id,
- 'account_id': account_id,
- 'price_unit': price,
- 'quantity': 1,
- 'invoice_line_tax_ids': [(6, 0, taxes_ids)],
- }
-
- def _invoice_create_line(self, cr, uid, moves, journal_id, inv_type='out_invoice', context=None):
- invoice_line_obj = self.pool.get('account.invoice.line')
- invoice_ids = super(stock_picking, self)._invoice_create_line(cr, uid, moves, journal_id, inv_type=inv_type, context=context)
- delivey_invoices = {}
- for move in moves:
- for invoice in move.picking_id.sale_id.invoice_ids:
- if invoice.id in invoice_ids:
- delivey_invoices[invoice] = move.picking_id
- if delivey_invoices:
- for invoice, picking in delivey_invoices.items():
- invoice_line = self._prepare_shipping_invoice_line(cr, uid, picking, invoice, context=context)
- if invoice_line:
- invoice_line_obj.create(cr, uid, invoice_line)
- return invoice_ids
-
- def _get_default_uom(self, cr, uid, context=None):
- uom_categ_id = self.pool.get('ir.model.data').xmlid_to_res_id(cr, uid, 'product.product_uom_categ_kgm')
- return self.pool.get('product.uom').search(cr, uid, [('category_id', '=', uom_categ_id), ('factor', '=', 1)])[0]
-
- _defaults = {
- 'weight_uom_id': lambda self, cr, uid, c: self._get_default_uom(cr, uid, c),
- }
-
-
-class stock_move(osv.osv):
- _inherit = 'stock.move'
-
- def _cal_move_weight(self, cr, uid, ids, name, args, context=None):
- res = {}
- for move in self.browse(cr, uid, ids, context=context):
- weight = 0.00
- if move.product_id.weight > 0.00:
- weight = move.product_qty * move.product_id.weight
- res[move.id] = weight
- return res
-
- _columns = {
- 'weight': fields.function(_cal_move_weight, type='float', string='Weight', digits_compute= dp.get_precision('Stock Weight'),
- store={
- 'stock.move': (lambda self, cr, uid, ids, c=None: ids, ['product_id', 'product_uom_qty', 'product_uom'], 30),
- }),
- 'weight_uom_id': fields.many2one('product.uom', 'Unit of Measure', required=True,readonly="1",help="Unit of Measure (Unit of Measure) is the unit of measurement for Weight",),
- }
-
- def action_confirm(self, cr, uid, ids, context=None):
- """
- Pass the carrier to the picking from the sales order
- (Should also work in case of Phantom BoMs when on explosion the original move is deleted)
- """
- procs_to_check = []
- for move in self.browse(cr, uid, ids, context=context):
- if move.procurement_id and move.procurement_id.sale_line_id and move.procurement_id.sale_line_id.order_id.carrier_id:
- procs_to_check += [move.procurement_id]
- res = super(stock_move, self).action_confirm(cr, uid, ids, context=context)
- pick_obj = self.pool.get("stock.picking")
- for proc in procs_to_check:
- pickings = list(set([x.picking_id.id for x in proc.move_ids if x.picking_id and not x.picking_id.carrier_id]))
- if pickings:
- pick_obj.write(cr, uid, pickings, {'carrier_id': proc.sale_line_id.order_id.carrier_id.id}, context=context)
- return res
-
-
- def _get_default_uom(self, cr, uid, context=None):
- uom_categ_id = self.pool.get('ir.model.data').xmlid_to_res_id(cr, uid, 'product.product_uom_categ_kgm')
- return self.pool.get('product.uom').search(cr, uid, [('category_id', '=', uom_categ_id),('factor','=',1)])[0]
-
- _defaults = {
- 'weight_uom_id': lambda self, cr, uid, c: self._get_default_uom(cr, uid, c),
- }
diff --git a/addons/delivery/test/delivery_cost.yml b/addons/delivery/test/delivery_cost.yml
deleted file mode 100644
index d44c1d8ab87..00000000000
--- a/addons/delivery/test/delivery_cost.yml
+++ /dev/null
@@ -1,80 +0,0 @@
--
- In order to test Carrier Cost,
--
- Create sale order with Normal Delivery Charges
--
- !record {model: sale.order, id: sale_normal_delivery_charges}:
- partner_id: base.res_partner_18
- partner_invoice_id: base.res_partner_18
- partner_shipping_id: base.res_partner_18
- pricelist_id: product.list0
- order_line:
- - name: 'PC Assamble + 2GB RAM'
- product_id: product.product_product_4
- product_uom_qty: 1
- product_uom: product.product_uom_unit
- price_unit: 750.00
- carrier_id: normal_delivery_carrier
--
- I add delivery cost in Sale order.
--
- !python {model: sale.order}: |
- self.pool.get('product.product').write(cr, uid, [ref('product_product_delivery')], {'taxes_id': [(5, 0, 0)]}, context=context) # Removing taxes as this can cause problem (re. price_included)
- self.delivery_set(cr, uid, [ref("sale_normal_delivery_charges")], context=context)
--
- I check sale order after added delivery cost.
--
- !python {model: sale.order.line}: |
- from openerp.tools import float_compare
- line_ids = self.search(cr, uid, [('order_id','=', ref('sale_normal_delivery_charges')), ('product_id','=', ref('product_product_delivery'))])
- assert len(line_ids), "Delivery cost is not Added"
- line_data = self.browse(cr ,uid ,line_ids[0] ,context)
- assert float_compare(line_data.price_subtotal, 10, precision_digits=2) == 0, "Delivey cost is not correspond."
--
- I confirm the sale order.
--
- !python {model: sale.order}: |
- self.action_confirm(cr, uid, ref("sale_normal_delivery_charges"), context=context)
--
- Create one more sale order with Free Delivery Charges
--
- !record {model: sale.order, id: sale_free_delivery_charges}:
- partner_id: base.res_partner_4
- partner_invoice_id: base.res_partner_address_13
- partner_shipping_id: base.res_partner_address_13
- pricelist_id: product.list0
- order_line:
- - name: 'Service on demand'
- product_id: product.product_product_1
- product_uom_qty: 24
- product_uom: product.product_uom_hour
- price_unit: 75.00
- order_line:
- - name: 'On Site Assistance'
- product_id: product.product_product_2
- product_uom_qty: 30
- product_uom: product.product_uom_hour
- price_unit: 38.25
- carrier_id: free_delivery_carrier
--
- I add free delivery cost in Sale order.
--
- !python {model: sale.order}: |
- self.delivery_set(cr, uid, [ref("sale_free_delivery_charges")], context=context)
--
- I check sale order after added delivery cost.
--
- !python {model: sale.order.line}: |
- from openerp.tools import float_compare
- line_ids = self.search(cr, uid, [('order_id','=', ref('sale_free_delivery_charges')), ('product_id','=', ref('product_product_delivery'))])
- assert len(line_ids), "Delivery cost is not Added"
- line_data = self.browse(cr ,uid ,line_ids[0] ,context)
- assert float_compare(line_data.price_subtotal, 0, precision_digits=2) == 0, "Delivey cost is not correspond."
--
- I set default delivery policy.
--
- !record {model: sale.config.settings, id: default_delivery_policy}:
- {}
--
- !python {model: sale.config.settings}: |
- self.execute(cr, uid, [ref('default_delivery_policy')], context=context)
diff --git a/addons/delivery/test/stock_move_values_with_invoice_before_delivery.yml b/addons/delivery/test/stock_move_values_with_invoice_before_delivery.yml
deleted file mode 100644
index 7a41665adf3..00000000000
--- a/addons/delivery/test/stock_move_values_with_invoice_before_delivery.yml
+++ /dev/null
@@ -1,73 +0,0 @@
--
- test that the store fields of stock moves are computed with invoice before delivery flow
--
- set a weight on ipod 16GB
--
- !python {model: product.product, id: product.product_product_11}: |
- self.write({'weight': 0.25})
--
- create a SO with invoice before delivery
--
- !record {model: sale.order, id: sale_prepaid}:
- partner_id: base.res_partner_18
- partner_invoice_id: base.res_partner_18
- partner_shipping_id: base.res_partner_18
- pricelist_id: product.list0
- order_line:
- - name: 'Ice Cream'
- product_id: stock.product_icecream
- product_uom_qty: 2
- product_uom: product.product_uom_kgm
- price_unit: 750.00
- carrier_id: normal_delivery_carrier
--
- I add delivery cost in Sale order.
--
- !python {model: sale.order, id: sale_prepaid}: |
- self.delivery_set()
--
- I confirm the SO.
--
- !python {model: sale.order}: |
- self.action_confirm(cr, uid, ref("sale_prepaid"), context=context)
- self.action_invoice_create(cr, uid, [ref("sale_prepaid")], context=context)
--
- I check that the invoice was created
--
- !python {model: sale.order, id: sale_prepaid}: |
- assert len(self.invoice_ids) == 1, "Invoice not created."
--
- I confirm the invoice
--
- !python {model: sale.order, id: sale_prepaid}: |
- invoice = self.invoice_ids[0]
- invoice.signal_workflow('invoice_open')
--
- I pay the invoice.
--
- !python {model: sale.order, id: sale_prepaid}: |
- invoice = self.invoice_ids[0]
- invoice.signal_workflow('invoice_open')
- journal = self.env['account.journal'].search([('type', '=', 'cash'),
- ('company_id', '=', self.company_id.id)],
- limit=1)
- invoice.pay_and_reconcile(journal, invoice.amount_total)
--
- Check the SO after paying the invoice
--
- !python {model: sale.order, id: sale_prepaid}: |
- assert self.invoice_count != 0, 'order not invoiced'
- assert self.invoice_status == 'invoiced', 'order is not invoiced'
- assert len(self.picking_ids) == 1, 'pickings not generated'
--
- check the stock moves
--
- !python {model: sale.order, id: sale_prepaid}: |
- move = self.picking_ids.move_lines
- assert move.product_qty == 2, 'wrong product_qty'
- assert move.weight == 2.0, 'wrong move weight'
--
- ship
--
- !python {model: sale.order, id: sale_prepaid}: |
- picking = self.picking_ids.action_done()
diff --git a/addons/delivery/tests/__init__.py b/addons/delivery/tests/__init__.py
new file mode 100644
index 00000000000..1c52d7dd4a2
--- /dev/null
+++ b/addons/delivery/tests/__init__.py
@@ -0,0 +1,4 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing detailsself.
+
+from . import test_delivery_cost, test_delivery_stock_move
diff --git a/addons/delivery/tests/test_delivery_cost.py b/addons/delivery/tests/test_delivery_cost.py
new file mode 100644
index 00000000000..0a83bfd7946
--- /dev/null
+++ b/addons/delivery/tests/test_delivery_cost.py
@@ -0,0 +1,125 @@
+# -*- coding: utf-8 -*-
+
+from openerp.tests import common
+from openerp.tools import float_compare
+
+
+class TestDeliveryCost(common.TransactionCase):
+
+ def setUp(self):
+ super(TestDeliveryCost, self).setUp()
+ self.SaleOrder = self.env['sale.order']
+ self.SaleOrderLine = self.env['sale.order.line']
+ self.AccountAccount = self.env['account.account']
+ self.SaleConfigSetting = self.env['sale.config.settings']
+ self.Product = self.env['product.product']
+
+ self.partner_18 = self.env.ref('base.res_partner_18')
+ self.pricelist = self.env.ref('product.list0')
+ self.product_4 = self.env.ref('product.product_product_4')
+ self.product_uom_unit = self.env.ref('product.product_uom_unit')
+ self.normal_delivery = self.env.ref('delivery.normal_delivery_carrier')
+ self.partner_4 = self.env.ref('base.res_partner_4')
+ self.partner_address_13 = self.env.ref('base.res_partner_address_13')
+ self.product_uom_hour = self.env.ref('product.product_uom_hour')
+ self.account_data = self.env.ref('account.data_account_type_revenue')
+ self.account_tag_operating = self.env.ref('account.account_tag_operating')
+ self.product_2 = self.env.ref('product.product_product_2')
+ self.product_category = self.env.ref('product.product_category_all')
+ self.free_delivery = self.env.ref('delivery.free_delivery_carrier')
+
+ def test_00_delivery_cost(self):
+ # In order to test Carrier Cost
+ # Create sale order with Normal Delivery Charges
+
+ self.sale_normal_delivery_charges = self.SaleOrder.create({
+ 'partner_id': self.partner_18.id,
+ 'partner_invoice_id': self.partner_18.id,
+ 'partner_shipping_id': self.partner_18.id,
+ 'pricelist_id': self.pricelist.id,
+ 'order_line': [(0, 0, {
+ 'name': 'PC Assamble + 2GB RAM',
+ 'product_id': self.product_4.id,
+ 'product_uom_qty': 1,
+ 'product_uom': self.product_uom_unit.id,
+ 'price_unit': 750.00,
+ })],
+ 'carrier_id': self.normal_delivery.id
+ })
+ # I add delivery cost in Sale order
+
+ self.a_sale = self.AccountAccount.create({
+ 'code': 'X2020',
+ 'name': 'Product Sales - (test)',
+ 'user_type_id': self.account_data.id,
+ 'tag_ids': [(6, 0, {
+ self.account_tag_operating.id
+ })]
+ })
+
+ self.product_consultant = self.Product.create({
+ 'sale_ok': True,
+ 'list_price': 75.0,
+ 'standard_price': 30.0,
+ 'uom_id': self.product_uom_hour.id,
+ 'uom_po_id': self.product_uom_hour.id,
+ 'name': 'Service',
+ 'categ_id': self.product_category.id,
+ 'type': 'service'
+ })
+
+ # I add delivery cost in Sale order
+ self.sale_normal_delivery_charges.delivery_set()
+
+ # I check sale order after added delivery cost
+
+ line = self.SaleOrderLine.search([('order_id', '=', self.sale_normal_delivery_charges.id),
+ ('product_id', '=', self.sale_normal_delivery_charges.carrier_id.product_id.id)])
+ self.assertEqual(len(line), 1, "Delivery cost is not Added")
+
+ self.assertEqual(float_compare(line.price_subtotal, 10, precision_digits=2), 0,
+ "Delivey cost is not correspond.")
+
+ # I confirm the sale order
+
+ self.sale_normal_delivery_charges.action_confirm()
+
+ # Create one more sale order with Free Delivery Charges
+
+ self.delivery_sale_order_cost = self.SaleOrder.create({
+ 'partner_id': self.partner_4.id,
+ 'partner_invoice_id': self.partner_address_13.id,
+ 'partner_shipping_id': self.partner_address_13.id,
+ 'pricelist_id': self.pricelist.id,
+ 'order_line': [(0, 0, {
+ 'name': 'Service on demand',
+ 'product_id': self.product_consultant.id,
+ 'product_uom_qty': 24,
+ 'product_uom': self.product_uom_hour.id,
+ 'price_unit': 75.00,
+ }), (0, 0, {
+ 'name': 'On Site Assistance',
+ 'product_id': self.product_2.id,
+ 'product_uom_qty': 30,
+ 'product_uom': self.product_uom_hour.id,
+ 'price_unit': 38.25,
+ })],
+ 'carrier_id': self.free_delivery.id
+ })
+
+ # I add free delivery cost in Sale order
+ self.delivery_sale_order_cost.delivery_set()
+
+ # I check sale order after adding delivery cost
+ line = self.SaleOrderLine.search([('order_id', '=', self.delivery_sale_order_cost.id),
+ ('product_id', '=', self.delivery_sale_order_cost.carrier_id.product_id.id)])
+
+ self.assertEqual(len(line), 1, "Delivery cost is not Added")
+ self.assertEqual(float_compare(line.price_subtotal, 0, precision_digits=2), 0,
+ "Delivey cost is not correspond.")
+
+ # I set default delivery policy
+
+ self.default_delivery_policy = self.SaleConfigSetting.create({})
+
+ self.default_delivery_policy.execute()
diff --git a/addons/delivery/tests/test_delivery_stock_move.py b/addons/delivery/tests/test_delivery_stock_move.py
new file mode 100644
index 00000000000..fb57a2ec1e5
--- /dev/null
+++ b/addons/delivery/tests/test_delivery_stock_move.py
@@ -0,0 +1,75 @@
+# -*- coding: utf-8 -*-
+
+from openerp.tests import common
+
+
+class StockMoveInvoice(common.TransactionCase):
+
+ def setUp(self):
+ super(StockMoveInvoice, self).setUp()
+ self.ProductProduct = self.env['product.product']
+ self.SaleOrder = self.env['sale.order']
+ self.AccountJournal = self.env['account.journal']
+
+ self.partner_18 = self.env.ref('base.res_partner_18')
+ self.pricelist_id = self.env.ref('product.list0')
+ self.product_11 = self.env.ref('product.product_product_11')
+ self.product_icecream = self.env.ref('stock.product_icecream')
+ self.product_uom_kgm = self.env.ref('product.product_uom_kgm')
+ self.normal_delivery = self.env.ref('delivery.normal_delivery_carrier')
+
+ def test_01_delivery_stock_move(self):
+ # Test if the stored fields of stock moves are computed with invoice before delivery flow
+ # Set a weight on ipod 16GB
+ self.product_11.write({
+ 'weight': 0.25,
+ })
+
+ self.sale_prepaid = self.SaleOrder.create({
+ 'partner_id': self.partner_18.id,
+ 'partner_invoice_id': self.partner_18.id,
+ 'partner_shipping_id': self.partner_18.id,
+ 'pricelist_id': self.pricelist_id.id,
+ 'order_line': [(0, 0, {
+ 'name': 'Ice Cream',
+ 'product_id': self.product_icecream.id,
+ 'product_uom_qty': 2,
+ 'product_uom': self.product_uom_kgm.id,
+ 'price_unit': 750.00,
+ })],
+ 'carrier_id': self.normal_delivery.id
+ })
+
+ # I add delivery cost in Sale order
+ self.sale_prepaid.delivery_set()
+
+ # I confirm the SO.
+ self.sale_prepaid.action_confirm()
+ self.sale_prepaid.action_invoice_create()
+
+ # I check that the invoice was created
+ self.assertEqual(len(self.sale_prepaid.invoice_ids), 1, "Invoice not created.")
+
+ # I confirm the invoice
+
+ self.invoice = self.sale_prepaid.invoice_ids
+ self.invoice.signal_workflow('invoice_open')
+
+ # I pay the invoice.
+ self.invoice = self.sale_prepaid.invoice_ids
+ self.invoice.signal_workflow('invoice_open')
+ self.journal = self.AccountJournal.search([('type', '=', 'cash'), ('company_id', '=', self.sale_prepaid.company_id.id)], limit=1)
+ self.invoice.pay_and_reconcile(self.journal, self.invoice.amount_total)
+
+ # Check the SO after paying the invoice
+ self.assertNotEqual(self.sale_prepaid.invoice_count, 0, 'order not invoiced')
+ self.assertTrue(self.sale_prepaid.invoice_status == 'invoiced', 'order is not invoiced')
+ self.assertEqual(len(self.sale_prepaid.picking_ids), 1, 'pickings not generated')
+
+ # Check the stock moves
+ moves = self.sale_prepaid.picking_ids.move_lines
+ self.assertEqual(moves[0].product_qty, 2, 'wrong product_qty')
+ self.assertEqual(moves[0].weight, 2.0, 'wrong move weight')
+
+ # Ship
+ self.picking = self.sale_prepaid.picking_ids.action_done()
diff --git a/addons/delivery/delivery_view.xml b/addons/delivery/views/delivery_view.xml
index ae20d85f7bf..6d7525be2b8 100644
--- a/addons/delivery/delivery_view.xml
+++ b/addons/delivery/views/delivery_view.xml
@@ -1,5 +1,5 @@
-<openerp>
- <data>
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
<!-- Delivery Carriers -->
<menuitem id="menu_delivery" name="Delivery" parent="stock.menu_stock_config_settings" groups="stock.group_stock_manager" sequence="50"/>
<menuitem id="sale_menu_delivery" name="Delivery" parent="base.menu_sale_config" groups="base.group_sale_manager" sequence="12"/>
@@ -12,7 +12,7 @@
<field name="sequence" widget="handle"/>
<field name="name"/>
<field name="partner_id"/>
- <field name="price" invisible="'order_id' not in context"/>
+ <field name="price"/>
</tree>
</field>
</record>
@@ -22,72 +22,59 @@
<field name="model">delivery.carrier</field>
<field name="arch" type="xml">
<form string="Carrier">
+ <header>
+ <field name="state" widget="statusbar" attrs="{'invisible':['|', ('delivery_type', '=', 'fixed'), ('delivery_type', '=', 'base_on_rule')]}" clickable="True"/>
+ </header>
<sheet>
- <div class="oe_title" name="title">
- <label for="name" class="oe_edit_only"/>
- <h1>
- <field name="name"/>
- </h1>
- </div>
- <group>
- <field name="active"/>
- </group>
- <group name="general">
- <group string="General Information">
- <field name="partner_id"/>
- <field name="product_id"/>
- <field name="delivery_type"/>
- </group>
- <group string="Pricing Information" name='pricing_info' attrs="{'invisible':[('delivery_type','!=','grid')]}">
- <field name="normal_price" attrs="{'readonly':[('use_detailed_pricelist', '=', True)]}"/>
- <label for="free_if_more_than"/>
- <div>
- <field name="free_if_more_than" attrs="{'readonly':[('use_detailed_pricelist', '=', True)]}"/>
- <field name="amount" attrs="{'required':[('free_if_more_than','&lt;&gt;',False)], 'invisible':[('free_if_more_than','=',False)]}"/>
- </div>
- <field name="use_detailed_pricelist"/>
- </group>
- </group>
-
- <field name="pricelist_ids" attrs="{'invisible':[('use_detailed_pricelist','=',False)]}" mode="tree">
- <tree string="Delivery grids">
- <field name="sequence"/>
- <field name="name"/>
- </tree>
- <form string="Delivery grids">
- <group>
+ <div class="oe_title" name="title">
+ <label for="name" string="Name" class="oe_edit_only"/>
+ <h1>
+ <field name="name"/>
+ </h1>
+ <group name="company">
+ <field name="partner_id" class="oe_inline"/>
+ <field name="product_type" invisible="1"/>
+ <field name="product_sale_ok" invisible="1"/>
+ </group>
+ </div>
+ <notebook>
+ <page string="Pricing">
<group>
- <field name="name"/>
+ <field name="delivery_type" widget="radio"/>
</group>
- <group>
- <field name="active"/>
- <field name="sequence"/>
+ <group attrs="{'invisible':[('delivery_type', '!=', 'fixed')]}">
+ <group>
+ <field name="fixed_price"/>
+ </group>
+ <group>
+ <div class="o_row">
+ <label for="free_if_more_than"/>
+ <field name="free_if_more_than"/>
+ <field nolabel="1" name="amount" attrs="{'required':[('free_if_more_than','!=', False)], 'invisible':[('free_if_more_than','=', False)]}"/>
+ </div>
+ </group>
+ </group>
+ <group name="general" attrs="{'invisible':[('delivery_type', '!=', 'base_on_rule')]}">
+ <field name="price_rule_ids" nolabel="1"/>
</group>
- </group>
- <notebook>
- <page string="Grid definition">
- <field name="line_ids"/>
- </page>
- <page string="Destination">
+ </page>
+ <page string="Destination">
+ <group>
<group>
- <group>
- <field name="country_ids" widget="many2many_tags"/>
- <field name="state_ids" widget="many2many_tags"/>
- </group>
- <group>
- <label for="zip_from" string="Zip"/>
- <div>
- <field name="zip_from" class="oe_inline"/>
- -
- <field name="zip_to" class="oe_inline"/>
- </div>
- </group>
+ <field name="country_ids" widget="many2many_tags"/>
+ <field name="state_ids" widget="many2many_tags"/>
</group>
- </page>
- </notebook>
- </form>
- </field>
- </sheet>
+ <group></group>
+ <group>
+ <field name="zip_from"/>
+ </group>
+ <group>
+ <field name="zip_to"/>
+ </group>
+ </group>
+ </page>
+ </notebook>
+ </sheet>
</form>
</field>
</record>
@@ -116,83 +103,20 @@
<menuitem action="action_delivery_carrier_form" id="menu_action_delivery_carrier_form" parent="menu_delivery" sequence="1"/>
<menuitem action="action_delivery_carrier_form" id="sale_menu_action_delivery_carrier_form" parent="sale_menu_delivery" sequence="1"/>
- <!-- Delivery Grids -->
- <record id="view_delivery_grid_tree" model="ir.ui.view">
- <field name="name">delivery.grid.tree</field>
- <field name="model">delivery.grid</field>
- <field name="arch" type="xml">
- <tree string="Delivery grids">
- <field name="sequence"/>
- <field name="carrier_id"/>
- <field name="name"/>
- </tree>
- </field>
- </record>
-
- <record id="view_delivery_grid_form" model="ir.ui.view">
- <field name="name">delivery.grid.form</field>
- <field name="model">delivery.grid</field>
- <field name="arch" type="xml">
- <form string="Delivery grids">
- <group col="4">
- <field name="name"/>
- <field name="active"/>
- <field name="carrier_id"/>
- <field name="sequence"/>
- </group>
- <notebook>
- <page string="Grid definition">
- <field name="line_ids"/>
- </page>
- <page string="Destination">
- <group string="Countries">
- <field name="country_ids" nolabel="1"/>
- </group>
- <group string="States">
- <field colspan="2" name="state_ids" nolabel="1"/>
- <field name="zip_from"/>
- <field name="zip_to"/>
- </group>
- </page>
- </notebook>
- </form>
- </field>
- </record>
- <record id="action_delivery_grid_form" model="ir.actions.act_window">
- <field name="name">Delivery Pricelist</field>
- <field name="type">ir.actions.act_window</field>
- <field name="res_model">delivery.grid</field>
- <field name="view_type">form</field>
- <field name="view_mode">tree,form</field>
- <field name="help" type="html">
- <p class="oe_view_nocontent_create">
- Click to create a delivery price list for a specific region.
- </p><p>
- The delivery price list allows you to compute the cost and
- sales price of the delivery according to the weight of the
- products and other criteria. You can define several price lists
- for each delivery method: per country or a zone in a specific
- country defined by a postal code range.
- </p>
- </field>
- </record>
- <menuitem action="action_delivery_grid_form" id="menu_action_delivery_grid_form" parent="menu_delivery" sequence="2"/>
- <menuitem action="action_delivery_grid_form" id="sale_menu_action_delivery_grid_form" parent="sale_menu_delivery" sequence="2"/>
-
- <record id="view_delivery_grid_line_form" model="ir.ui.view">
- <field name="name">delivery.grid.line.form</field>
- <field name="model">delivery.grid.line</field>
+ <record id="view_delivery_price_rule_form" model="ir.ui.view">
+ <field name="name">delivery.price.rule.form</field>
+ <field name="model">delivery.price.rule</field>
<field name="arch" type="xml">
- <form string="Grid Lines">
+ <form string="Price Rules">
<group>
- <field name="name"/>
+ <field name="name" invisible="1"/>
</group>
<group>
- <label for="type" string="Condition"/>
- <div>
- <field name="type" class="oe_inline"/>
- <field name="operator" class="oe_inline"/>
- <field name="max_value" class="oe_inline"/>
+ <label for="variable" string="Condition"/>
+ <div class="o_row">
+ <field name="variable"/>
+ <field name="operator"/>
+ <field name="max_value"/>
</div>
<label for="list_base_price" string="Sale Price"/>
<div>
@@ -202,23 +126,19 @@
*
<field name="variable_factor" class="oe_inline"/>
</div>
- <field name="standard_price"/>
+ <field name="standard_price" invisible="1"/>
</group>
</form>
</field>
</record>
- <record id="view_delivery_grid_line_tree" model="ir.ui.view">
- <field name="name">delivery.grid.line.tree</field>
- <field name="model">delivery.grid.line</field>
+ <record id="view_delivery_price_rule_tree" model="ir.ui.view">
+ <field name="name">delivery.price.rule.tree</field>
+ <field name="model">delivery.price.rule</field>
<field name="arch" type="xml">
- <tree string="Grid Lines">
+ <tree string="Price Rules">
<field name="sequence" widget="handle" />
<field name="name"/>
- <field name="type"/>
- <field name="operator"/>
- <field name="max_value" nolabel="1"/>
- <field name="list_price"/>
- <field name="standard_price"/>
+ <field name="list_base_price" string="Sale Price"/>
</tree>
</field>
</record>
@@ -238,7 +158,7 @@
<label for="carrier_tracking_ref"/>
<div>
<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),('delivery_type','=','grid'),('delivery_type','=',False),('state','not in',('done'))]}"/>
+ <button type='object' class="fa fa-arrow-right oe_link" name="cancel_shipment" string="Cancel" attrs="{'invisible':['|','|','|',('carrier_tracking_ref','=',False),('delivery_type','in', ['fixed', 'base_on_rule']),('delivery_type','=',False),('state','not in',('done'))]}"/>
</div>
<field name="carrier_price"/>
</group>
@@ -254,11 +174,11 @@
</xpath>
<xpath expr="//h1" position="before">
<div class="oe_right oe_button_box">
- <button type="object" name="open_website_url" class="oe_stat_button" icon='fa-truck' string="Tracking" attrs="{'invisible': ['|',('carrier_tracking_ref','=',False),('delivery_type','=','grid')]}" />
+ <button type="object" name="open_website_url" class="oe_stat_button" icon='fa-truck' string="Tracking" attrs="{'invisible': ['|',('carrier_tracking_ref','=',False),('delivery_type','in', ['fixed', 'base_on_rule'])]}" />
</div>
</xpath>
<xpath expr="/form/header/button[last()]" position="after">
- <button name="send_to_shipper" string="Send to Shipper" type="object" attrs="{'invisible':['|','|','|',('carrier_tracking_ref','!=',False),('delivery_type','=','grid'),('delivery_type','=',False),('state','not in',('done'))]}"/>
+ <button name="send_to_shipper" string="Send to Shipper" type="object" attrs="{'invisible':['|','|','|',('carrier_tracking_ref','!=',False),('delivery_type','in', ['fixed', 'base_on_rule']),('delivery_type','=',False),('state','not in',('done'))]}"/>
</xpath>
</data>
</field>
@@ -327,7 +247,7 @@
<label for="carrier_tracking_ref"/>
<div>
<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),('delivery_type','=','grid'),('delivery_type','=',False),('state','not in',('done'))]}"/>
+ <button type='object' class="fa fa-arrow-right oe_link" name="cancel_shipment" string="Cancel" attrs="{'invisible':['|','|','|',('carrier_tracking_ref','=',False),('delivery_type','in', ['fixed', 'base_on_rule']),('delivery_type','=',False),('state','not in',('done'))]}"/>
</div>
<field name="carrier_price"/>
</group>
@@ -335,5 +255,4 @@
</field>
</record>
- </data>
-</openerp>
+</odoo>
diff --git a/addons/delivery/partner_view.xml b/addons/delivery/views/partner_view.xml
index e9960fee6f8..7d708a65e70 100644
--- a/addons/delivery/partner_view.xml
+++ b/addons/delivery/views/partner_view.xml
@@ -1,5 +1,4 @@
-<openerp>
- <data>
+<odoo>
<record id="view_partner_property_form" model="ir.ui.view">
<field name="name">res.partner.carrier.property.form.inherit</field>
@@ -7,10 +6,9 @@
<field name="inherit_id" ref="mail.view_emails_partner_info_form" />
<field name="arch" type="xml">
<field name="opt_out" position="after">
- <field name="property_delivery_carrier"/>
+ <field name="property_delivery_carrier_id"/>
</field>
</field>
</record>
- </data>
-</openerp>
+</odoo>
diff --git a/addons/delivery/views/report_shipping.xml b/addons/delivery/views/report_shipping.xml
index 7259668edb7..7743d13f87e 100644
--- a/addons/delivery/views/report_shipping.xml
+++ b/addons/delivery/views/report_shipping.xml
@@ -1,5 +1,4 @@
-<openerp>
- <data>
+<odoo>
<template id="report_shipping2" inherit_id="stock.report_picking">
<xpath expr="//div[@name='partner_header']" position="after">
<div name="invoice_partner" t-if="o.picking_type_id.code == 'outgoing' and o.sale_id and o.sale_id.partner_invoice_id">
@@ -21,5 +20,4 @@
</td>
</xpath>
</template>
- </data>
-</openerp>
+</odoo>
diff --git a/addons/website_sale/static/src/js/website_sale.js b/addons/website_sale/static/src/js/website_sale.js
index 6805beab280..52c58c09012 100644
--- a/addons/website_sale/static/src/js/website_sale.js
+++ b/addons/website_sale/static/src/js/website_sale.js
@@ -333,18 +333,18 @@ $('.oe_website_sale').each(function () {
});
$(oe_website_sale).on('change', "select[name='country_id']", function () {
- var $select = $("select[name='state_id']");
+ var $select = $("select[name='state_id']:enabled");
$select.find("option:not(:first)").hide();
var nb = $select.find("option[data-country_id="+($(this).val() || 0)+"]").show().size();
- $select.parent().toggle(nb>1);
+ $select.parent().toggle(nb>=1);
});
$(oe_website_sale).find("select[name='country_id']").change();
$(oe_website_sale).on('change', "select[name='shipping_country_id']", function () {
- var $select = $("select[name='shipping_state_id']");
+ var $select = $("select[name='shipping_state_id']:enabled");
$select.find("option:not(:first)").hide();
var nb = $select.find("option[data-country_id="+($(this).val() || 0)+"]").show().size();
- $select.parent().toggle(nb>1);
+ $select.parent().toggle(nb>=1);
});
$(oe_website_sale).find("select[name='shipping_country_id']").change();
});
diff --git a/addons/website_sale_delivery/__openerp__.py b/addons/website_sale_delivery/__openerp__.py
index f843d0deac5..c05689e506f 100644
--- a/addons/website_sale_delivery/__openerp__.py
+++ b/addons/website_sale_delivery/__openerp__.py
@@ -13,6 +13,7 @@ Delivery Costs
'views/website_sale_delivery.xml',
'views/website_sale_delivery_view.xml',
'security/ir.model.access.csv',
+ 'demo/website_sale_delivery_data.xml'
],
'demo': [
'data/website_sale_delivery_demo.xml'
diff --git a/addons/website_sale_delivery/controllers/main.py b/addons/website_sale_delivery/controllers/main.py
index 15bc881c364..42b6214d128 100644
--- a/addons/website_sale_delivery/controllers/main.py
+++ b/addons/website_sale_delivery/controllers/main.py
@@ -26,3 +26,7 @@ class website_sale(openerp.addons.website_sale.controllers.main.website_sale):
""" Transforms a list of order lines into a dict for google analytics """
order_lines_not_delivery = [line for line in order_lines if not line.is_delivery]
return super(website_sale, self).order_lines_2_google_api(order_lines_not_delivery)
+
+ def checkout_values(self, data=None):
+ values = super(website_sale, self).checkout_values(data)
+ return request.env['sale.order']._get_shipping_country(values)
diff --git a/addons/website_sale_delivery/demo/website_sale_delivery_data.xml b/addons/website_sale_delivery/demo/website_sale_delivery_data.xml
new file mode 100644
index 00000000000..d77522880d0
--- /dev/null
+++ b/addons/website_sale_delivery/demo/website_sale_delivery_data.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+ <record id="delivery.free_delivery_carrier" model="delivery.carrier">
+ <field name="website_published" eval="True"/>
+ </record>
+</odoo>
diff --git a/addons/website_sale_delivery/models/sale_order.py b/addons/website_sale_delivery/models/sale_order.py
index 7416bfc8007..d8fbeb38f05 100644
--- a/addons/website_sale_delivery/models/sale_order.py
+++ b/addons/website_sale_delivery/models/sale_order.py
@@ -12,7 +12,7 @@ class delivery_carrier(orm.Model):
_inherit = ['delivery.carrier', 'website.published.mixin']
_columns = {
- 'website_description': fields.text('Description for Online Quotations'),
+ 'website_description': fields.related('product_id', 'description_sale', type="text", string='Description for Online Quotations'),
}
_defaults = {
'website_published': False
@@ -91,8 +91,8 @@ class SaleOrder(orm.Model):
carrier_ids.insert(0, carrier_id)
if force_carrier_id or not carrier_id or not carrier_id in carrier_ids:
for delivery_id in carrier_ids:
- grid_id = carrier_obj.grid_get(cr, SUPERUSER_ID, [delivery_id], order.partner_shipping_id.id)
- if grid_id:
+ carrier = carrier_obj.verify_carrier(cr, SUPERUSER_ID, [delivery_id], order.partner_shipping_id)
+ if carrier:
carrier_id = delivery_id
break
order.write({'carrier_id': carrier_id})
@@ -157,3 +157,25 @@ class SaleOrder(orm.Model):
self._check_carrier_quotation(cr, uid, sale_order, context=context)
return values
+
+ def _get_shipping_country(self, cr, uid, values, context=None):
+ country_ids = set()
+ state_ids = set()
+ values['shipping_countries'] = values['countries']
+ values['shipping_states'] = values['states']
+
+ DeliveryCarrier = self.pool['delivery.carrier']
+ delivery_carrier_ids = DeliveryCarrier.search(cr, SUPERUSER_ID, [('website_published', '=', True)], context=context)
+ for carrier in DeliveryCarrier.browse(cr, SUPERUSER_ID, delivery_carrier_ids, context):
+ if not carrier.country_ids and not carrier.state_ids:
+ return values
+ # Authorized shipping countries
+ country_ids = country_ids|set(carrier.country_ids.ids)
+ # Authorized shipping countries without any state restriction
+ state_country_ids = [country.id for country in carrier.country_ids if country.id not in carrier.state_ids.mapped('country_id.id')]
+ # Authorized shipping states + all states from shipping countries without any state restriction
+ state_ids = state_ids|set(carrier.state_ids.ids)|set(values['states'].filtered(lambda r: r.country_id.id in state_country_ids).ids)
+
+ values['shipping_countries'] = values['countries'].filtered(lambda r: r.id in list(country_ids))
+ values['shipping_states'] = values['states'].filtered(lambda r: r.id in list(state_ids))
+ return values
diff --git a/addons/website_sale_delivery/static/src/js/website_sale_delivery.js b/addons/website_sale_delivery/static/src/js/website_sale_delivery.js
index d18b6516383..46caab18e0e 100644
--- a/addons/website_sale_delivery/static/src/js/website_sale_delivery.js
+++ b/addons/website_sale_delivery/static/src/js/website_sale_delivery.js
@@ -7,4 +7,19 @@ $(document).ready(function () {
window.location.href = '/shop/payment?carrier_id=' + carrier_id;
});
+ $(".oe_website_sale select[name='shipping_id']").on('change', function () {
+ var value = $(this).val();
+ var $provider_free = $("select[name='country_id']:not(.o_provider_restricted), select[name='state_id']:not(.o_provider_restricted)");
+ var $provider_restricted = $("select[name='country_id'].o_provider_restricted, select[name='state_id'].o_provider_restricted");
+ if (value == 0) {
+ // Ship to the same address : only show shipping countries available for billing
+ $provider_free.hide().attr('disabled', true);
+ $provider_restricted.show().attr('disabled', false).change();
+ } else {
+ // Create a new address : show all countries available for billing
+ $provider_free.show().attr('disabled', false).change();
+ $provider_restricted.hide().attr('disabled', true);
+ }
+ });
+
});
diff --git a/addons/website_sale_delivery/views/website_sale_delivery.xml b/addons/website_sale_delivery/views/website_sale_delivery.xml
index 0c33577cea3..9708cd0d335 100644
--- a/addons/website_sale_delivery/views/website_sale_delivery.xml
+++ b/addons/website_sale_delivery/views/website_sale_delivery.xml
@@ -51,5 +51,39 @@
</xpath>
</template>
+ <template id="country_state_shipping" name="Shipping Country and states" inherit_id="website_sale.checkout">
+ <!-- By default, with 'ship to the same address', we can only ship to restricted countries & states -->
+ <xpath expr="//select[@name='country_id']" position="attributes">
+ <attribute name="t-att-disabled">not shipping_id and 'disabled' or ''</attribute>
+ <attribute name="t-att-style">not shipping_id and 'display: none' or ''</attribute>
+ </xpath>
+ <xpath expr="//select[@name='country_id']" position="after">
+ <select name="country_id" class="form-control o_provider_restricted" t-att-style="shipping_id and 'display:none' or ''" t-att-disabled="shipping_id and 'disabled' or ''">
+ <option value="">Country...</option>
+ <t t-foreach="shipping_countries or []" t-as="country">
+ <option t-att-value="country.id" t-att-selected="country.id == checkout.get('country_id')"><t t-esc="country.name"/></option>
+ </t>
+ </select>
+ </xpath>
+ <xpath expr="//select[@name='state_id']" position="attributes">
+ <attribute name="t-att-disabled">not shipping_id and 'disabled' or ''</attribute>
+ <attribute name="t-att-style">not shipping_id and 'display: none' or ''</attribute>
+ </xpath>
+ <xpath expr="//select[@name='state_id']" position="after">
+ <select name="state_id" class="form-control o_provider_restricted" t-att-style="shipping_id and 'display:none' or ''" t-att-disabled="shipping_id and 'disabled' or ''">
+ <option value="">Select...</option>
+ <t t-foreach="shipping_states or []" t-as="state">
+ <option t-att-value="state.id" style="display:none;" t-att-data-country_id="state.country_id.id" t-att-selected="state.id == checkout.get('state_id')"><t t-esc="state.name"/></option>
+ </t>
+ </select>
+ </xpath>
+ <xpath expr="//select[@name='shipping_country_id']/t[@t-foreach]" position="attributes">
+ <attribute name="t-foreach">shipping_countries</attribute>
+ </xpath>
+ <xpath expr="//select[@name='shipping_state_id']/t[@t-foreach]" position="attributes">
+ <attribute name="t-foreach">shipping_states</attribute>
+ </xpath>
+ </template>
+
</data>
</openerp>
diff --git a/addons/website_sale_delivery/views/website_sale_delivery_view.xml b/addons/website_sale_delivery/views/website_sale_delivery_view.xml
index 58f26f7d39e..12a48b96f85 100644
--- a/addons/website_sale_delivery/views/website_sale_delivery_view.xml
+++ b/addons/website_sale_delivery/views/website_sale_delivery_view.xml
@@ -13,11 +13,11 @@
</button>
</div>
</div>
- <group name="general" position="after">
- <group name="website">
- <field name="website_description" attrs="{'invisible':[('website_published', '=', False)]}"/>
+ <div name="title" position="after">
+ <group>
+ <field name="website_description" string="Description" placeholder="Description displayed on the eCommerce and on the Online Quotations."/>
</group>
- </group>
+ </div>
</field>
</record>
</odoo>