[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:
Joren Van Onder
2015-09-28 09:00:21 +02:00
parent a208007502
commit 358e285fd8
5 changed files with 121 additions and 11 deletions
+27 -7
View File
@@ -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_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."),
'fiscal_position_ids': fields.many2many('account.fiscal.position', string='Fiscal Positions')
}
def _check_company_location(self, cr, uid, ids, context=None):
@@ -640,8 +641,10 @@ class pos_order(osv.osv):
_description = "Point of Sale"
_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)
if fiscal_position_id:
taxes = fiscal_position_id.map_tax(taxes)
price = line.price_unit * (1 - (line.discount or 0.0) / 100.0)
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']
@@ -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,
'pos_reference':ui_order['name'],
'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):
@@ -800,7 +804,7 @@ class pos_order(osv.osv):
res[order.id]['amount_paid'] += payment.amount
res[order.id]['amount_return'] += (payment.amount < 0 and payment.amount or 0)
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
res[order.id]['amount_tax'] = cur_obj.round(cr, uid, cur, val1)
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),
'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),
'fiscal_position_id': fields.many2one('account.fiscal.position', 'Fiscal Position')
}
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._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]
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
inv_line = invoice_line._convert_to_write(invoice_line._cache)
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')
for line in self.browse(cr, uid, ids, context=context):
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)
res[line.id]['price_subtotal'] = res[line.id]['price_subtotal_incl'] = price * line.qty
if taxes_ids:
@@ -1412,6 +1425,12 @@ class pos_order_line(osv.osv):
result['price_subtotal_incl'] = taxes['total_included']
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 = {
'company_id': fields.many2one('res.company', 'Company', required=True),
'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),
'price_unit': fields.float(string='Unit Price', 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_incl': fields.function(_amount_line_all, multi='pos_order_line_amount', digits=0, string='Subtotal', 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'),
'discount': fields.float('Discount (%)', digits=0),
'order_id': fields.many2one('pos.order', 'Order Ref', ondelete='cascade'),
'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 = {
+3 -1
View File
@@ -29,6 +29,7 @@
<field name="date_order"/>
<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="fiscal_position_id" options="{'no_create': True}"/>
</group>
<notebook colspan="4">
<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="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="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_incl" widget="monetary"/>
</tree>
@@ -661,6 +662,7 @@
<field name="group_by" groups="account.group_account_user"/>
<field name="barcode_nomenclature_id" />
<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"/>
</group>
<separator string="Available Payment Methods" colspan="4"/>
+55 -3
View File
@@ -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',
loaded: function(){
@@ -1269,6 +1303,22 @@ exports.Orderline = Backbone.Model.extend({
}
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) {
if (tax.amount_type === 'fixed') {
var ret = tax.amount * quantity;
@@ -1295,12 +1345,13 @@ exports.Orderline = Backbone.Model.extend({
currency_rounding = currency_rounding * 0.00001;
}
_(taxes).each(function(tax) {
tax = self._map_tax_fiscal_position(tax);
if (tax.amount_type === 'group'){
var ret = self.compute_all(tax.children_tax_ids, price_unit, quantity, currency_rounding);
total_excluded = ret.total_excluded;
base = ret.total_excluded;
total_included = ret.total_included;
list_taxes.concat(ret.taxes)
list_taxes.concat(ret.taxes);
}
else {
var tax_amount = self._compute_all(tax, price_unit, quantity);
@@ -1321,8 +1372,8 @@ exports.Orderline = Backbone.Model.extend({
id: tax.id,
amount: tax_amount,
name: tax.name,
}
list_taxes.push(data)
};
list_taxes.push(data);
}
}
});
@@ -1540,6 +1591,7 @@ exports.Order = Backbone.Model.extend({
uid: this.uid,
sequence_number: this.sequence_number,
creation_date: this.creation_date,
fiscal_position_id: this.fiscal_position ? this.fiscal_position.id : false
};
},
export_for_printing: function(){
@@ -1908,6 +1908,36 @@ var PaymentScreenWidget = ScreenWidget.extend({
});
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 {
ReceiptScreenWidget: ReceiptScreenWidget,
ActionButtonWidget: ActionButtonWidget,
@@ -100,6 +100,12 @@
</div>
</t>
<t t-name="SetFiscalPositionButton">
<div class='control-button'>
<i class='fa fa-book' /> Fiscal position
</div>
</t>
<t t-name="ActionpadWidget">
<div class="actionpad">
<button t-attf-class='button set-customer #{ ( widget.pos.get_client() and widget.pos.get_client().name.length > 10) ? "decentered" : "" }' >