332 lines
21 KiB
Python
332 lines
21 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
from odoo import api, fields, models
|
|
|
|
import logging
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
|
|
class ResConfigSettings(models.TransientModel):
|
|
"""
|
|
NOTES
|
|
1. Fields with name starting with 'pos_' are removed from the vals before super call to `create`.
|
|
Values of these fields are written to `pos_config_id` record after the super call.
|
|
This is done so that these fields are written at the same time to the active pos.config record.
|
|
2. During `creation` of this record, each related field is written to the source record
|
|
*one after the other*, so constraints on the source record that are based on multiple
|
|
fields might not work properly. However, only the *modified* related fields are written
|
|
to the source field. But the identification of modified fields happen during the super
|
|
call, not before `create` is called. Because of this, vals contains a lot of field before
|
|
super call, then the number of fields is reduced after.
|
|
"""
|
|
_inherit = 'res.config.settings'
|
|
|
|
def _default_pos_config(self):
|
|
# Default to the last modified pos.config.
|
|
active_model = self.env.context.get('active_model', '')
|
|
if active_model == 'pos.config':
|
|
return self.env.context.get('active_id')
|
|
return self.env['pos.config'].search([('company_id', '=', self.env.company.id)], order='write_date desc', limit=1)
|
|
|
|
pos_config_id = fields.Many2one('pos.config', string="Point of Sale", default=lambda self: self._default_pos_config())
|
|
sale_tax_id = fields.Many2one('account.tax', string="Default Sale Tax", related='company_id.account_sale_tax_id', readonly=False)
|
|
module_pos_mercury = fields.Boolean(string="Vantiv Payment Terminal", help="The transactions are processed by Vantiv. Set your Vantiv credentials on the related payment method.")
|
|
module_pos_adyen = fields.Boolean(string="Adyen Payment Terminal", help="The transactions are processed by Adyen. Set your Adyen credentials on the related payment method.")
|
|
module_pos_stripe = fields.Boolean(string="Stripe Payment Terminal", help="The transactions are processed by Stripe. Set your Stripe credentials on the related payment method.")
|
|
module_pos_six = fields.Boolean(string="Six Payment Terminal", help="The transactions are processed by Six. Set the IP address of the terminal on the related payment method.")
|
|
module_pos_paytm = fields.Boolean(string="PayTM Payment Terminal", help="The transactions are processed by PayTM. Set your PayTM credentials on the related payment method.")
|
|
module_pos_preparation_display = fields.Boolean(string="Preparation Display", help="Show orders on the preparation display screen.")
|
|
update_stock_quantities = fields.Selection(related="company_id.point_of_sale_update_stock_quantities", readonly=False)
|
|
account_default_pos_receivable_account_id = fields.Many2one(string='Default Account Receivable (PoS)', related='company_id.account_default_pos_receivable_account_id', readonly=False)
|
|
barcode_nomenclature_id = fields.Many2one('barcode.nomenclature', related='company_id.nomenclature_id', readonly=False)
|
|
is_kiosk_mode = fields.Boolean(string="Is Kiosk Mode", default=False)
|
|
|
|
# pos.config fields
|
|
pos_module_pos_discount = fields.Boolean(related='pos_config_id.module_pos_discount', readonly=False)
|
|
pos_module_pos_hr = fields.Boolean(related='pos_config_id.module_pos_hr', readonly=False)
|
|
pos_module_pos_restaurant = fields.Boolean(related='pos_config_id.module_pos_restaurant', readonly=False)
|
|
pos_is_order_printer = fields.Boolean(compute='_compute_pos_printer', store=True, readonly=False)
|
|
pos_printer_ids = fields.Many2many(related='pos_config_id.printer_ids', readonly=False)
|
|
|
|
pos_allowed_pricelist_ids = fields.Many2many('product.pricelist', compute='_compute_pos_allowed_pricelist_ids')
|
|
pos_amount_authorized_diff = fields.Float(related='pos_config_id.amount_authorized_diff', readonly=False)
|
|
pos_available_pricelist_ids = fields.Many2many('product.pricelist', string='Available Pricelists', compute='_compute_pos_pricelist_id', readonly=False, store=True)
|
|
pos_cash_control = fields.Boolean(related='pos_config_id.cash_control')
|
|
pos_cash_rounding = fields.Boolean(related='pos_config_id.cash_rounding', readonly=False, string="Cash Rounding (PoS)")
|
|
pos_company_has_template = fields.Boolean(related='pos_config_id.company_has_template')
|
|
pos_default_bill_ids = fields.Many2many(related='pos_config_id.default_bill_ids', readonly=False)
|
|
pos_default_fiscal_position_id = fields.Many2one('account.fiscal.position', string='Default Fiscal Position', compute='_compute_pos_fiscal_positions', readonly=False, store=True)
|
|
pos_fiscal_position_ids = fields.Many2many('account.fiscal.position', string='Fiscal Positions', compute='_compute_pos_fiscal_positions', readonly=False, store=True)
|
|
pos_has_active_session = fields.Boolean(related='pos_config_id.has_active_session')
|
|
pos_iface_available_categ_ids = fields.Many2many('pos.category', string='Available PoS Product Categories', compute='_compute_pos_iface_available_categ_ids', readonly=False, store=True)
|
|
pos_iface_big_scrollbars = fields.Boolean(related='pos_config_id.iface_big_scrollbars', readonly=False)
|
|
pos_iface_cashdrawer = fields.Boolean(string='Cashdrawer', compute='_compute_pos_iface_cashdrawer', readonly=False, store=True)
|
|
pos_iface_customer_facing_display_local = fields.Boolean(related='pos_config_id.iface_customer_facing_display_local', readonly=False)
|
|
pos_iface_customer_facing_display_via_proxy = fields.Boolean(string='Customer Facing Display', compute='_compute_pos_iface_customer_facing_display_via_proxy', readonly=False, store=True)
|
|
pos_iface_customer_facing_display_background_image_1920 = fields.Image(related='pos_config_id.iface_customer_facing_display_background_image_1920', readonly=False)
|
|
pos_iface_electronic_scale = fields.Boolean(string='Electronic Scale', compute='_compute_pos_iface_electronic_scale', readonly=False, store=True)
|
|
pos_iface_print_auto = fields.Boolean(related='pos_config_id.iface_print_auto', readonly=False)
|
|
pos_iface_print_skip_screen = fields.Boolean(related='pos_config_id.iface_print_skip_screen', readonly=False)
|
|
pos_iface_print_via_proxy = fields.Boolean(string='Print via Proxy', compute='_compute_pos_iface_print_via_proxy', readonly=False, store=True)
|
|
pos_iface_scan_via_proxy = fields.Boolean(string='Scan via Proxy', compute='_compute_pos_iface_scan_via_proxy', readonly=False, store=True)
|
|
pos_iface_start_categ_id = fields.Many2one('pos.category', string='Initial Category', compute='_compute_pos_iface_start_categ_id', readonly=False, store=True)
|
|
pos_iface_tax_included = fields.Selection(related='pos_config_id.iface_tax_included', readonly=False)
|
|
pos_iface_tipproduct = fields.Boolean(related='pos_config_id.iface_tipproduct', readonly=False)
|
|
pos_invoice_journal_id = fields.Many2one(related='pos_config_id.invoice_journal_id', readonly=False)
|
|
pos_is_header_or_footer = fields.Boolean(related='pos_config_id.is_header_or_footer', readonly=False)
|
|
pos_is_margins_costs_accessible_to_every_user = fields.Boolean(related='pos_config_id.is_margins_costs_accessible_to_every_user', readonly=False)
|
|
pos_is_posbox = fields.Boolean(related='pos_config_id.is_posbox', readonly=False)
|
|
pos_journal_id = fields.Many2one(related='pos_config_id.journal_id', readonly=False)
|
|
pos_limit_categories = fields.Boolean(related='pos_config_id.limit_categories', readonly=False)
|
|
pos_manual_discount = fields.Boolean(related='pos_config_id.manual_discount', readonly=False)
|
|
pos_only_round_cash_method = fields.Boolean(related='pos_config_id.only_round_cash_method', readonly=False)
|
|
pos_other_devices = fields.Boolean(related='pos_config_id.other_devices', readonly=False)
|
|
pos_payment_method_ids = fields.Many2many(related='pos_config_id.payment_method_ids', readonly=False)
|
|
pos_picking_policy = fields.Selection(related='pos_config_id.picking_policy', readonly=False)
|
|
pos_picking_type_id = fields.Many2one(related='pos_config_id.picking_type_id', readonly=False)
|
|
pos_pricelist_id = fields.Many2one('product.pricelist', string='Default Pricelist', compute='_compute_pos_pricelist_id', readonly=False, store=True)
|
|
pos_proxy_ip = fields.Char(string='IP Address', compute='_compute_pos_proxy_ip', readonly=False, store=True)
|
|
pos_receipt_footer = fields.Text(string='Receipt Footer', compute='_compute_pos_receipt_header_footer', readonly=False, store=True)
|
|
pos_receipt_header = fields.Text(string='Receipt Header', compute='_compute_pos_receipt_header_footer', readonly=False, store=True)
|
|
pos_restrict_price_control = fields.Boolean(related='pos_config_id.restrict_price_control', readonly=False)
|
|
pos_rounding_method = fields.Many2one(related='pos_config_id.rounding_method', readonly=False)
|
|
pos_route_id = fields.Many2one(related='pos_config_id.route_id', readonly=False)
|
|
pos_selectable_categ_ids = fields.Many2many('pos.category', compute='_compute_pos_selectable_categ_ids')
|
|
pos_sequence_id = fields.Many2one(related='pos_config_id.sequence_id')
|
|
pos_set_maximum_difference = fields.Boolean(related='pos_config_id.set_maximum_difference', readonly=False)
|
|
pos_ship_later = fields.Boolean(related='pos_config_id.ship_later', readonly=False)
|
|
pos_start_category = fields.Boolean(related='pos_config_id.start_category', readonly=False)
|
|
pos_tax_regime_selection = fields.Boolean(related='pos_config_id.tax_regime_selection', readonly=False)
|
|
pos_tip_product_id = fields.Many2one('product.product', string='Tip Product', compute='_compute_pos_tip_product_id', readonly=False, store=True)
|
|
pos_use_pricelist = fields.Boolean(related='pos_config_id.use_pricelist', readonly=False)
|
|
pos_warehouse_id = fields.Many2one(related='pos_config_id.warehouse_id', readonly=False, string="Warehouse (PoS)")
|
|
point_of_sale_use_ticket_qr_code = fields.Boolean(related='company_id.point_of_sale_use_ticket_qr_code', readonly=False)
|
|
pos_auto_validate_terminal_payment = fields.Boolean(related='pos_config_id.auto_validate_terminal_payment', readonly=False, string="Automatically validates orders paid with a payment terminal.")
|
|
pos_trusted_config_ids = fields.Many2many(related='pos_config_id.trusted_config_ids', readonly=False)
|
|
point_of_sale_ticket_unique_code = fields.Boolean(related='company_id.point_of_sale_ticket_unique_code', readonly=False)
|
|
|
|
@api.model_create_multi
|
|
def create(self, vals_list):
|
|
# STEP: Remove the 'pos' fields from each vals.
|
|
# They will be written atomically to `pos_config_id` after the super call.
|
|
pos_config_id_to_fields_vals_map = {}
|
|
|
|
for vals in vals_list:
|
|
pos_config_id = vals.get('pos_config_id')
|
|
if pos_config_id:
|
|
pos_fields_vals = {}
|
|
|
|
if vals.get('pos_cash_rounding'):
|
|
vals['group_cash_rounding'] = True
|
|
|
|
if vals.get('pos_use_pricelist'):
|
|
vals['group_product_pricelist'] = True
|
|
|
|
for field in self._fields.values():
|
|
if field.name == 'pos_config_id':
|
|
continue
|
|
|
|
val = vals.get(field.name)
|
|
|
|
# Add only to pos_fields_vals if
|
|
# 1. _field is in vals -- meaning, the _field is in view.
|
|
# 2. _field starts with 'pos_' -- meaning, the _field is a pos field.
|
|
if field.name.startswith('pos_') and val is not None:
|
|
pos_config_field_name = field.name[4:]
|
|
if not pos_config_field_name in self.env['pos.config']._fields:
|
|
_logger.warning("The value of '%s' is not properly saved to the pos_config_id field because the destination"
|
|
" field '%s' is not a valid field in the pos.config model.", field.name, pos_config_field_name)
|
|
else:
|
|
pos_fields_vals[pos_config_field_name] = val
|
|
del vals[field.name]
|
|
|
|
pos_config_id_to_fields_vals_map[pos_config_id] = pos_fields_vals
|
|
|
|
# STEP: Call super on the modified vals_list.
|
|
# NOTE: When creating `res.config.settings` records, it doesn't write on *unmodified* related fields.
|
|
result = super().create(vals_list)
|
|
|
|
# STEP: Finally, we write the value of 'pos' fields to 'pos_config_id'.
|
|
for pos_config_id, pos_fields_vals in pos_config_id_to_fields_vals_map.items():
|
|
pos_config = self.env['pos.config'].browse(pos_config_id)
|
|
pos_config.with_context(from_settings_view=True).write(pos_fields_vals)
|
|
|
|
return result
|
|
|
|
def set_values(self):
|
|
super(ResConfigSettings, self).set_values()
|
|
if not self.group_product_pricelist:
|
|
self.env['pos.config'].search([
|
|
('use_pricelist', '=', True)
|
|
]).use_pricelist = False
|
|
|
|
if not self.group_cash_rounding:
|
|
self.env['pos.config'].search([
|
|
('cash_rounding', '=', True)
|
|
]).cash_rounding = False
|
|
|
|
def action_pos_config_create_new(self):
|
|
return {
|
|
'view_mode': 'form',
|
|
'res_model': 'pos.config',
|
|
'type': 'ir.actions.act_window',
|
|
'target': 'new',
|
|
'res_id': False,
|
|
'context': {'pos_config_open_modal': True, 'pos_config_create_mode': True},
|
|
}
|
|
|
|
def pos_open_ui(self):
|
|
if self._context.get('pos_config_id'):
|
|
pos_config_id = self._context['pos_config_id']
|
|
pos_config = self.env['pos.config'].browse(pos_config_id)
|
|
return pos_config.open_ui()
|
|
|
|
@api.model
|
|
def _is_cashdrawer_displayed(self, res_config):
|
|
return res_config.pos_iface_print_via_proxy
|
|
|
|
@api.depends('pos_module_pos_restaurant', 'pos_config_id')
|
|
def _compute_pos_printer(self):
|
|
for res_config in self:
|
|
res_config.update({
|
|
'pos_is_order_printer': res_config.pos_config_id.is_order_printer,
|
|
})
|
|
|
|
@api.depends('pos_limit_categories', 'pos_config_id')
|
|
def _compute_pos_iface_available_categ_ids(self):
|
|
for res_config in self:
|
|
if not res_config.pos_limit_categories:
|
|
res_config.pos_iface_available_categ_ids = False
|
|
else:
|
|
res_config.pos_iface_available_categ_ids = res_config.pos_config_id.iface_available_categ_ids
|
|
|
|
@api.depends('pos_start_category', 'pos_config_id')
|
|
def _compute_pos_iface_start_categ_id(self):
|
|
for res_config in self:
|
|
if not res_config.pos_start_category:
|
|
res_config.pos_iface_start_categ_id = False
|
|
else:
|
|
res_config.pos_iface_start_categ_id = res_config.pos_config_id.iface_start_categ_id
|
|
|
|
@api.depends('pos_iface_available_categ_ids')
|
|
def _compute_pos_selectable_categ_ids(self):
|
|
for res_config in self:
|
|
if res_config.pos_iface_available_categ_ids:
|
|
res_config.pos_selectable_categ_ids = res_config.pos_iface_available_categ_ids
|
|
else:
|
|
res_config.pos_selectable_categ_ids = self.env['pos.category'].search([])
|
|
|
|
@api.depends('pos_iface_print_via_proxy', 'pos_config_id')
|
|
def _compute_pos_iface_cashdrawer(self):
|
|
for res_config in self:
|
|
if self._is_cashdrawer_displayed(res_config):
|
|
res_config.pos_iface_cashdrawer = res_config.pos_config_id.iface_cashdrawer
|
|
else:
|
|
res_config.pos_iface_cashdrawer = False
|
|
|
|
@api.depends('pos_is_header_or_footer', 'pos_config_id')
|
|
def _compute_pos_receipt_header_footer(self):
|
|
for res_config in self:
|
|
if res_config.pos_is_header_or_footer:
|
|
res_config.pos_receipt_header = res_config.pos_config_id.receipt_header
|
|
res_config.pos_receipt_footer = res_config.pos_config_id.receipt_footer
|
|
else:
|
|
res_config.pos_receipt_header = False
|
|
res_config.pos_receipt_footer = False
|
|
|
|
@api.depends('pos_tax_regime_selection', 'pos_config_id')
|
|
def _compute_pos_fiscal_positions(self):
|
|
for res_config in self:
|
|
if res_config.pos_tax_regime_selection:
|
|
res_config.pos_default_fiscal_position_id = res_config.pos_config_id.default_fiscal_position_id
|
|
res_config.pos_fiscal_position_ids = res_config.pos_config_id.fiscal_position_ids
|
|
else:
|
|
res_config.pos_default_fiscal_position_id = False
|
|
res_config.pos_fiscal_position_ids = [(5, 0, 0)]
|
|
|
|
@api.depends('pos_iface_tipproduct', 'pos_config_id')
|
|
def _compute_pos_tip_product_id(self):
|
|
for res_config in self:
|
|
if res_config.pos_iface_tipproduct:
|
|
res_config.pos_tip_product_id = res_config.pos_config_id.tip_product_id
|
|
else:
|
|
res_config.pos_tip_product_id = False
|
|
|
|
@api.depends('pos_use_pricelist', 'pos_config_id', 'pos_journal_id')
|
|
def _compute_pos_pricelist_id(self):
|
|
for res_config in self:
|
|
currency_id = res_config.pos_journal_id.currency_id.id if res_config.pos_journal_id.currency_id else res_config.pos_config_id.company_id.currency_id.id
|
|
pricelists_in_current_currency = self.env['product.pricelist'].search([
|
|
*self.env['product.pricelist']._check_company_domain(res_config.pos_config_id.company_id),
|
|
('currency_id', '=', currency_id),
|
|
])
|
|
if not res_config.pos_use_pricelist:
|
|
res_config.pos_pricelist_id = False
|
|
res_config.pos_available_pricelist_ids = res_config.pos_config_id.available_pricelist_ids
|
|
else:
|
|
if any([p.currency_id.id != currency_id for p in res_config.pos_available_pricelist_ids]):
|
|
res_config.pos_available_pricelist_ids = pricelists_in_current_currency
|
|
res_config.pos_pricelist_id = pricelists_in_current_currency[:1]
|
|
else:
|
|
res_config.pos_available_pricelist_ids = res_config.pos_config_id.available_pricelist_ids
|
|
res_config.pos_pricelist_id = res_config.pos_config_id.pricelist_id
|
|
|
|
@api.depends('pos_available_pricelist_ids', 'pos_use_pricelist')
|
|
def _compute_pos_allowed_pricelist_ids(self):
|
|
for res_config in self:
|
|
if res_config.pos_use_pricelist:
|
|
res_config.pos_allowed_pricelist_ids = res_config.pos_available_pricelist_ids.ids
|
|
else:
|
|
res_config.pos_allowed_pricelist_ids = self.env['product.pricelist'].search([]).ids
|
|
|
|
@api.depends('pos_is_posbox', 'pos_config_id')
|
|
def _compute_pos_proxy_ip(self):
|
|
for res_config in self:
|
|
if not res_config.pos_is_posbox:
|
|
res_config.pos_proxy_ip = False
|
|
else:
|
|
res_config.pos_proxy_ip = res_config.pos_config_id.proxy_ip
|
|
|
|
@api.depends('pos_is_posbox', 'pos_config_id')
|
|
def _compute_pos_iface_print_via_proxy(self):
|
|
for res_config in self:
|
|
if not res_config.pos_is_posbox:
|
|
res_config.pos_iface_print_via_proxy = False
|
|
else:
|
|
res_config.pos_iface_print_via_proxy = res_config.pos_config_id.iface_print_via_proxy
|
|
|
|
@api.depends('pos_is_posbox', 'pos_config_id')
|
|
def _compute_pos_iface_scan_via_proxy(self):
|
|
for res_config in self:
|
|
if not res_config.pos_is_posbox:
|
|
res_config.pos_iface_scan_via_proxy = False
|
|
else:
|
|
res_config.pos_iface_scan_via_proxy = res_config.pos_config_id.iface_scan_via_proxy
|
|
|
|
@api.depends('pos_is_posbox', 'pos_config_id')
|
|
def _compute_pos_iface_electronic_scale(self):
|
|
for res_config in self:
|
|
if not res_config.pos_is_posbox:
|
|
res_config.pos_iface_electronic_scale = False
|
|
else:
|
|
res_config.pos_iface_electronic_scale = res_config.pos_config_id.iface_electronic_scale
|
|
|
|
@api.depends('pos_is_posbox', 'pos_config_id')
|
|
def _compute_pos_iface_customer_facing_display_via_proxy(self):
|
|
for res_config in self:
|
|
if not res_config.pos_is_posbox:
|
|
res_config.pos_iface_customer_facing_display_via_proxy = False
|
|
else:
|
|
res_config.pos_iface_customer_facing_display_via_proxy = res_config.pos_config_id.iface_customer_facing_display_via_proxy
|
|
|
|
@api.onchange('pos_trusted_config_ids')
|
|
def _onchange_trusted_config_ids(self):
|
|
for config in self:
|
|
removed_trusted_configs = set(config.pos_config_id.trusted_config_ids.ids) - set(config.pos_trusted_config_ids.ids)
|
|
for old in config.pos_config_id.trusted_config_ids:
|
|
if config.pos_config_id.id not in old.trusted_config_ids.ids:
|
|
old._add_trusted_config_id(config.pos_config_id)
|
|
if old.id in removed_trusted_configs:
|
|
old._remove_trusted_config_id(config.pos_config_id)
|