[IMP] point_of_sale: fiscal position per pos_order
Allows a user to set multiple fiscal positions per pos_config. Orders created on that config can then be assigned one of these fiscal positions. In the POS this can be done through a button that will appear above the numpad when fiscal positions are set on the pos_config.
This commit is contained in:
@@ -126,6 +126,7 @@ class pos_config(osv.osv):
|
|||||||
'group_pos_manager_id': fields.many2one('res.groups','Point of Sale Manager Group', help='This field is there to pass the id of the pos manager group to the point of sale client'),
|
'group_pos_manager_id': fields.many2one('res.groups','Point of Sale Manager Group', help='This field is there to pass the id of the pos manager group to the point of sale client'),
|
||||||
'group_pos_user_id': fields.many2one('res.groups','Point of Sale User Group', help='This field is there to pass the id of the pos user group to the point of sale client'),
|
'group_pos_user_id': fields.many2one('res.groups','Point of Sale User Group', help='This field is there to pass the id of the pos user group to the point of sale client'),
|
||||||
'tip_product_id': fields.many2one('product.product','Tip Product', help="The product used to encode the customer tip. Leave empty if you do not accept tips."),
|
'tip_product_id': fields.many2one('product.product','Tip Product', help="The product used to encode the customer tip. Leave empty if you do not accept tips."),
|
||||||
|
'fiscal_position_ids': fields.many2many('account.fiscal.position', string='Fiscal Positions')
|
||||||
}
|
}
|
||||||
|
|
||||||
def _check_company_location(self, cr, uid, ids, context=None):
|
def _check_company_location(self, cr, uid, ids, context=None):
|
||||||
@@ -640,8 +641,10 @@ class pos_order(osv.osv):
|
|||||||
_description = "Point of Sale"
|
_description = "Point of Sale"
|
||||||
_order = "id desc"
|
_order = "id desc"
|
||||||
|
|
||||||
def _amount_line_tax(self, cr, uid, line, context=None):
|
def _amount_line_tax(self, cr, uid, line, fiscal_position_id, context=None):
|
||||||
taxes = line.product_id.taxes_id.filtered(lambda t: t.company_id.id == line.order_id.company_id.id)
|
taxes = line.product_id.taxes_id.filtered(lambda t: t.company_id.id == line.order_id.company_id.id)
|
||||||
|
if fiscal_position_id:
|
||||||
|
taxes = fiscal_position_id.map_tax(taxes)
|
||||||
price = line.price_unit * (1 - (line.discount or 0.0) / 100.0)
|
price = line.price_unit * (1 - (line.discount or 0.0) / 100.0)
|
||||||
cur = line.order_id.pricelist_id.currency_id
|
cur = line.order_id.pricelist_id.currency_id
|
||||||
taxes = taxes.compute_all(price, cur, line.qty, product=line.product_id, partner=line.order_id.partner_id or False)['taxes']
|
taxes = taxes.compute_all(price, cur, line.qty, product=line.product_id, partner=line.order_id.partner_id or False)['taxes']
|
||||||
@@ -659,7 +662,8 @@ class pos_order(osv.osv):
|
|||||||
'lines': [process_line(l) for l in ui_order['lines']] if ui_order['lines'] else False,
|
'lines': [process_line(l) for l in ui_order['lines']] if ui_order['lines'] else False,
|
||||||
'pos_reference':ui_order['name'],
|
'pos_reference':ui_order['name'],
|
||||||
'partner_id': ui_order['partner_id'] or False,
|
'partner_id': ui_order['partner_id'] or False,
|
||||||
'date_order': ui_order['creation_date']
|
'date_order': ui_order['creation_date'],
|
||||||
|
'fiscal_position_id': ui_order['fiscal_position_id']
|
||||||
}
|
}
|
||||||
|
|
||||||
def _payment_fields(self, cr, uid, ui_paymentline, context=None):
|
def _payment_fields(self, cr, uid, ui_paymentline, context=None):
|
||||||
@@ -800,7 +804,7 @@ class pos_order(osv.osv):
|
|||||||
res[order.id]['amount_paid'] += payment.amount
|
res[order.id]['amount_paid'] += payment.amount
|
||||||
res[order.id]['amount_return'] += (payment.amount < 0 and payment.amount or 0)
|
res[order.id]['amount_return'] += (payment.amount < 0 and payment.amount or 0)
|
||||||
for line in order.lines:
|
for line in order.lines:
|
||||||
val1 += self._amount_line_tax(cr, uid, line, context=context)
|
val1 += self._amount_line_tax(cr, uid, line, order.fiscal_position_id, context=context)
|
||||||
val2 += line.price_subtotal
|
val2 += line.price_subtotal
|
||||||
res[order.id]['amount_tax'] = cur_obj.round(cr, uid, cur, val1)
|
res[order.id]['amount_tax'] = cur_obj.round(cr, uid, cur, val1)
|
||||||
amount_untaxed = cur_obj.round(cr, uid, cur, val2)
|
amount_untaxed = cur_obj.round(cr, uid, cur, val2)
|
||||||
@@ -845,6 +849,7 @@ class pos_order(osv.osv):
|
|||||||
'nb_print': fields.integer('Number of Print', readonly=True, copy=False),
|
'nb_print': fields.integer('Number of Print', readonly=True, copy=False),
|
||||||
'pos_reference': fields.char('Receipt Ref', readonly=True, copy=False),
|
'pos_reference': fields.char('Receipt Ref', readonly=True, copy=False),
|
||||||
'sale_journal': fields.related('session_id', 'config_id', 'journal_id', relation='account.journal', type='many2one', string='Sale Journal', store=True, readonly=True),
|
'sale_journal': fields.related('session_id', 'config_id', 'journal_id', relation='account.journal', type='many2one', string='Sale Journal', store=True, readonly=True),
|
||||||
|
'fiscal_position_id': fields.many2one('account.fiscal.position', 'Fiscal Position')
|
||||||
}
|
}
|
||||||
|
|
||||||
def _default_session(self, cr, uid, context=None):
|
def _default_session(self, cr, uid, context=None):
|
||||||
@@ -1127,6 +1132,10 @@ class pos_order(osv.osv):
|
|||||||
invoice_line = inv_line_ref.new(cr, SUPERUSER_ID, inv_line, context=local_context)
|
invoice_line = inv_line_ref.new(cr, SUPERUSER_ID, inv_line, context=local_context)
|
||||||
invoice_line._onchange_product_id()
|
invoice_line._onchange_product_id()
|
||||||
invoice_line.invoice_line_tax_ids = [tax.id for tax in invoice_line.invoice_line_tax_ids if tax.company_id.id == company_id]
|
invoice_line.invoice_line_tax_ids = [tax.id for tax in invoice_line.invoice_line_tax_ids if tax.company_id.id == company_id]
|
||||||
|
fiscal_position_id = line.order_id.fiscal_position_id
|
||||||
|
if fiscal_position_id:
|
||||||
|
invoice_line.invoice_line_tax_ids = fiscal_position_id.map_tax(invoice_line.invoice_line_tax_ids)
|
||||||
|
invoice_line.invoice_line_tax_ids = [tax.id for tax in invoice_line.invoice_line_tax_ids]
|
||||||
# We convert a new id object back to a dictionary to write to bridge between old and new api
|
# We convert a new id object back to a dictionary to write to bridge between old and new api
|
||||||
inv_line = invoice_line._convert_to_write(invoice_line._cache)
|
inv_line = invoice_line._convert_to_write(invoice_line._cache)
|
||||||
inv_line.update(price_unit=line.price_unit, discount=line.discount)
|
inv_line.update(price_unit=line.price_unit, discount=line.discount)
|
||||||
@@ -1367,7 +1376,11 @@ class pos_order_line(osv.osv):
|
|||||||
account_tax_obj = self.pool.get('account.tax')
|
account_tax_obj = self.pool.get('account.tax')
|
||||||
for line in self.browse(cr, uid, ids, context=context):
|
for line in self.browse(cr, uid, ids, context=context):
|
||||||
cur = line.order_id.pricelist_id.currency_id
|
cur = line.order_id.pricelist_id.currency_id
|
||||||
taxes_ids = [ tax.id for tax in line.product_id.taxes_id if tax.company_id.id == line.order_id.company_id.id ]
|
taxes = [ tax for tax in line.product_id.taxes_id if tax.company_id.id == line.order_id.company_id.id ]
|
||||||
|
fiscal_position_id = line.order_id.fiscal_position_id
|
||||||
|
if fiscal_position_id:
|
||||||
|
taxes = fiscal_position_id.map_tax(taxes)
|
||||||
|
taxes_ids = [ tax.id for tax in taxes ]
|
||||||
price = line.price_unit * (1 - (line.discount or 0.0) / 100.0)
|
price = line.price_unit * (1 - (line.discount or 0.0) / 100.0)
|
||||||
res[line.id]['price_subtotal'] = res[line.id]['price_subtotal_incl'] = price * line.qty
|
res[line.id]['price_subtotal'] = res[line.id]['price_subtotal_incl'] = price * line.qty
|
||||||
if taxes_ids:
|
if taxes_ids:
|
||||||
@@ -1412,6 +1425,12 @@ class pos_order_line(osv.osv):
|
|||||||
result['price_subtotal_incl'] = taxes['total_included']
|
result['price_subtotal_incl'] = taxes['total_included']
|
||||||
return {'value': result}
|
return {'value': result}
|
||||||
|
|
||||||
|
def _get_tax_ids_after_fiscal_position(self, cr, uid, ids, field_name, args, context=None):
|
||||||
|
res = dict.fromkeys(ids, False)
|
||||||
|
for line in self.browse(cr, uid, ids, context=context):
|
||||||
|
res[line.id] = line.order_id.fiscal_position_id.map_tax(line.tax_ids)
|
||||||
|
return res
|
||||||
|
|
||||||
_columns = {
|
_columns = {
|
||||||
'company_id': fields.many2one('res.company', 'Company', required=True),
|
'company_id': fields.many2one('res.company', 'Company', required=True),
|
||||||
'name': fields.char('Line No', required=True, copy=False),
|
'name': fields.char('Line No', required=True, copy=False),
|
||||||
@@ -1419,12 +1438,13 @@ class pos_order_line(osv.osv):
|
|||||||
'product_id': fields.many2one('product.product', 'Product', domain=[('sale_ok', '=', True)], required=True, change_default=True),
|
'product_id': fields.many2one('product.product', 'Product', domain=[('sale_ok', '=', True)], required=True, change_default=True),
|
||||||
'price_unit': fields.float(string='Unit Price', digits=0),
|
'price_unit': fields.float(string='Unit Price', digits=0),
|
||||||
'qty': fields.float('Quantity', digits=0),
|
'qty': fields.float('Quantity', digits=0),
|
||||||
'price_subtotal': fields.function(_amount_line_all, multi='pos_order_line_amount', digits=0, string='Subtotal w/o Tax', store=True),
|
'price_subtotal': fields.function(_amount_line_all, multi='pos_order_line_amount', digits=0, string='Subtotal w/o Tax'),
|
||||||
'price_subtotal_incl': fields.function(_amount_line_all, multi='pos_order_line_amount', digits=0, string='Subtotal', store=True),
|
'price_subtotal_incl': fields.function(_amount_line_all, multi='pos_order_line_amount', digits=0, string='Subtotal'),
|
||||||
'discount': fields.float('Discount (%)', digits=0),
|
'discount': fields.float('Discount (%)', digits=0),
|
||||||
'order_id': fields.many2one('pos.order', 'Order Ref', ondelete='cascade'),
|
'order_id': fields.many2one('pos.order', 'Order Ref', ondelete='cascade'),
|
||||||
'create_date': fields.datetime('Creation Date', readonly=True),
|
'create_date': fields.datetime('Creation Date', readonly=True),
|
||||||
'tax_ids': fields.many2many('account.tax', string='Taxes', readonly=True),
|
'tax_ids': fields.many2many('account.tax', string='Taxes'),
|
||||||
|
'tax_ids_after_fiscal_position': fields.function(_get_tax_ids_after_fiscal_position, type='many2many', relation='account.tax', string='Taxes')
|
||||||
}
|
}
|
||||||
|
|
||||||
_defaults = {
|
_defaults = {
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
<field name="date_order"/>
|
<field name="date_order"/>
|
||||||
<field name="session_id" />
|
<field name="session_id" />
|
||||||
<field name="partner_id" on_change="onchange_partner_id(partner_id, context)" domain="[('customer', '=', True)]" context="{'search_default_customer':1}" attrs="{'readonly': [('state','=','invoiced')]}"/>
|
<field name="partner_id" on_change="onchange_partner_id(partner_id, context)" domain="[('customer', '=', True)]" context="{'search_default_customer':1}" attrs="{'readonly': [('state','=','invoiced')]}"/>
|
||||||
|
<field name="fiscal_position_id" options="{'no_create': True}"/>
|
||||||
</group>
|
</group>
|
||||||
<notebook colspan="4">
|
<notebook colspan="4">
|
||||||
<page string="Products">
|
<page string="Products">
|
||||||
@@ -38,7 +39,7 @@
|
|||||||
<field name="qty" on_change="onchange_qty(parent.pricelist_id,product_id, discount, qty, price_unit, context)"/>
|
<field name="qty" on_change="onchange_qty(parent.pricelist_id,product_id, discount, qty, price_unit, context)"/>
|
||||||
<field name="price_unit" on_change="onchange_qty(parent.pricelist_id,product_id, discount, qty, price_unit, context)" widget="monetary"/>
|
<field name="price_unit" on_change="onchange_qty(parent.pricelist_id,product_id, discount, qty, price_unit, context)" widget="monetary"/>
|
||||||
<field name="discount" on_change="onchange_qty(parent.pricelist_id,product_id, discount, qty, price_unit, context)" widget="monetary"/>
|
<field name="discount" on_change="onchange_qty(parent.pricelist_id,product_id, discount, qty, price_unit, context)" widget="monetary"/>
|
||||||
<field name="tax_ids" widget="many2many_tags"/>
|
<field name="tax_ids_after_fiscal_position" widget="many2many_tags"/>
|
||||||
<field name="price_subtotal" widget="monetary"/>
|
<field name="price_subtotal" widget="monetary"/>
|
||||||
<field name="price_subtotal_incl" widget="monetary"/>
|
<field name="price_subtotal_incl" widget="monetary"/>
|
||||||
</tree>
|
</tree>
|
||||||
@@ -661,6 +662,7 @@
|
|||||||
<field name="group_by" groups="account.group_account_user"/>
|
<field name="group_by" groups="account.group_account_user"/>
|
||||||
<field name="barcode_nomenclature_id" />
|
<field name="barcode_nomenclature_id" />
|
||||||
<field name="sequence_id" readonly="1" groups="base.group_no_one"/>
|
<field name="sequence_id" readonly="1" groups="base.group_no_one"/>
|
||||||
|
<field name="fiscal_position_ids" widget="many2many_tags" options="{'no_create': True}"/>
|
||||||
<field name="currency_id" invisible="1"/>
|
<field name="currency_id" invisible="1"/>
|
||||||
</group>
|
</group>
|
||||||
<separator string="Available Payment Methods" colspan="4"/>
|
<separator string="Available Payment Methods" colspan="4"/>
|
||||||
|
|||||||
@@ -371,6 +371,40 @@ exports.PosModel = Backbone.Model.extend({
|
|||||||
});
|
});
|
||||||
|
|
||||||
},
|
},
|
||||||
|
}, {
|
||||||
|
model: 'account.fiscal.position',
|
||||||
|
fields: [],
|
||||||
|
domain: function(self){ return [['id','in',self.config.fiscal_position_ids]]; },
|
||||||
|
loaded: function(self, fiscal_positions){
|
||||||
|
self.fiscal_positions = fiscal_positions;
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
model: 'account.fiscal.position.tax',
|
||||||
|
fields: [],
|
||||||
|
domain: function(self){
|
||||||
|
var fiscal_position_tax_ids = [];
|
||||||
|
|
||||||
|
self.fiscal_positions.forEach(function (fiscal_position) {
|
||||||
|
fiscal_position.tax_ids.forEach(function (tax_id) {
|
||||||
|
fiscal_position_tax_ids.push(tax_id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return [['id','in',fiscal_position_tax_ids]];
|
||||||
|
},
|
||||||
|
loaded: function(self, fiscal_position_taxes){
|
||||||
|
self.fiscal_position_taxes = fiscal_position_taxes;
|
||||||
|
self.fiscal_positions.forEach(function (fiscal_position) {
|
||||||
|
fiscal_position.fiscal_position_taxes_by_id = {};
|
||||||
|
fiscal_position.tax_ids.forEach(function (tax_id) {
|
||||||
|
var fiscal_position_tax = _.find(fiscal_position_taxes, function (fiscal_position_tax) {
|
||||||
|
return fiscal_position_tax.id === tax_id;
|
||||||
|
});
|
||||||
|
|
||||||
|
fiscal_position.fiscal_position_taxes_by_id[fiscal_position_tax.id] = fiscal_position_tax;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
}, {
|
}, {
|
||||||
label: 'fonts',
|
label: 'fonts',
|
||||||
loaded: function(){
|
loaded: function(){
|
||||||
@@ -1269,6 +1303,22 @@ exports.Orderline = Backbone.Model.extend({
|
|||||||
}
|
}
|
||||||
return taxes;
|
return taxes;
|
||||||
},
|
},
|
||||||
|
_map_tax_fiscal_position: function(tax) {
|
||||||
|
var current_order = this.pos.get_order();
|
||||||
|
var order_fiscal_position = current_order && current_order.fiscal_position;
|
||||||
|
|
||||||
|
if (order_fiscal_position) {
|
||||||
|
var mapped_tax = _.find(order_fiscal_position.fiscal_position_taxes_by_id, function (fiscal_position_tax) {
|
||||||
|
return fiscal_position_tax.tax_src_id[0] === tax.id;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (mapped_tax) {
|
||||||
|
tax = this.pos.taxes_by_id[mapped_tax.tax_dest_id[0]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tax;
|
||||||
|
},
|
||||||
_compute_all: function(tax, base_amount, quantity) {
|
_compute_all: function(tax, base_amount, quantity) {
|
||||||
if (tax.amount_type === 'fixed') {
|
if (tax.amount_type === 'fixed') {
|
||||||
var ret = tax.amount * quantity;
|
var ret = tax.amount * quantity;
|
||||||
@@ -1295,12 +1345,13 @@ exports.Orderline = Backbone.Model.extend({
|
|||||||
currency_rounding = currency_rounding * 0.00001;
|
currency_rounding = currency_rounding * 0.00001;
|
||||||
}
|
}
|
||||||
_(taxes).each(function(tax) {
|
_(taxes).each(function(tax) {
|
||||||
|
tax = self._map_tax_fiscal_position(tax);
|
||||||
if (tax.amount_type === 'group'){
|
if (tax.amount_type === 'group'){
|
||||||
var ret = self.compute_all(tax.children_tax_ids, price_unit, quantity, currency_rounding);
|
var ret = self.compute_all(tax.children_tax_ids, price_unit, quantity, currency_rounding);
|
||||||
total_excluded = ret.total_excluded;
|
total_excluded = ret.total_excluded;
|
||||||
base = ret.total_excluded;
|
base = ret.total_excluded;
|
||||||
total_included = ret.total_included;
|
total_included = ret.total_included;
|
||||||
list_taxes.concat(ret.taxes)
|
list_taxes.concat(ret.taxes);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
var tax_amount = self._compute_all(tax, price_unit, quantity);
|
var tax_amount = self._compute_all(tax, price_unit, quantity);
|
||||||
@@ -1321,8 +1372,8 @@ exports.Orderline = Backbone.Model.extend({
|
|||||||
id: tax.id,
|
id: tax.id,
|
||||||
amount: tax_amount,
|
amount: tax_amount,
|
||||||
name: tax.name,
|
name: tax.name,
|
||||||
}
|
};
|
||||||
list_taxes.push(data)
|
list_taxes.push(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -1540,6 +1591,7 @@ exports.Order = Backbone.Model.extend({
|
|||||||
uid: this.uid,
|
uid: this.uid,
|
||||||
sequence_number: this.sequence_number,
|
sequence_number: this.sequence_number,
|
||||||
creation_date: this.creation_date,
|
creation_date: this.creation_date,
|
||||||
|
fiscal_position_id: this.fiscal_position ? this.fiscal_position.id : false
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
export_for_printing: function(){
|
export_for_printing: function(){
|
||||||
|
|||||||
@@ -1908,6 +1908,36 @@ var PaymentScreenWidget = ScreenWidget.extend({
|
|||||||
});
|
});
|
||||||
gui.define_screen({name:'payment', widget: PaymentScreenWidget});
|
gui.define_screen({name:'payment', widget: PaymentScreenWidget});
|
||||||
|
|
||||||
|
var set_fiscal_position_button = ActionButtonWidget.extend({
|
||||||
|
template: 'SetFiscalPositionButton',
|
||||||
|
button_click: function () {
|
||||||
|
var self = this;
|
||||||
|
var selection_list = _.map(self.pos.fiscal_positions, function (fiscal_position) {
|
||||||
|
return {
|
||||||
|
label: fiscal_position.name,
|
||||||
|
item: fiscal_position
|
||||||
|
};
|
||||||
|
});
|
||||||
|
self.gui.show_popup('selection',{
|
||||||
|
title: _t('Select Fiscal Position'),
|
||||||
|
list: selection_list,
|
||||||
|
confirm: function (fiscal_position) {
|
||||||
|
var order = self.pos.get_order();
|
||||||
|
order.fiscal_position = fiscal_position;
|
||||||
|
order.trigger('change');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
define_action_button({
|
||||||
|
'name': 'set_fiscal_position',
|
||||||
|
'widget': set_fiscal_position_button,
|
||||||
|
'condition': function(){
|
||||||
|
return this.pos.fiscal_positions.length > 0;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
ReceiptScreenWidget: ReceiptScreenWidget,
|
ReceiptScreenWidget: ReceiptScreenWidget,
|
||||||
ActionButtonWidget: ActionButtonWidget,
|
ActionButtonWidget: ActionButtonWidget,
|
||||||
|
|||||||
@@ -100,6 +100,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</t>
|
</t>
|
||||||
|
|
||||||
|
<t t-name="SetFiscalPositionButton">
|
||||||
|
<div class='control-button'>
|
||||||
|
<i class='fa fa-book' /> Fiscal position
|
||||||
|
</div>
|
||||||
|
</t>
|
||||||
|
|
||||||
<t t-name="ActionpadWidget">
|
<t t-name="ActionpadWidget">
|
||||||
<div class="actionpad">
|
<div class="actionpad">
|
||||||
<button t-attf-class='button set-customer #{ ( widget.pos.get_client() and widget.pos.get_client().name.length > 10) ? "decentered" : "" }' >
|
<button t-attf-class='button set-customer #{ ( widget.pos.get_client() and widget.pos.get_client().name.length > 10) ? "decentered" : "" }' >
|
||||||
|
|||||||
Reference in New Issue
Block a user