Browse Source

Mar 27 [UPDT] : Updated 'pos_traceability_validation'

pull/278/merge
AjmalCybro 1 year ago
parent
commit
d7b78df875
  1. 4
      pos_traceability_validation/__manifest__.py
  2. 5
      pos_traceability_validation/doc/RELEASE_NOTES.md
  3. 2
      pos_traceability_validation/models/__init__.py
  4. 42
      pos_traceability_validation/models/serial_no_validation.py
  5. 72
      pos_traceability_validation/models/stock_lot.py
  6. BIN
      pos_traceability_validation/static/description/assets/screenshots/1.png
  7. BIN
      pos_traceability_validation/static/description/assets/screenshots/10.png
  8. BIN
      pos_traceability_validation/static/description/assets/screenshots/11.png
  9. BIN
      pos_traceability_validation/static/description/assets/screenshots/2.png
  10. BIN
      pos_traceability_validation/static/description/assets/screenshots/3.png
  11. BIN
      pos_traceability_validation/static/description/assets/screenshots/4.png
  12. BIN
      pos_traceability_validation/static/description/assets/screenshots/5.png
  13. BIN
      pos_traceability_validation/static/description/assets/screenshots/6.png
  14. BIN
      pos_traceability_validation/static/description/assets/screenshots/7.png
  15. BIN
      pos_traceability_validation/static/description/assets/screenshots/8.png
  16. BIN
      pos_traceability_validation/static/description/assets/screenshots/9.png
  17. BIN
      pos_traceability_validation/static/description/assets/screenshots/hero.gif
  18. 1292
      pos_traceability_validation/static/description/index.html
  19. 77
      pos_traceability_validation/static/src/js/EditListPopup.js
  20. 37
      pos_traceability_validation/static/src/js/OrderWidget.js
  21. 90
      pos_traceability_validation/static/src/js/ProductScreen.js
  22. 69
      pos_traceability_validation/static/src/js/pos_models.js

4
pos_traceability_validation/__manifest__.py

@ -35,7 +35,9 @@
'depends': ['point_of_sale', 'stock'],
'assets': {
'web.assets_backend': [
'pos_traceability_validation/static/src/js/pos_models.js',
'pos_traceability_validation/static/src/js/EditListPopup.js',
'pos_traceability_validation/static/src/js/OrderWidget.js',
'pos_traceability_validation/static/src/js/ProductScreen.js',
],
},
'images': ['static/description/banner.jpg'],

5
pos_traceability_validation/doc/RELEASE_NOTES.md

@ -4,3 +4,8 @@
#### Version 16.0.1.0.0
#### ADD
- Initial Commit for POS Serial Number Validator
#### 11.02.2024
#### Version 16.0.1.1.0
#### REF
- Refactor the module and update the index

2
pos_traceability_validation/models/__init__.py

@ -18,4 +18,4 @@
# If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from . import serial_no_validation
from . import stock_lot

42
pos_traceability_validation/models/serial_no_validation.py

