[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_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 = {
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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" : "" }' >
|
||||
|
||||
Reference in New Issue
Block a user