Browse Source

Jan 17: [FIX] Bug Fixed 'pos_multi_variant'

pull/364/head
Cybrosys Technologies 3 months ago
parent
commit
06fdea1639
  1. 2
      pos_multi_variant/__init__.py
  2. 2
      pos_multi_variant/__manifest__.py
  3. 5
      pos_multi_variant/doc/RELEASE_NOTES.md
  4. 2
      pos_multi_variant/models/__init__.py
  5. 2
      pos_multi_variant/models/pos_session.py
  6. 2
      pos_multi_variant/models/product_template.py
  7. 2
      pos_multi_variant/models/variants_tree.py
  8. 154
      pos_multi_variant/static/src/css/label.css
  9. 90
      pos_multi_variant/static/src/js/ProductPopup.js
  10. 33
      pos_multi_variant/static/src/js/ProductScreen.js
  11. 22
      pos_multi_variant/static/src/js/models.js
  12. 23
      pos_multi_variant/static/src/js/product_variant_orderline.js
  13. 5
      pos_multi_variant/static/src/xml/label.xml
  14. 54
      pos_multi_variant/static/src/xml/popup.xml

2
pos_multi_variant/__init__.py

@ -1,4 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
############################################################################# #############################################################################
# #
# Cybrosys Technologies Pvt. Ltd. # Cybrosys Technologies Pvt. Ltd.
@ -19,4 +20,5 @@
# If not, see <http://www.gnu.org/licenses/>. # If not, see <http://www.gnu.org/licenses/>.
# #
############################################################################# #############################################################################
from . import models from . import models

2
pos_multi_variant/__manifest__.py

