Browse Source

Nov 4 [UPDT] Updated 'service_charges_pos'

pull/347/head
AjmalCybro 6 months ago
parent
commit
6be5ae82ab
  1. 3
      service_charges_pos/__manifest__.py
  2. 8
      service_charges_pos/doc/RELEASE_NOTES.md
  3. 98
      service_charges_pos/models/pos_config.py
  4. 36
      service_charges_pos/models/pos_session.py
  5. 75
      service_charges_pos/models/res_config_settings.py
  6. 10
      service_charges_pos/static/src/js/pos_load_data.js
  7. 132
      service_charges_pos/static/src/js/service_charge_button.js
  8. 10
      service_charges_pos/static/src/xml/ServiceChargeButton.xml
  9. 12
      service_charges_pos/static/src/xml/service_charge_button.xml
  10. 26
      service_charges_pos/views/pos_config_views.xml
  11. 50
      service_charges_pos/views/res_config_settings_views.xml

3
service_charges_pos/__manifest__.py

@ -37,9 +37,8 @@
],
'assets': {
'point_of_sale._assets_pos': [
'service_charges_pos/static/src/js/pos_load_data.js',
'service_charges_pos/static/src/js/service_charge_button.js',
'service_charges_pos/static/src/xml/ServiceChargeButton.xml',
'service_charges_pos/static/src/xml/service_charge_button.xml',
],
},
'images': ['static/description/banner.png'],

8
service_charges_pos/doc/RELEASE_NOTES.md

@ -1,5 +1,5 @@
## Module <service_charges_pos>
#### 18.01.2024
#### Version 17.0.1.0.0
#### ADD
- Initial commit for Service Charges POS
#### 02.11.2024
#### Version 17.0.1.0.1
#### FIX
Bug fixes for Service Charges POS

98
service_charges_pos/models/pos_config.py

@ -19,56 +19,70 @@
# If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
from odoo import api, fields, models
from odoo import api, fields, models, _
from odoo.exceptions import UserError
from odoo.osv.expression import OR
class PosConfig(models.Model):
"""Inherited POS Configuration"""
"""
To inherit model pos.config
"""
_inherit = 'pos.config'
is_session = fields.Boolean(string="Session",
compute='_compute_check_session',
help="Check it is for sessions", )
is_service_charges = fields.Boolean(string="Service Charges",
help="Enable to add service charge")
charge_type = fields.Selection(
[('amount', 'Amount'),
('percentage', 'Percentage')],
string='Type', default='amount',
help="Can choose charge percentage or amount")
is_service_charges = fields.Boolean("Service Charges")
visibility_type = fields.Selection([
('global', 'Global'),
('session', 'Session')],
string='Visibility', default='global',
help="Can choose visibility of service charges.")
service_charge = fields.Float(string='Service Charge',
help="Charge need to apply")
help="Charge need to apply",
default=10.0)
service_product_id = fields.Many2one('product.product',
string='Service Product',
domain="[('available_in_pos', '=', "
"True),"
"('sale_ok', '=', True), "
"('type', '=', 'service')]",
domain="[('sale_ok', '=', True)]",
help="Service Product")
service_charge_type = fields.Selection([
('amount', 'Amount'),
('percentage', 'Percentage')],
string='Type', default='amount',
help="Can choose charge percentage or amount")
@api.model
def _default_service_charge_on_module_install(self):
configs = self.env['pos.config'].search([])
open_configs = (
self.env['pos.session']
.search(['|', ('state', '!=', 'closed'), ('rescue', '=', True)])
.mapped('config_id')
)
# Do not modify configs where an opened session exists.
product = self.env.ref("point_of_sale.product_product_consumable",
raise_if_not_found=False)
for conf in (configs - open_configs):
conf.service_product_id = product if (
conf.is_service_charges
) and product and (
not product.company_id or product.company_id == conf.company_id
) else False
def open_ui(self):
for config in self:
if not self.current_session_id and (
config.is_service_charges
) and not config.service_product_id:
raise UserError(_(
'A discount product is needed to use the Service Charge '
'feature. Go to Point of Sale > Configuration > Settings '
'to set it.'))
return super().open_ui()
def _compute_check_session(self):
"""To check the service charge is set up for session wise or
globally"""
for record in self:
check_session = self.env['ir.config_parameter'].sudo().get_param(
'service_charges_pos.visibility')
if check_session == 'session':
record.is_session = True
else:
record.is_session = False
def _get_special_products(self):
res = super()._get_special_products()
return res | self.env['pos.config'].search(
[]).mapped('service_product_id')
@api.onchange('is_service_charges')
def onchange_is_service_charges(self):
"""When the service charge is enabled set service product
and amount by default per session"""
if self.is_service_charges:
if not self.service_product_id:
self.service_product_id = self.env[
'product.product'].search([
('available_in_pos', '=', True),
('sale_ok', '=', True), ('type', '=', 'service')
], limit=1)
self.service_charge = 10.0
else:
self.service_product_id = False
self.service_charge = 0.0
def _get_available_product_domain(self):
domain = super()._get_available_product_domain()
return OR([domain, [('id', '=', self.service_product_id.id)]])