@ -1,42 +0,0 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2023-TODAY Cybrosys Technologies(<http://www.cybrosys.com>).
# Author: Jumana Jabin MP(<http://www.cybrosys.com>)
# you can modify it under the terms of the GNU LESSER
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
#
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
# GENERAL PUBLIC LICENSE (LGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from odoo import api, models
class SerialNoValidation(models.Model):
"""Serial Number Validation Model.This model is used for serial number
validation in Odoo."""
_name = 'serial_no.validation'
@api.model
def validate_lots(self, lots):
""" This method validates a list of lots."""
processed = []
LotObj = self.env['stock.lot']
for lot in lots:
lot_id = LotObj.search([('name', '=', lot)], limit=1)
if lot_id.product_qty > 0 and lot not in processed:
processed.append(lot)
continue
if lot in processed:
return ['duplicate', lot]
return ['no_stock', lot]
return True

72
pos_traceability_validation/models/stock_lot.py

@ -0,0 +1,72 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2023-TODAY Cybrosys Technologies(<http://www.cybrosys.com>).
# Author: Jumana Jabin MP(<http://www.cybrosys.com>)
# you can modify it under the terms of the GNU LESSER
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
#
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
# GENERAL PUBLIC LICENSE (LGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from odoo import api, models
class StockLot(models.Model):
"""
This class is inherited for adding a new function to validate the lots and
serial numbers.
Methods:
validate_lots(lots):
check and validate the lots and serial numbers for the product
based on the stock location.
"""
_inherit = 'stock.lot'
@api.model
def validate_lots(self, lots, product_id, picking_type_id):
""" To check
- the invalid lots/ serial numbers
- duplicate serial numbers
- insufficient stock for the lots or serial numbers.
All these cases are checked based on the product and the stock location
set for the active PoS.
Args:
lots (list[str,..., str]): the lots for validation.
product_id (int): id of the selected product.
picking_type_id (int): id of the operation type added for the PoS.
Returns:
list[str, str] or Bool: True if the lot is valid, else the list of
the string that indicates the exception: 'invalid', 'duplicate' or
'no_stock' with the lot/ serial number.
"""
processed = []
if not product_id:
return ['invalid', 'product']
for lot in lots:
stock_lots = self.sudo().search([
('name', '=', lot), ('product_id', '=', product_id)])
if not stock_lots:
return ['invalid', lot]
picking_type = self.env['stock.picking.type'].sudo().browse(
picking_type_id)
stock_quant = self.env['stock.quant'].sudo().search(
[('location_id', '=', picking_type.default_location_src_id.id),
('lot_id', 'in', stock_lots.ids)])
if (stock_quant and stock_quant.available_quantity > 0
and lot not in processed):
processed.append(lot)
else:
if lot in processed:
return ['duplicate', lot]
return ['no_stock', lot]
return True

BIN
pos_traceability_validation/static/description/assets/screenshots/1.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 118 KiB

After

Width:  |  Height:  |  Size: 134 KiB

BIN
pos_traceability_validation/static/description/assets/screenshots/10.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 290 KiB

BIN
pos_traceability_validation/static/description/assets/screenshots/11.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 352 KiB

BIN
pos_traceability_validation/static/description/assets/screenshots/2.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

After

Width:  |  Height:  |  Size: 129 KiB

BIN
pos_traceability_validation/static/description/assets/screenshots/3.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 222 KiB

After

Width:  |  Height:  |  Size: 143 KiB

BIN
pos_traceability_validation/static/description/assets/screenshots/4.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 294 KiB

After

Width:  |  Height:  |  Size: 137 KiB

BIN
pos_traceability_validation/static/description/assets/screenshots/5.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 231 KiB

After

Width:  |  Height:  |  Size: 68 KiB

BIN
pos_traceability_validation/static/description/assets/screenshots/6.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 243 KiB

After

Width:  |  Height:  |  Size: 249 KiB

BIN
pos_traceability_validation/static/description/assets/screenshots/7.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 233 KiB

After

Width:  |  Height:  |  Size: 290 KiB

BIN
pos_traceability_validation/static/description/assets/screenshots/8.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 244 KiB

After

Width:  |  Height:  |  Size: 281 KiB

BIN
pos_traceability_validation/static/description/assets/screenshots/9.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 257 KiB

BIN
pos_traceability_validation/static/description/assets/screenshots/hero.gif

Binary file not shown.

Before

Width:  |  Height:  |  Size: 383 KiB

After

Width:  |  Height:  |  Size: 475 KiB

1292
pos_traceability_validation/static/description/index.html

File diff suppressed because it is too large

77
pos_traceability_validation/static/src/js/EditListPopup.js

@ -0,0 +1,77 @@
odoo.define('pos_traceability_validation.PoSEditListPopup', function (require) {
"use strict";
const EditListPopup = require('point_of_sale.EditListPopup');
const Registries = require('point_of_sale.Registries');
var rpc = require('web.rpc');
/**
* EditListPopup Override
*
* This module overrides the EditListPopup component in the Point of Sale (POS) module
* to add custom behavior for serial number validation.
*/
const PosEditlistpopup = (EditListPopup) =>
class extends EditListPopup {
constructor() {
super(...arguments);
this.product = this.props.product;
}
/**
* On confirming from the popup after adding lots/ serial numbers,
* the values are passed to the function validate_lots() for the
* validation. The corresponding error messages will be displayed
* on the popup if the lot is invalid or duplicated, or there is
* no insufficient stock.
*/
async confirm() {
if (this.props.title == 'Lot/Serial Number(s) Required'){
var lot_string = this.state.array
var lot_names = [];
for (var i = 0; i < lot_string.length; i++) {
if (lot_string[i].text != ""){
lot_names.push(lot_string[i].text);
}
}
const picking_type_id = this.env.pos.config && this.env.pos.config.picking_type_id && this.env.pos.config.picking_type_id[0]
const result = await rpc.query({
model: 'stock.lot',
method: 'validate_lots',
args: [lot_names, this.props.product, picking_type_id]
})
if(result != true){
if(result[0] == 'no_stock'){
this.showPopup('ErrorPopup', {
'title': this.env._t('Out of stock'),
'body': this.env._t("The product is out of stock for " + result[1] + '.'),
});
}
else if(result[0] == 'duplicate'){
this.showPopup('ErrorPopup', {
'title': this.env._t('Duplicate entry'),
'body': this.env._t("Duplicate entry for " + result[1] + '.'),
});
}
else if(result[0] == 'invalid'){
this.showPopup('ErrorPopup', {
'title': this.env._t('Invalid Lot/ Serial Number'),
'body': this.env._t("The Lot/ Serial Number " + result[1]+ ' is not available for this product.'),
});
}
}
else{
this.env.posbus.trigger('close-popup', {
popupId: this.props.id,
response: { confirmed: true, payload: await this.getPayload() },
});
}
}
else{
this.env.posbus.trigger('close-popup', {
popupId: this.props.id,
response: { confirmed: true, payload: await this.getPayload() },
});
}
}
};
Registries.Component.extend(EditListPopup, PosEditlistpopup);
return EditListPopup;
});

37
pos_traceability_validation/static/src/js/OrderWidget.js

@ -0,0 +1,37 @@
odoo.define('pos_traceability_validation.PoSOrderWidget', function (require) {
'use strict';
const OrderWidget = require('point_of_sale.OrderWidget');
const Registries = require('point_of_sale.Registries');
/**
* Extends OrderWidget for passing the product IDs to the EditListPopup
* validation
*/
const PoSOrderWidget = (OrderWidget) =>
class extends OrderWidget {
async _editPackLotLines(event) {
const orderline = event.detail.orderline;
const isAllowOnlyOneLot = orderline.product.isAllowOnlyOneLot();
const packLotLinesToEdit = orderline.getPackLotLinesToEdit(isAllowOnlyOneLot);
const { confirmed, payload } = await this.showPopup('EditListPopup', {
title: this.env._t('Lot/Serial Number(s) Required'),
isSingleItem: isAllowOnlyOneLot,
array: packLotLinesToEdit,
product: orderline.product.id
});
if (confirmed) {
// Segregate the old and new packlot lines
const modifiedPackLotLines = Object.fromEntries(
payload.newArray.filter(item => item.id).map(item => [item.id, item.text])
);
const newPackLotLines = payload.newArray
.filter(item => !item.id)
.map(item => ({ lot_name: item.text }));
orderline.setPackLotLines({ modifiedPackLotLines, newPackLotLines });
}
this.order.select_orderline(event.detail.orderline);
}
}
Registries.Component.extend(OrderWidget, PoSOrderWidget);
return OrderWidget;
});

90
pos_traceability_validation/static/src/js/ProductScreen.js

@ -0,0 +1,90 @@
odoo.define('pos_traceability_validation.PoSProductScreen', function (require) {
'use strict';
const ProductScreen = require('point_of_sale.ProductScreen');
const Registries = require('point_of_sale.Registries');
/**
* Extends ProductScreen for passing the product ID to the EditListPopup
* validation
*/
const PoSProductScreen = (ProductScreen) =>
class extends ProductScreen {
async _getAddProductOptions(product, base_code) {
let price_extra = 0.0;
let draftPackLotLines, weight, description, packLotLinesToEdit;
if (this.env.pos.config.product_configurator && _.some(product.attribute_line_ids, (id) => id in this.env.pos.attributes_by_ptal_id)) {
let attributes = _.map(product.attribute_line_ids, (id) => this.env.pos.attributes_by_ptal_id[id])
.filter((attr) => attr !== undefined);
let { confirmed, payload } = await this.showPopup('ProductConfiguratorPopup', {
product: product,
attributes: attributes,
});
if (confirmed) {
description = payload.selected_attributes.join(', ');
price_extra += payload.price_extra;
} else {
return;
}
}
// Gather lot information if required.
if (['serial', 'lot'].includes(product.tracking) && (this.env.pos.picking_type.use_create_lots || this.env.pos.picking_type.use_existing_lots)) {
const isAllowOnlyOneLot = product.isAllowOnlyOneLot();
if (isAllowOnlyOneLot) {
packLotLinesToEdit = [];
} else {
const orderline = this.currentOrder
.get_orderlines()
.filter(line => !line.get_discount())
.find(line => line.product.id === product.id);
if (orderline) {
packLotLinesToEdit = orderline.getPackLotLinesToEdit();
} else {
packLotLinesToEdit = [];
}
}
const { confirmed, payload } = await this.showPopup('EditListPopup', {
title: this.env._t('Lot/Serial Number(s) Required'),
isSingleItem: isAllowOnlyOneLot,
array: packLotLinesToEdit,
product: product.id
});
if (confirmed) {
// Segregate the old and new packlot lines
const modifiedPackLotLines = Object.fromEntries(
payload.newArray.filter(item => item.id).map(item => [item.id, item.text])
);
const newPackLotLines = payload.newArray
.filter(item => !item.id)
.map(item => ({ lot_name: item.text }));
draftPackLotLines = { modifiedPackLotLines, newPackLotLines };
} else {
// We don't proceed on adding product.
return;
}
}
// Take the weight if necessary.
if (product.to_weight && this.env.pos.config.iface_electronic_scale) {
// Show the ScaleScreen to weigh the product.
if (this.isScaleAvailable) {
const { confirmed, payload } = await this.showTempScreen('ScaleScreen', {
product,
});
if (confirmed) {
weight = payload.weight;
} else {
// do not add the product;
return;
}
} else {
await this._onScaleNotAvailable();
}
}
if (base_code && this.env.pos.db.product_packaging_by_barcode[base_code.code]) {
weight = this.env.pos.db.product_packaging_by_barcode[base_code.code].qty;
}
return { draftPackLotLines, quantity: weight, description, price_extra };
}
}
Registries.Component.extend(ProductScreen, PoSProductScreen);
return ProductScreen;
});

69
pos_traceability_validation/static/src/js/pos_models.js

@ -1,69 +0,0 @@
odoo.define('pos_traceability_validation.pos_models', function (require) {
"use strict";
/**
* EditListPopup Override
*
* This module overrides the EditListPopup component in the Point of Sale (POS) module
* to add custom behavior for serial number validation.
*/
const EditListPopup = require('point_of_sale.EditListPopup');
const Registries = require('point_of_sale.Registries');
var rpc = require('web.rpc');
const PosEditlistpopup = (EditListPopup) =>
class extends EditListPopup {
/**
* Confirm Override
*
* Overrides the base confirm method to handle serial number validation.
* If the title of the popup is 'Lot/Serial Number(s) Required', it validates
* the entered lot numbers using the 'serial_no.validation' model.
*/
async confirm() {
if (this.props.title == 'Lot/Serial Number(s) Required'){
var lot_string = this.state.array
var lot_names = [];
for (var i = 0; i < lot_string.length; i++) {
if (lot_string[i].text != ""){
lot_names.push(lot_string[i].text);
}
}
const result = await rpc.query({
model: 'serial_no.validation',
method: 'validate_lots',
args: [lot_names]
})
if(result != true){
if(result[0] == 'no_stock'){
this.showPopup('ErrorPopup', {
'title': this.env._t('Insufficient stock'),
'body': this.env._t("Insufficient stock for " + result[1]),
});
}
else if(result[0] == 'duplicate'){
this.showPopup('ErrorPopup', {
'title': this.env._t('Duplicate entry'),
'body': this.env._t("Duplicate entry for " + result[1]),
});
}
else if(result[0] == 'except'){
alert("Exception occurred with " + result[1])
this.showPopup('ErrorPopup', {
'title': this.env._t('Exception'),
'body': this.env._t("Exception occurred with" + result[1]),
});
}
}
else{
this.props.resolve({ confirmed: true, payload: await this.getPayload() });
this.trigger('close-popup');
}
}
else{
this.props.resolve({ confirmed: true, payload: await this.getPayload() });
this.trigger('close-popup');
}
}
};
Registries.Component.extend(EditListPopup, PosEditlistpopup);
return EditListPopup;
});
Loading…
Cancel
Save