@ -21,7 +21,7 @@
############################################################################# #############################################################################
{ {
'name': "POS Product Multi variant", 'name': "POS Product Multi variant",
'version': '16.0.1.0.0', 'version': '16.0.1.0.1',
'category': 'Point of Sale', 'category': 'Point of Sale',
'summary': """POS Multi-variant module is an advanced way for managing 'summary': """POS Multi-variant module is an advanced way for managing
product variants from the point of sale application""", product variants from the point of sale application""",

5
pos_multi_variant/doc/RELEASE_NOTES.md

@ -6,4 +6,7 @@
- Initial commit for POS Product Multi variant - Initial commit for POS Product Multi variant
#### 14.01.2025
#### Version 16.0.1.0.1
##### ADD
- Fixed issue related to the amount total in pos orders.

2
pos_multi_variant/models/__init__.py

@ -1,4 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
############################################################################# #############################################################################
# #
# Cybrosys Technologies Pvt. Ltd. # Cybrosys Technologies Pvt. Ltd.
@ -19,6 +20,7 @@
# If not, see <http://www.gnu.org/licenses/>. # If not, see <http://www.gnu.org/licenses/>.
# #
############################################################################# #############################################################################
from . import product_template from . import product_template
from . import pos_session from . import pos_session
from . import variants_tree from . import variants_tree

2
pos_multi_variant/models/pos_session.py

@ -1,4 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
############################################################################# #############################################################################
# #
# Cybrosys Technologies Pvt. Ltd. # Cybrosys Technologies Pvt. Ltd.
@ -19,6 +20,7 @@
# If not, see <http://www.gnu.org/licenses/>. # If not, see <http://www.gnu.org/licenses/>.
# #
############################################################################# #############################################################################
from odoo import models from odoo import models

2
pos_multi_variant/models/product_template.py

@ -1,4 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
############################################################################# #############################################################################
# #
# Cybrosys Technologies Pvt. Ltd. # Cybrosys Technologies Pvt. Ltd.
@ -19,6 +20,7 @@
# If not, see <http://www.gnu.org/licenses/>. # If not, see <http://www.gnu.org/licenses/>.
# #
############################################################################# #############################################################################
from odoo import fields, models from odoo import fields, models

2
pos_multi_variant/models/variants_tree.py

@ -1,4 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
############################################################################# #############################################################################
# #
# Cybrosys Technologies Pvt. Ltd. # Cybrosys Technologies Pvt. Ltd.
@ -19,6 +20,7 @@
# If not, see <http://www.gnu.org/licenses/>. # If not, see <http://www.gnu.org/licenses/>.
# #
############################################################################# #############################################################################
from odoo import fields, models from odoo import fields, models

154
pos_multi_variant/static/src/css/label.css

@ -38,6 +38,24 @@ position: absolute;
background-color:#d7d7d7; background-color:#d7d7d7;
font-style: italic; font-style: italic;
} }
.multi_variant {
position: absolute;
top: 51px;
left: 10px;
padding: 5px 10px;
background-color: #007bff;
color: white;
font-size: 12px;
font-weight: bold;
border-radius: 3px;
border: 1px solid #0056b3;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
text-transform: uppercase;
cursor: default;
white-space: nowrap;
}
.variant-selected{ .variant-selected{
position: absolute; position: absolute;
bottom: 23px; bottom: 23px;
@ -65,3 +83,139 @@ font-style: italic;
align-items: center; align-items: center;
color: #03001C; color: #03001C;
} }
.pos_multi_variant .product-category {
padding: 0.4rem 0;
max-width: 1200px;
margin: 0 auto;
}
.pos_multi_variant .category-title {
font-size: 1.8rem;
color: #333;
margin-bottom: 1.5rem;
text-align: center;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.pos_multi_variant .product-grid {
display: grid;
/* Three items per row by default */
grid-template-columns: repeat(3, 1fr);
gap: 1rem;
padding: 0 1rem;
margin: 0 auto;
max-width: 1000px;
}
.pos_multi_variant .product-card {
position: relative;
background: white;
border-radius: 12px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
transition: transform 0.3s ease, box-shadow 0.3s ease;
overflow: hidden;
cursor: pointer;
width: 100%;
}
.pos_multi_variant .product-card:hover {
transform: translateY(-5px);
box-shadow: 0 8px 12px rgba(0, 0, 0, 0.15);
}
.pos_multi_variant .product-image {
position: relative;
width: 100%;
padding-top: 100%; /* 1:1 Aspect Ratio */
overflow: hidden;
}
.pos_multi_variant .product-image img {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.3s ease;
}
.pos_multi_variant .product-card:hover .product-image img {
transform: scale(1.05);
}
.pos_multi_variant .ribbon {
position: absolute;
top: 20px;
left: -5px;
background: #40bb4e;
color: white;
padding: 8px 20px;
font-size: 0.85rem;
font-weight: 600;
text-transform: uppercase;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
z-index: 100;
clip-path: polygon(0 0, 100% 0, calc(100% - 10px) 100%, 0 100%, 10px 50%);
}
.pos_multi_variant .price-tag {
position: absolute;
bottom: 60px;
right: 10px;
background: rgba(0, 0, 0, 0.8);
color: white;
padding: 5px 10px;
border-radius: 20px;
font-size: 0.9rem;
font-weight: 500;
}
.pos_multi_variant .variant-name {
position: absolute;
bottom: 0;
left: 0;
right: 0;
background: rgba(0, 0, 0, 0.7);
color: white;
padding: 12px;
text-align: center;
font-size: 1rem;
font-weight: 500;
}
.pos_multi_variant .section-divider {
border: none;
height: 1px;
background: linear-gradient(to right, transparent, #ddd, transparent);
margin: 2rem 0;
}
/* Tablet view - 2 items per row */
@media (max-width: 900px) {
.pos_multi_variant .product-grid {
grid-template-columns: repeat(2, 1fr);
gap: 1.5rem;
padding: 0 1rem;
}
.pos_multi_variant .category-title {
font-size: 1.5rem;
}
}
/* Mobile view - 1 item per row */
@media (max-width: 600px) {
.pos_multi_variant .product-grid {
grid-template-columns: 1fr;
gap: 1rem;
}
.pos_multi_variant .product-card {
max-width: 350px;
margin: 0 auto;
}
}

90
pos_multi_variant/static/src/js/ProductPopup.js

@ -1,75 +1,57 @@
odoo.define('pos_multi_variant.ProductsPopup', function(require) { /** @odoo-module **/
'use strict';
/* This JavaScript code defines the ProductsPopup component, which extends
* the ProductItem class from the point_of_sale module. It represents a popup
* for selecting product variants.
*/
const Registries = require('point_of_sale.Registries');
const { useListener } = require("@web/core/utils/hooks");
const ProductItem = require('point_of_sale.ProductItem');
class ProductsPopup extends ProductItem { import Registries from 'point_of_sale.Registries';
import { useListener } from "@web/core/utils/hooks";
const ProductItem = require('point_of_sale.ProductItem');
const { useState } = owl;
class ProductsPopup extends ProductItem {
setup() { setup() {
super.setup(); super.setup();
useListener('click','.confirm', this.click_confirm); this.state = useState({
useListener('click','.product', this.select_variant); variant_details: this.props.variant_details,
useListener('click','.cancel', this.click_cancel); selected_variants: {},
price_total: {},
})
} }
select_variant(e) {
var order = this.env.pos.get_order() SelectVariant(product,variant) {
var self = e.composedPath ? e.composedPath()[2] : e.path[2]; if (this.state.selected_variants[product.attribute_id[1]] === variant.id){
var action = $(self).find('.action').text(); this.state.selected_variants[product.attribute_id[1]] = false
var category = $(self).find('.action').data('category'); this.state.price_total[product.attribute_id[1]] = 0.0
$('.product-img').find('.variant-selected').each(function () }
{ if($(this).data('category') == category) else{
{ $(this).text("").removeClass('variant-selected'); this.state.selected_variants[product.attribute_id[1]] = variant.id
$(self).find('.action').text("Selected").addClass('variant-selected'); this.state.price_total[product.attribute_id[1]] = product.extra_price
} }
});
$(self).find('.action').text("Selected").addClass('variant-selected');
var add = $(self).find('.extra-price').text().substr(1).slice(0, -2);
var type = $(self).find('.variants').text();
$(self).find('.variant-selected').attr('data-price', add);
$(self).find('.variant-selected').attr('data-type', type);
} }
click_confirm(e){
var price = 0.00 clickConfirm(e){
const total = Object.values(this.state.price_total).reduce((sum, value) => sum + value, 0);
var order = this.env.pos.get_order() var order = this.env.pos.get_order()
var selected_orderline = order.get_selected_orderline() var selected_orderline = order.get_selected_orderline()
var variants = [] selected_orderline.price += total
this.env.pos.selectedOrder.selected_orderline.product_variants=variants
$('.product-img').find('.variant-selected').each(function ()
{
var add = this.previousSibling.innerHTML;
add = add.slice(3)
price += parseFloat(add)
if (order.selected_orderline.product.is_pos_variants){
variants.push({
'extra_price': add,
'type': $(this).data('type'),
})
};
})
selected_orderline.price_manually_set = true;
selected_orderline.price += price
this.env.posbus.trigger('close-popup', { this.env.posbus.trigger('close-popup', {
popupId: this.props.id popupId: this.props.id
}); });
} }
click_cancel(){ clickCancel(){
this.env.pos.get_order().orderlines.remove(this.env.pos.get_order().selected_orderline)
this.env.posbus.trigger('close-popup', { this.env.posbus.trigger('close-popup', {
popupId: this.props.id popupId: this.props.id
}); });
} }
imageUrl() { imageUrl() {
return `/web/image?model=product.product&field=image_1920&id=${this.props.product_tmpl_id}&unique=1`; return `/web/image?model=product.product&field=image_1920&id=${this.props.product_tmpl_id}&unique=1`;
} }
async _clickProduct(event) {
} getSelected(attr, variant) {
return this.state.selected_variants[attr] === variant.id
} }
ProductsPopup.template = 'ProductsPopUp';
ProductsPopup.defaultProps = {};
Registries.Component.add(ProductsPopup);
return ProductsPopup;
});
}
ProductsPopup.template = 'ProductsPopUp';
ProductsPopup.defaultProps = {};
Registries.Component.add(ProductsPopup);

33
pos_multi_variant/static/src/js/ProductScreen.js

@ -1,18 +1,16 @@
odoo.define('pos_multi_variant.ProductScreen', function(require) { /** @odoo-module **/
'use strict';
/* This JavaScript code extends the ProductScreen class from the point_of_sale module. import ProductScreen from 'point_of_sale.ProductScreen';
* It adds functionality for handling product clicks and displaying the ProductsPopup import Registries from 'point_of_sale.Registries';
* for selecting variants. import NumberBuffer from 'point_of_sale.NumberBuffer';
*/ import rpc from 'web.rpc';
var ProductScreen = require('point_of_sale.ProductScreen');
const Registries = require('point_of_sale.Registries'); const ProductScreenExtend = (ProductScreen) =>
const NumberBuffer = require('point_of_sale.NumberBuffer'); class extends ProductScreen {
var rpc = require('web.rpc'); setup() {
const ProductScreenExtend = (ProductScreen) => super.setup();
class extends ProductScreen {
constructor() {
super(...arguments);
} }
async _clickProduct(event) { async _clickProduct(event) {
await super._clickProduct(...arguments) await super._clickProduct(...arguments)
if (!this.currentOrder) { if (!this.currentOrder) {
@ -56,7 +54,6 @@ odoo.define('pos_multi_variant.ProductScreen', function(require) {
}); });
} }
} }
}; };
Registries.Component.extend(ProductScreen, ProductScreenExtend);
return ProductScreen; Registries.Component.extend(ProductScreen, ProductScreenExtend);
});