36
service_charges_pos/models/pos_session.py

@ -23,28 +23,20 @@ from odoo import models
class PosSession(models.Model):
"""Inherited POS Session"""
_inherit = 'pos.session'
def _pos_ui_models_to_load(self):
"""Supering the function for loading res.config.settings to pos
session"""
result = super()._pos_ui_models_to_load()
result.append('res.config.settings')
return result
def _loader_params_res_config_settings(self):
"""Returning the field required"""
return {
'search_params': {
'fields': ['enable_service_charge', 'visibility',
'global_selection', 'global_charge',
'global_product_id'],
},
}
def _get_pos_ui_product_product(self, params):
result = super()._get_pos_ui_product_product(params)
service_product_id = self.config_id.service_product_id.id
product_ids_set = {product['id'] for product in result}
def _get_pos_ui_res_config_settings(self, params):
"""Returns the model"""
return self.env['res.config.settings'].sudo().search_read(
**params['search_params']
)
if self.config_id.is_service_charges and (
service_product_id) not in product_ids_set:
product_model = self.env['product.product'].with_context(
**params['context'])
product = product_model.search_read([(
'id', '=', service_product_id
)], fields=params['search_params']['fields'])
self._process_pos_ui_product_product(product)
result.extend(product)
return result

75
service_charges_pos/models/res_config_settings.py

