summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAntony Lesuisse <al@openerp.com>2014-06-29 23:31:16 +0200
committerAntony Lesuisse <al@openerp.com>2014-06-30 02:12:41 +0200
commit22f4c315a3ee4916d476fceea2a6d5a5248c8a2d (patch)
tree470e60bcad1a17c0954b3db4faab3e173eb9e51d
parent5209fbc7ed9fcad966ab064654a8a8697142be42 (diff)
[IMP] automatic fiscal positions for simple cases
Add group of countries res.country.group Add get_fiscal_position method a method to compute a fiscal position based on company_id, partner_id, delivery_id The meaning of res.partner.fiscal_position is now a forced a fiscal position. The default implementation should handle simple cases, like VAT in UE and sales tax in the US, but the method can be overriden to handle more complex ficals rules.
-rw-r--r--addons/account/partner.py34
-rw-r--r--addons/account/partner_view.xml8
-rw-r--r--addons/base_vat/base_vat.py4
-rw-r--r--addons/sale/sale.py23
-rw-r--r--addons/sale/sale_view.xml2
-rw-r--r--addons/website_sale/controllers/main.py7
-rw-r--r--addons/website_sale/models/sale_order.py24
-rw-r--r--openerp/addons/base/res/res_country.py8
-rw-r--r--openerp/addons/base/res/res_country_data.xml13
-rw-r--r--openerp/addons/base/res/res_country_view.xml32
-rw-r--r--openerp/addons/base/security/ir.model.access.csv2
11 files changed, 135 insertions, 22 deletions
diff --git a/addons/account/partner.py b/addons/account/partner.py
index d9bb6083f43..70f5280b861 100644
--- a/addons/account/partner.py
+++ b/addons/account/partner.py
@@ -27,13 +27,19 @@ from openerp.osv import fields, osv
class account_fiscal_position(osv.osv):
_name = 'account.fiscal.position'
_description = 'Fiscal Position'
+ _order = 'sequence'
_columns = {
+ 'sequence': fields.integer('Sequence'),
'name': fields.char('Fiscal Position', required=True),
'active': fields.boolean('Active', help="By unchecking the active field, you may hide a fiscal position without deleting it."),
'company_id': fields.many2one('res.company', 'Company'),
'account_ids': fields.one2many('account.fiscal.position.account', 'position_id', 'Account Mapping'),
'tax_ids': fields.one2many('account.fiscal.position.tax', 'position_id', 'Tax Mapping'),
'note': fields.text('Notes'),
+ 'auto_apply': fields.boolean('Automatic', help="Apply automatically this fiscal position."),
+ 'vat_required': fields.boolean('VAT required', help="Apply only if partner has a VAT number."),
+ 'country_id': fields.many2one('res.country', 'Countries', help="Apply only if delivery or invoicing country match."),
+ 'country_group_id': fields.many2one('res.country.group', 'Country Group', help="Apply only if delivery or invocing country match the group."),
}
_defaults = {
@@ -66,6 +72,33 @@ class account_fiscal_position(osv.osv):
break
return account_id
+ def get_fiscal_position(self, cr, uid, company_id, partner_id, delivery_id=None, context=None):
+ if not partner_id:
+ return False
+ # This can be easily overriden to apply more complex fiscal rules
+ part_obj = self.pool['res.partner']
+ partner = part_obj.browse(cr, uid, partner_id, context=context)
+
+ # partner manually set fiscal position always win
+ if partner.property_account_position:
+ return part.property_account_position.id
+
+ # if no delivery use invocing
+ if delivery_id:
+ delivery = part_obj.browse(cr, uid, delivery_id, context=context)
+ else:
+ delivery = partner
+
+ domain = [
+ ('auto_apply', '=', True),
+ '|', ('vat_required', '=', False), ('vat_required', '=', partner.vat_subjected),
+ '|', ('country_id', '=', None), ('country_id', '=', delivery.country_id.id),
+ '|', ('country_group_id', '=', None), ('country_group_id.country_ids', '=', delivery.country_id.id)
+ ]
+ fiscal_position_ids = self.search(cr, uid, domain, context=context)
+ if fiscal_position_ids:
+ return fiscal_position_ids[0]
+ return False
class account_fiscal_position_tax(osv.osv):
_name = 'account.fiscal.position.tax'
@@ -206,6 +239,7 @@ class res_partner(osv.osv):
return self.write(cr, uid, ids, {'last_reconciliation_date': time.strftime('%Y-%m-%d %H:%M:%S')}, context=context)
_columns = {
+ 'vat_subjected': fields.boolean('VAT Legal Statement', help="Check this box if the partner is subjected to the VAT. It will be used for the VAT legal statement."),
'credit': fields.function(_credit_debit_get,
fnct_search=_credit_search, string='Total Receivable', multi='dc', help="Total amount this customer owes you."),
'debit': fields.function(_credit_debit_get, fnct_search=_debit_search, string='Total Payable', multi='dc', help="Total amount you have to pay to this supplier."),
diff --git a/addons/account/partner_view.xml b/addons/account/partner_view.xml
index c2a01c7e40a..309296c7955 100644
--- a/addons/account/partner_view.xml
+++ b/addons/account/partner_view.xml
@@ -12,6 +12,13 @@
<field name="active"/>
<field name="company_id" widget="selection" groups="base.group_multi_company"/>
</group>
+ <group>
+ <field name="auto_apply"/>
+ <field name="sequence"/>
+ <field name="vat_required" attrs="{'readonly': [('auto_apply', '=', False)]}"/>
+ <field name="country_id" attrs="{'readonly': ['|', ('country_group_id','!=',False), ('auto_apply', '=', False)]}" />
+ <field name="country_group_id" attrs="{'readonly': ['|', ('country_id','!=',False), ('auto_apply', '=', False)]}"/>
+ </group>
<separator string="Taxes Mapping"/>
<field name="tax_ids" widget="one2many_list">
<tree string="Tax Mapping" editable="bottom">
@@ -44,6 +51,7 @@
<field name="model">account.fiscal.position</field>
<field name="arch" type="xml">
<tree string="Fiscal Position">
+ <field name="sequence"/>
<field name="name"/>
<field name="company_id" groups="base.group_multi_company" widget="selection"/>
</tree>
diff --git a/addons/base_vat/base_vat.py b/addons/base_vat/base_vat.py
index 9b3862508b0..b7ac9592dd3 100644
--- a/addons/base_vat/base_vat.py
+++ b/addons/base_vat/base_vat.py
@@ -133,10 +133,6 @@ class res_partner(osv.osv):
def vat_change(self, cr, uid, ids, value, context=None):
return {'value': {'vat_subjected': bool(value)}}
- _columns = {
- 'vat_subjected': fields.boolean('VAT Legal Statement', help="Check this box if the partner is subjected to the VAT. It will be used for the VAT legal statement.")
- }
-
def _commercial_fields(self, cr, uid, context=None):
return super(res_partner, self)._commercial_fields(cr, uid, context=context) + ['vat_subjected']
diff --git a/addons/sale/sale.py b/addons/sale/sale.py
index 87ad2d80be0..5e44ad5b0f4 100644
--- a/addons/sale/sale.py
+++ b/addons/sale/sale.py
@@ -320,6 +320,16 @@ class sale_order(osv.osv):
context_lang.update({'lang': partner_lang})
return self.pool.get('res.users').browse(cr, uid, uid, context=context_lang).company_id.sale_note
+ def onchange_delivery_id(self, cr, uid, ids, company_id, partner_id, delivery_id, fiscal_position, context=None):
+ r = {'value': {}}
+ if not fiscal_position:
+ if not company_id:
+ company_id = self._get_default_company(cr, uid, context=context)
+ fiscal_position = self.pool['account.fiscal.position'].get_fiscal_position(cr, uid, company_id, partner_id, delivery_id, context=context)
+ if fiscal_position:
+ r['value']['fiscal_position'] = fiscal_position
+ return r
+
def onchange_partner_id(self, cr, uid, ids, part, context=None):
if not part:
return {'value': {'partner_invoice_id': False, 'partner_shipping_id': False, 'payment_term': False, 'fiscal_position': False}}
@@ -328,15 +338,15 @@ class sale_order(osv.osv):
addr = self.pool.get('res.partner').address_get(cr, uid, [part.id], ['delivery', 'invoice', 'contact'])
pricelist = part.property_product_pricelist and part.property_product_pricelist.id or False
payment_term = part.property_payment_term and part.property_payment_term.id or False
- fiscal_position = part.property_account_position and part.property_account_position.id or False
dedicated_salesman = part.user_id and part.user_id.id or uid
val = {
'partner_invoice_id': addr['invoice'],
'partner_shipping_id': addr['delivery'],
'payment_term': payment_term,
- 'fiscal_position': fiscal_position,
'user_id': dedicated_salesman,
}
+ delivery_onchange = self.onchange_delivery_id(cr, uid, ids, False, part.id, addr['delivery'], False, context=context)
+ val.update(delivery_onchange['value'])
if pricelist:
val['pricelist_id'] = pricelist
sale_note = self.get_salenote(cr, uid, ids, part.id, context=context)
@@ -345,11 +355,14 @@ class sale_order(osv.osv):
def create(self, cr, uid, vals, context=None):
if context is None:
- context = {}
+ context = {}
if vals.get('name', '/') == '/':
vals['name'] = self.pool.get('ir.sequence').get(cr, uid, 'sale.order') or '/'
- if vals.get('partner_id') and any(f not in vals for f in ['partner_invoice_id', 'partner_shipping_id', 'pricelist_id']):
- defaults = self.onchange_partner_id(cr, uid, [], vals['partner_id'], context)['value']
+ if vals.get('partner_id') and any(f not in vals for f in ['partner_invoice_id', 'partner_shipping_id', 'pricelist_id', 'fiscal_position']):
+ defaults = self.onchange_partner_id(cr, uid, [], vals['partner_id'], context=context)['value']
+ if not vals.get('fiscal_position') and vals.get('partner_shipping_id'):
+ delivery_onchange = self.onchange_delivery_id(cr, uid, [], vals.get('company_id'), None, vals['partner_id'], vals.get('partner_shipping_id'), context=context)
+ defaults.update(delivery_onchange['value'])
vals = dict(defaults, **vals)
context.update({'mail_create_nolog': True})
new_id = super(sale_order, self).create(cr, uid, vals, context=context)
diff --git a/addons/sale/sale_view.xml b/addons/sale/sale_view.xml
index d5d072c4513..f61c518918e 100644
--- a/addons/sale/sale_view.xml
+++ b/addons/sale/sale_view.xml
@@ -102,7 +102,7 @@
<group>
<field name="partner_id" on_change="onchange_partner_id(partner_id, context)" domain="[('customer','=',True)]" context="{'search_default_customer':1, 'show_address': 1}" options='{"always_reload": True}'/>
<field name="partner_invoice_id" groups="sale.group_delivery_invoice_address" context="{'default_type':'invoice'}"/>
- <field name="partner_shipping_id" groups="sale.group_delivery_invoice_address" context="{'default_type':'delivery'}"/>
+ <field name="partner_shipping_id" on_change="onchange_delivery_id(company_id, partner_id, partner_shipping_id, fiscal_position)" groups="sale.group_delivery_invoice_address" context="{'default_type':'delivery'}"/>
<field name="project_id" context="{'partner_id':partner_invoice_id, 'default_pricelist_id':pricelist_id, 'default_name':name, 'default_type': 'contract'}" groups="sale.group_analytic_accounting" domain="[('type','in',['view','normal','contract'])]"/>
</group>
<group>
diff --git a/addons/website_sale/controllers/main.py b/addons/website_sale/controllers/main.py
index f187e6799ac..656f581bc63 100644
--- a/addons/website_sale/controllers/main.py
+++ b/addons/website_sale/controllers/main.py
@@ -456,6 +456,7 @@ class website_sale(http.Controller):
if partner_id and request.website.partner_id.id != partner_id:
orm_partner.write(cr, SUPERUSER_ID, [partner_id], billing_info, context=context)
else:
+ # create partner
partner_id = orm_partner.create(cr, SUPERUSER_ID, billing_info, context=context)
# create a new shipping partner
@@ -472,7 +473,9 @@ class website_sale(http.Controller):
'partner_invoice_id': partner_id,
'partner_shipping_id': shipping_id or partner_id
}
- order_info.update(registry.get('sale.order').onchange_partner_id(cr, SUPERUSER_ID, [], partner_id, context=context)['value'])
+ order_info.update(order_obj.onchange_partner_id(cr, SUPERUSER_ID, [], partner_id, context=context)['value'])
+ order_info.update(order_obj.onchange_delivery_id(cr, SUPERUSER_ID, [], order.company_id.id, partner_id, shipping_id, None, context=context)['value'])
+
order_info.pop('user_id')
order_obj.write(cr, SUPERUSER_ID, [order.id], order_info, context=context)
@@ -512,6 +515,8 @@ class website_sale(http.Controller):
self.checkout_form_save(values["checkout"])
request.session['sale_last_order_id'] = order.id
+ request.website.sale_get_order(update_pricelist=True, context=context)
+
return request.redirect("/shop/payment")
#------------------------------------------------------
diff --git a/addons/website_sale/models/sale_order.py b/addons/website_sale/models/sale_order.py
index 165030f9f1a..7c0700a555a 100644
--- a/addons/website_sale/models/sale_order.py
+++ b/addons/website_sale/models/sale_order.py
@@ -128,7 +128,7 @@ class website(orm.Model):
def sale_product_domain(self, cr, uid, ids, context=None):
return [("sale_ok", "=", True)]
- def sale_get_order(self, cr, uid, ids, force_create=False, code=None, context=None):
+ def sale_get_order(self, cr, uid, ids, force_create=False, code=None, update_pricelist=None, context=None):
sale_order_obj = self.pool['sale.order']
sale_order_id = request.session.get('sale_order_id')
sale_order = None
@@ -157,26 +157,20 @@ class website(orm.Model):
request.session['sale_order_id'] = None
return None
- def update_pricelist(pricelist_id):
- values = {'pricelist_id': pricelist_id}
- values.update(sale_order.onchange_pricelist_id(pricelist_id, None)['value'])
- sale_order.write(values)
- for line in sale_order.order_line:
- sale_order._cart_update(product_id=line.product_id.id, add_qty=0)
-
# check for change of pricelist with a coupon
if code and code != sale_order.pricelist_id.code:
pricelist_ids = self.pool['product.pricelist'].search(cr, SUPERUSER_ID, [('code', '=', code)], context=context)
if pricelist_ids:
pricelist_id = pricelist_ids[0]
request.session['sale_order_code_pricelist_id'] = pricelist_id
- update_pricelist(pricelist_id)
+ update_pricelist = True
request.session['sale_order_code_pricelist_id'] = False
+ pricelist_id = request.session.get('sale_order_code_pricelist_id') or partner.property_product_pricelist.id
+
# check for change of partner_id ie after signup
if sale_order.partner_id.id != partner.id and request.website.partner_id.id != partner.id:
flag_pricelist = False
- pricelist_id = request.session.get('sale_order_code_pricelist_id') or partner.property_product_pricelist.id
if pricelist_id != sale_order.pricelist_id.id:
flag_pricelist = True
fiscal_position = sale_order.fiscal_position and sale_order.fiscal_position.id or False
@@ -190,7 +184,15 @@ class website(orm.Model):
sale_order_obj.write(cr, SUPERUSER_ID, [sale_order_id], values, context=context)
if flag_pricelist or values.get('fiscal_position') != fiscal_position:
- update_pricelist(pricelist_id)
+ update_pricelist = True
+
+ # update the pricelist
+ if update_pricelist:
+ values = {'pricelist_id': pricelist_id}
+ values.update(sale_order.onchange_pricelist_id(pricelist_id, None)['value'])
+ sale_order.write(values)
+ for line in sale_order.order_line:
+ sale_order._cart_update(product_id=line.product_id.id, add_qty=0)
# update browse record
if (code and code != sale_order.pricelist_id.code) or sale_order.partner_id.id != partner.id:
diff --git a/openerp/addons/base/res/res_country.py b/openerp/addons/base/res/res_country.py
index 10f82df2ee8..f086f1440e8 100644
--- a/openerp/addons/base/res/res_country.py
+++ b/openerp/addons/base/res/res_country.py
@@ -84,6 +84,14 @@ addresses belonging to this country.\n\nYou can use the python-style string pate
context=context)
+class CountryGroup(osv.osv):
+ _description="Country Group"
+ _name = 'res.country.group'
+ _columns = {
+ 'name': fields.char('Name', required=True),
+ 'country_ids': fields.many2many('res.country', string='Countries'),
+ }
+
class CountryState(osv.osv):
_description="Country state"
_name = 'res.country.state'
diff --git a/openerp/addons/base/res/res_country_data.xml b/openerp/addons/base/res/res_country_data.xml
index 633b4cc42c4..1bdd6a73460 100644
--- a/openerp/addons/base/res/res_country_data.xml
+++ b/openerp/addons/base/res/res_country_data.xml
@@ -1516,5 +1516,18 @@
<field name="image" type="base64" file="base/static/img/country_flags/zw.png"></field>
<field name="currency_id" ref="ZWD"/>
</record>
+
+
+
+ <record id="europe" model="res.country.group">
+ <field name="name">Europe</field>
+ <field name="country_ids" eval="[(6,0,[
+ ref('at'),ref('be'),ref('bg'),ref('hr'),ref('cy'),
+ ref('cz'),ref('dk'),ref('ee'),ref('fi'),ref('fr'),
+ ref('de'),ref('gr'),ref('hu'),ref('ie'),ref('it'),
+ ref('lv'),ref('lt'),ref('lu'),ref('mt'),ref('nl'),
+ ref('pl'),ref('pt'),ref('ro'),ref('sk'),ref('si'),
+ ref('es'),ref('se'),ref('uk')])]"/>
+ </record>
</data>
</openerp>
diff --git a/openerp/addons/base/res/res_country_view.xml b/openerp/addons/base/res/res_country_view.xml
index ad46235c5c6..f1316844dd7 100644
--- a/openerp/addons/base/res/res_country_view.xml
+++ b/openerp/addons/base/res/res_country_view.xml
@@ -50,6 +50,38 @@
<menuitem action="action_country" id="menu_country_partner" parent="menu_localisation" sequence="0" groups="base.group_no_one"/>
+ <record id="view_country_group_tree" model="ir.ui.view">
+ <field name="name">res.country.group.tree</field>
+ <field name="model">res.country.group</field>
+ <field name="arch" type="xml">
+ <tree string="Country Group">
+ <field name="name"/>
+ </tree>
+ </field>
+ </record>
+
+ <record id="view_country_group_form" model="ir.ui.view">
+ <field name="name">res.country.group.form</field>
+ <field name="model">res.country.group</field>
+ <field name="arch" type="xml">
+ <form string="Country Group">
+ <field name="name"/>
+ <field name="country_ids" widget="many2many_tags"/>
+ </form>
+ </field>
+ </record>
+
+ <record id="action_country_group" model="ir.actions.act_window">
+ <field name="name">Country Group</field>
+ <field name="type">ir.actions.act_window</field>
+ <field name="res_model">res.country.group</field>
+ <field name="view_type">form</field>
+ <field name="help">Display and manage the list of all countries group. You can create or delete country group to make sure the ones you are working on will be maintained.</field>
+ </record>
+
+
+ <menuitem action="action_country_group" id="menu_country_group" name="Country Group" parent="menu_localisation" sequence="1" groups="base.group_no_one"/>
+
<!--
State
-->
diff --git a/openerp/addons/base/security/ir.model.access.csv b/openerp/addons/base/security/ir.model.access.csv
index c70733b5140..bbf579486a4 100644
--- a/openerp/addons/base/security/ir.model.access.csv
+++ b/openerp/addons/base/security/ir.model.access.csv
@@ -45,8 +45,10 @@
"access_res_company_group_user","res_company group_user","model_res_company",,1,0,0,0
"access_res_country_group_all","res_country group_user_all","model_res_country",,1,0,0,0
"access_res_country_state_group_all","res_country_state group_user_all","model_res_country_state",,1,0,0,0
+"access_res_country_group_group_all","res_country_group group_user_all","model_res_country_group",,1,0,0,0
"access_res_country_group_user","res_country group_user","model_res_country","group_partner_manager",1,1,1,1
"access_res_country_state_group_user","res_country_state group_user","model_res_country_state","group_partner_manager",1,1,1,1
+"access_res_country_group_group_user","res_country_group group_user","model_res_country_group","group_partner_manager",1,1,1,1
"access_res_currency_group_all","res_currency group_all","model_res_currency",,1,0,0,0
"access_res_currency_rate_group_all","res_currency_rate group_all","model_res_currency_rate",,1,0,0,0
"access_res_currency_rate_type_group_all","res_currency_rate_type group_all","model_res_currency_rate_type",,1,0,0,0