22
pos_multi_variant/static/src/js/models.js

@ -1,14 +1,9 @@
odoo.define('pos_multi_variant.model', function(require) { /** @odoo-module **/
'use strict';
/* This JavaScript code defines the VariantsPosGlobalState class import { PosGlobalState } from 'point_of_sale.models';
* extending the PosGlobalState class from the point_of_sale.models module. import Registries from 'point_of_sale.Registries';
* It adds additional functionality to process variants data.
*/ const VariantsPosGlobalState = (PosGlobalState) => class VariantsPosGlobalState extends PosGlobalState {
var {
PosGlobalState
} = require('point_of_sale.models');
const Registries = require('point_of_sale.Registries');
const VariantsPosGlobalState = (PosGlobalState) => class VariantsPosGlobalState extends PosGlobalState {
async _processData(loadedData) { async _processData(loadedData) {
await super._processData(...arguments); await super._processData(...arguments);
@ -16,6 +11,5 @@ odoo.define('pos_multi_variant.model', function(require) {
this.variants_tree = loadedData['variants.tree']; this.variants_tree = loadedData['variants.tree'];
this.product_attribute_value = loadedData['product.attribute.value']; this.product_attribute_value = loadedData['product.attribute.value'];
} }
} }
Registries.Model.extend(PosGlobalState, VariantsPosGlobalState); Registries.Model.extend(PosGlobalState, VariantsPosGlobalState);
});

23
pos_multi_variant/static/src/js/product_variant_orderline.js

@ -1,17 +1,9 @@
odoo.define('pos_order_question.Orderline', function(require) { /** @odoo-module **/
'use strict';
/* This JavaScript code defines the PosMultiVariantOrderline class
* extending the Orderline class from the point_of_sale.models module.
* It adds additional functionality related to product variants.
*/
var {
Orderline,
} = require('point_of_sale.models');
var utils = require('web.utils');
const Registries = require('point_of_sale.Registries');
const PosMultiVariantOrderline = (Orderline) => class PosMultiVariantOrderline extends Orderline { import { Orderline } from 'point_of_sale.models';
import Registries from 'point_of_sale.Registries';
export const PosMultiVariantOrderline = (Orderline) => class PosMultiVariantOrderline extends Orderline {
constructor(obj, options) { constructor(obj, options) {
super(...arguments); super(...arguments);
this.product_variants = this.product_variants || []; this.product_variants = this.product_variants || [];
@ -26,9 +18,6 @@ odoo.define('pos_order_question.Orderline', function(require) {
line.product_variants = this.product_variants; line.product_variants = this.product_variants;
return line; return line;
} }
} }
Registries.Model.extend(Orderline, PosMultiVariantOrderline);
});
Registries.Model.extend(Orderline, PosMultiVariantOrderline);

5
pos_multi_variant/static/src/xml/label.xml

@ -2,10 +2,9 @@
<templates id="template" xml:space="preserve"> <templates id="template" xml:space="preserve">
<!-- This XML snippet extends the point_of_sale.ProductItem template --> <!-- This XML snippet extends the point_of_sale.ProductItem template -->
<t t-inherit="point_of_sale.ProductItem" t-inherit-mode="extension" owl="1"> <t t-inherit="point_of_sale.ProductItem" t-inherit-mode="extension" owl="1">
<xpath expr="//div[hasclass('product-img')]" position="inside"> <xpath expr="//div[hasclass('product-content')]" position="inside">
<t t-if="props.product.is_pos_variants"> <t t-if="props.product.is_pos_variants">
<span class="custom-pos-label">Multi-variant</span> <span class="multi_variant">Multi-variant</span>
<link rel="stylesheet" type="text/scss" href="pos_multi_variant/static/src/css/label.css"/>
</t> </t>
</xpath> </xpath>
</t> </t>

54
pos_multi_variant/static/src/xml/popup.xml

@ -2,40 +2,35 @@
<templates id="template" xml:space="preserve"> <templates id="template" xml:space="preserve">
<!-- This XML snippet defines the ProductsPopUp template --> <!-- This XML snippet defines the ProductsPopUp template -->
<t t-name="ProductsPopUp" owl="1"> <t t-name="ProductsPopUp" owl="1">
<div role="dialog" class="modal-dialog"> <Draggable>
<div class="popup popup-selection"> <div class="popup popup-selection pos_multi_variant" style="">
<header class="title"> <header class="title">
<t t-esc="props.title || 'Product'" /> <t t-esc="props.title || 'Product'"/>
</header> </header>
<div class="selection scrollable-y touch-scrollable"> <div class="" style="max-height: 25rem; overflow: auto;">
<div id="notify"/></div>
<t t-foreach="props.products" t-as="product" t-key="product.id"> <t t-foreach="props.products" t-as="product" t-key="product.id">
<div> <div class="product-category">
<h2> <h2 class="category-title">
<t t-esc="product['attribute_id'][1]"/> <t t-esc="product['attribute_id'][1]"/>
</h2> </h2>
</div>
<t t-set="attribute" t-value="product['attribute_id'][1]"/> <t t-set="attribute" t-value="product['attribute_id'][1]"/>
<div class="row">
<div class="" style="display:flex; flex-wrap: wrap;justify-content:center; padding:0 1.5rem;"> <div class="product-grid">
<t t-foreach="props.variant_details" t-as="variant" <t t-foreach="props.variant_details" t-as="variant" t-key="variant.id">
t-key="variant.id">
<t t-foreach="product['value_ids']" t-as="val" t-key="val"> <t t-foreach="product['value_ids']" t-as="val" t-key="val">
<t t-if="variant['id'] == val"> <t t-if="variant['id'] == val">
<!-- Display the product variant --> <article class="product-card"
<article class='product' style="flex-grow: 1;"> t-on-click="()=>this.SelectVariant(product,variant)">
<div class='product-img'> <div t-if="getSelected(attribute, variant)" class="ribbon">Selected</div>
<img t-att-src="imageUrl()"/> <div class="product-image">
<span class='extra-price'>+ <img t-att-src="imageUrl()" alt="Product Variant"/>
<t <div class="price-tag">
t-esc="env.pos.format_currency(product['extra_price'])"/> + <t t-esc="env.pos.format_currency(product['extra_price'])"/>
</span> </div>
<h2 class='action' data-price='' <div class="variant-name">
data-type=''
t-att-data-category='attribute'/>
<span class='variants'>
<t t-esc="variant['name']"/> <t t-esc="variant['name']"/>
</span> </div>
</div> </div>
</article> </article>
</t> </t>
@ -43,15 +38,16 @@
</t> </t>
</div> </div>
</div> </div>
<hr/> <hr class="section-divider"/>
</t> </t>
</div>
<footer class="footer"> <footer class="footer">
<div class="button confirm"> Confirm </div> <div class="button confirm" t-on-click="clickConfirm">Confirm</div>
<div class="button cancel"> <div class="button cancel" t-on-click="clickCancel">
Cancel Cancel
</div> </div>
</footer> </footer>
</div> </div>
</div> </Draggable>
</t> </t>
</templates> </templates>
Loading…
Cancel
Save