@ -23,55 +23,38 @@ from odoo import api, fields, models
class ResConfigSettings(models.TransientModel):
"""Inherited Configuration Settings"""
_inherit = "res.config.settings"
_inherit = 'res.config.settings'
enable_service_charge = fields.Boolean(string="Service Charges",
config_parameter="service_charges_"
"pos.enable_service"
"_charge",
help="Enable to add service charge")
visibility = fields.Selection([
# pos.config fields
pos_is_service_charges = fields.Boolean(
related='pos_config_id.is_service_charges', readonly=False)
pos_visibility_type = fields.Selection([
('global', 'Global'),
('session', 'Session')],
default='global', string="Visibility",
config_parameter="service_charges_pos.visibility",
help='Setup the Service charge globally or per session')
global_selection = fields.Selection([
related='pos_config_id.visibility_type',
readonly=False)
pos_service_charge = fields.Float(related='pos_config_id.service_charge',
readonly=False)
pos_service_product_id = fields.Many2one(
'product.product',
compute='_compute_pos_service_product_id', store=True, readonly=False)
pos_service_charge_type = fields.Selection([
('amount', 'Amount'),
('percentage', 'Percentage')],
string='Type', default='amount',
config_parameter="service_charges_pos.global_selection",
help='Set the service charge as a amount or percentage')
global_charge = fields.Float(string='Service Charge',
config_parameter="service_charges_pos."
"global_charge",
help='Set a default service charge globally')
global_product_id = fields.Many2one('product.product',
string='Service Product',
domain="[('available_in_pos', '=', "
"True),"
"('sale_ok', '=', True), "
"('type', '=', 'service')]",
config_parameter="service_charges_pos"
".global_product_id",
help='Set a service product globally')
related='pos_config_id.service_charge_type',
readonly=False)
@api.onchange('enable_service_charge')
def onchange_enable_service_charge(self):
"""When the service charge is enabled set service product and amount
by default in globally"""
service_charges =self.env['pos.config'].search([])
if self.enable_service_charge:
service_charges.is_service_charges = True
if not self.global_product_id:
self.global_product_id = self.env[
'product.product'].search([
('available_in_pos', '=', True),
('sale_ok', '=', True),
('type', '=', 'service')
], limit=1)
self.global_charge = 10.0
else:
self.global_product_id = False
self.global_charge = 0.0
@api.depends('company_id', 'pos_is_service_charges', 'pos_config_id')
def _compute_pos_service_product_id(self):
default_product = self.env.ref(
"point_of_sale.product_product_consumable",
raise_if_not_found=False) or self.env['product.product']
for res_config in self:
service_product = res_config.pos_config_id.service_product_id or (
default_product)
if (res_config.pos_is_service_charges) and (
not service_product.company_id or (
service_product.company_id) == res_config.company_id):
res_config.pos_service_product_id = service_product
else:
res_config.pos_service_product_id = False

10
service_charges_pos/static/src/js/pos_load_data.js

@ -1,10 +0,0 @@
/** @odoo-module */
import { patch } from "@web/core/utils/patch";
import { PosStore } from "@point_of_sale/app/store/pos_store";
patch(PosStore.prototype, {
async _processData(loadedData) {
// To load data to pos session
await super._processData(...arguments);
this.res_config_settings = loadedData["res.config.settings"];
}
});

132
service_charges_pos/static/src/js/service_charge_button.js

@ -1,100 +1,72 @@
/** @odoo-module */
import { Component } from "@odoo/owl";
import { useService } from "@web/core/utils/hooks";
/** @odoo-module **/
import { _t } from "@web/core/l10n/translation";
import { ProductScreen } from "@point_of_sale/app/screens/product_screen/product_screen";
import { usePos } from "@point_of_sale/app/store/pos_hook";
import { ErrorPopup } from "@point_of_sale/app/errors/popups/error_popup";
import { useService } from "@web/core/utils/hooks";
import { NumberPopup } from "@point_of_sale/app/utils/input_popups/number_popup";
import { _t } from "@web/core/l10n/translation";
import { ErrorPopup } from "@point_of_sale/app/errors/popups/error_popup";
import { Component } from "@odoo/owl";
import { usePos } from "@point_of_sale/app/store/pos_hook";
import { parseFloat } from "@web/views/fields/parsers";
export class ServiceChargeButton extends Component {
static template = "service_charges_pos.ServiceChargeButton";
setup() {
this.pos = usePos();
this.popup = useService("popup");
}
async click() {
// To show number pop up and service charge applying functions based on conditions.
let res_config_settings = this.pos.res_config_settings[this.pos.res_config_settings.length -1]
var global_selection = res_config_settings.global_selection
var global_charge = res_config_settings.global_charge
var visibility = res_config_settings.visibility
var global_product = res_config_settings.global_product_id[0]
var order = this.pos.get_order();
var lines = order.get_orderlines();
if (visibility == 'global') {
var product = this.pos.db.get_product_by_id(global_product)
if (product === undefined) {
await this.popup.add(ErrorPopup, {
title: _t("No service product found"),
body: _t("The service product seems misconfigured. Make sure it is flagged as 'Can be Sold' and 'Available in Point of Sale'.")
});
return
}
// Remove existing discounts
lines.filter(line => line.get_product() === product).forEach(line => order.removeOrderline(line));
const { confirmed, payload } = await this.popup.add(NumberPopup, {
title: _t('Service Charge'),
startingValue: parseInt(global_charge),
isInputSelected: true
})
if (confirmed) {
if (payload > 0) {
if (global_selection == 'amount') {
order.add_product(product, {
price: payload
});
} else {
var total_amount = order.get_total_with_tax()
var per_amount = payload / 100 * total_amount
order.add_product(product, {
price: per_amount
});
}
}
var self = this;
const { confirmed, payload } = await this.popup.add(NumberPopup, {
title: _t("Service Charge"),
startingValue: this.pos.config.service_charge,
isInputSelected: true,
});
if (confirmed) {
const val = Math.max(0, Math.min(100, parseFloat(payload)));
if (val > 0) {
await self.apply_service_charge(val);
}
}
}
async apply_service_charge(val) {
const order = this.pos.get_order();
const lines = order.get_orderlines();
const product = this.pos.db.get_product_by_id(this.pos.config.service_product_id[0]);
if (product === undefined) {
await this.popup.add(ErrorPopup, {
title: _t("No service product found"),
body: _t(
"The service product seems misconfigured. Make sure it is flagged as 'Can be Sold' and 'Available in Point of Sale'."
),
});
return;
}
// Remove existing service charges
lines
.filter((line) => line.get_product() === product)
.forEach((line) => order._unlinkOrderline(line));
// Add service charge
if (this.pos.config.service_charge_type == 'amount') {
var sc_price = val
} else {
var type = this.pos.config.charge_type
var product = this.pos.db.get_product_by_id(this.pos.config.service_product_id[0]);
if (product === undefined) {
await this.popup.add(ErrorPopup, {
title: _t("No service product found"),
body: _t("The service product seems misconfigured. Make sure it is flagged as 'Can be Sold' and 'Available in Point of Sale'."),
});
return;
}
lines.filter(line => line.get_product() === product).forEach(line => order.removeOrderline(line));
const {confirmed, payload } = await this.popup.add(NumberPopup, {
title: _t('Service Charge'),
startingValue: this.pos.config.service_charge,
isInputSelected: true
})
if (confirmed) {
if (payload > 0) {
if (type == 'amount') {
order.add_product(product, {
price: payload
});
} else {
var total_amount = order.get_total_with_tax()
var per_amount = payload / 100 * total_amount
order.add_product(product, {
price: per_amount
});
}
}
}
var sc_price = order.get_total_with_tax() * val / 100
}
order.add_product(product, {
price: sc_price,
tax_ids: []
});
}
}
ProductScreen.addControlButton({
component: ServiceChargeButton,
condition: function () {
let res_config_settings = this.pos.config.is_service_charges
if (res_config_settings) {
return this.pos.config.is_service_charges
} else {
return false
}
const { is_service_charges, service_product_id } = this.pos.config;
return is_service_charges && service_product_id;
},
});

10
service_charges_pos/static/src/xml/ServiceChargeButton.xml

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates id="template" xml:space="preserve">
<!-- Pos button -->
<t t-name="service_charges_pos.ServiceChargeButton" owl="1">
<button class="control-button btn btn-light rounded-0 fw-bolder" t-on-click="() => this.click()">
<i class="fa fa-calculator" role="img" aria-label="Service Charge" title="Service Charge" />
Service Charge
</button>
</t>
</templates>

12
service_charges_pos/static/src/xml/service_charge_button.xml

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates id="template" xml:space="preserve">
<t t-name="service_charges_pos.ServiceChargeButton">
<span class="control-button btn btn-light rounded-0 fw-bolder" t-on-click="() => this.click()">
<i class="fa fa-calculator"></i>
<span> </span>
<span>Service Charge</span>
</span>
</t>
</templates>

26
service_charges_pos/views/pos_config_views.xml

@ -7,33 +7,25 @@
<field name="inherit_id" ref="point_of_sale.pos_config_view_form"/>
<field name="arch" type="xml">
<xpath expr="//setting[@id='other_devices']" position="after">
<field name="is_session" invisible="1"/>
<setting help="Allow service charges on orders" invisible="not is_session">
<field name="is_service_charges" force_save="1"/>
<field name="visibility_type" invisible="1"/>
<setting help="Allow service charges on orders" invisible="visibility_type == 'global'">
<field name="is_service_charges"/>
<div class="content-group mt16" invisible="not is_service_charges">
<div class="row">
<label for="charge_type"
class="col-lg-3 o_light_label"/>
<field name="charge_type" widget="radio"
options="{'horizontal': true}"
required="is_service_charges"
/>
<label for="service_product_id" class="col-lg-3 o_light_label"/>
<field name="service_product_id"/>
</div>
</div>
<div class="content-group mt16" invisible="not is_service_charges">
<div class="row">
<label for="service_product_id"
class="col-lg-3 o_light_label"/>
<field name="service_product_id"
required="is_service_charges"
/>
<label for="service_charge" class="col-lg-3 o_light_label"/>
<field name="service_charge"/>
</div>
</div>
<div class="content-group mt16" invisible="not is_service_charges">
<div class="row">
<label for="service_charge"
class="col-lg-3 o_light_label"/>
<field name="service_charge"/>
<label for="service_charge_type" class="col-lg-3 o_light_label"/>
<field name="service_charge_type" widget="radio" options="{'horizontal': true}"/>
</div>
</div>
</setting>

50
service_charges_pos/views/res_config_settings_views.xml

@ -4,45 +4,27 @@
<record id="res_config_settings_view_form" model="ir.ui.view">
<field name="name">res.config.settings.view.form.inherit.service.charges.pos</field>
<field name="model">res.config.settings</field>
<field name="inherit_id"
ref="point_of_sale.res_config_settings_view_form"/>
<field name="inherit_id" ref="point_of_sale.res_config_settings_view_form"/>
<field name="arch" type="xml">
<xpath expr="//block[@id='pos_interface_section']" position="inside">
<setting help="Allow service charges on orders">
<field name="enable_service_charge"/>
<div class="content-group mt16" invisible="not enable_service_charge">
<xpath expr="//block[@id='pos_pricing_section']" position="inside">
<setting help="Adds a button to set a service charge.">
<field name="pos_is_service_charges" readonly="pos_has_active_session"/>
<div class="content-group mt16" invisible="not pos_is_service_charges">
<div class="row">
<label string="Visibility" for="visibility" class="col-lg-3 o_light_label"/>
<field name="visibility" widget="radio"
options="{'horizontal': true}"
required="enable_service_charge"
/>
<label string="Visibility" for="pos_visibility_type" class="col-lg-3 o_light_label"/>
<field name="pos_visibility_type" widget="radio" options="{'horizontal': true}"/>
</div>
</div>
<div class="content-group mt16" invisible="not enable_service_charge or (visibility == 'session')">
<div class="row">
<label for="global_selection"
class="col-lg-3 o_light_label"/>
<field name="global_selection" widget="radio"
options="{'horizontal': true}"
required="enable_service_charge"
/>
<div class="row" invisible="pos_visibility_type == 'session'">
<label string="Service Product" for="pos_service_product_id" class="col-lg-3 o_light_label"/>
<field name="pos_service_product_id" required="pos_is_service_charges"/>
</div>
</div>
<div class="content-group mt16" invisible="not enable_service_charge or (visibility == 'session')">
<div class="row">
<label for="global_product_id"
class="col-lg-3 o_light_label"/>
<field name="global_product_id"
required="enable_service_charge"
/>
<div class="row" invisible="pos_visibility_type == 'session'">
<label string="Charge Type" for="pos_service_charge_type" class="col-lg-3 o_light_label"/>
<field name="pos_service_charge_type" widget="radio" options="{'horizontal': true}"/>
</div>
</div>
<div class="content-group mt16" invisible="not enable_service_charge or (visibility == 'session')">
<div class="row">
<label for="global_charge"
class="col-lg-3 o_light_label"/>
<field name="global_charge"/>
<div class="row" invisible="pos_visibility_type == 'session'">
<label string="Service Charge" for="pos_service_charge" class="col-lg-3 o_light_label"/>
<field name="pos_service_charge"/>
</div>
</div>
</setting>

Loading…
Cancel
Save