14 changed files with 362 additions and 238 deletions
@ -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'; |
||||
setup() { |
import { useListener } from "@web/core/utils/hooks"; |
||||
super.setup(); |
const ProductItem = require('point_of_sale.ProductItem'); |
||||
useListener('click','.confirm', this.click_confirm); |
const { useState } = owl; |
||||
useListener('click','.product', this.select_variant); |
|
||||
useListener('click','.cancel', this.click_cancel); |
|
||||
} |
class ProductsPopup extends ProductItem { |
||||
select_variant(e) { |
setup() { |
||||
var order = this.env.pos.get_order() |
super.setup(); |
||||
var self = e.composedPath ? e.composedPath()[2] : e.path[2]; |
this.state = useState({ |
||||
var action = $(self).find('.action').text(); |
variant_details: this.props.variant_details, |
||||
var category = $(self).find('.action').data('category'); |
selected_variants: {}, |
||||
$('.product-img').find('.variant-selected').each(function () |
price_total: {}, |
||||
{ if($(this).data('category') == category) |
}) |
||||
{ $(this).text("").removeClass('variant-selected'); |
} |
||||
$(self).find('.action').text("Selected").addClass('variant-selected'); |
|
||||
} |
SelectVariant(product,variant) { |
||||
}); |
if (this.state.selected_variants[product.attribute_id[1]] === variant.id){ |
||||
$(self).find('.action').text("Selected").addClass('variant-selected'); |
this.state.selected_variants[product.attribute_id[1]] = false |
||||
var add = $(self).find('.extra-price').text().substr(1).slice(0, -2); |
this.state.price_total[product.attribute_id[1]] = 0.0 |
||||
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 |
|
||||
var order = this.env.pos.get_order() |
|
||||
var selected_orderline = order.get_selected_orderline() |
|
||||
var variants = [] |
|
||||
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', { |
|
||||
popupId: this.props.id |
|
||||
}); |
|
||||
} |
|
||||
click_cancel(){ |
|
||||
this.env.posbus.trigger('close-popup', { |
|
||||
popupId: this.props.id |
|
||||
}); |
|
||||
} |
|
||||
imageUrl() { |
|
||||
return `/web/image?model=product.product&field=image_1920&id=${this.props.product_tmpl_id}&unique=1`; |
|
||||
} |
} |
||||
async _clickProduct(event) { |
else{ |
||||
|
this.state.selected_variants[product.attribute_id[1]] = variant.id |
||||
|
this.state.price_total[product.attribute_id[1]] = product.extra_price |
||||
} |
} |
||||
} |
} |
||||
ProductsPopup.template = 'ProductsPopUp'; |
|
||||
ProductsPopup.defaultProps = {}; |
|
||||
Registries.Component.add(ProductsPopup); |
|
||||
return ProductsPopup; |
|
||||
}); |
|
||||
|
|
||||
|
clickConfirm(e){ |
||||
|
const total = Object.values(this.state.price_total).reduce((sum, value) => sum + value, 0); |
||||
|
var order = this.env.pos.get_order() |
||||
|
var selected_orderline = order.get_selected_orderline() |
||||
|
selected_orderline.price += total |
||||
|
this.env.posbus.trigger('close-popup', { |
||||
|
popupId: this.props.id |
||||
|
}); |
||||
|
} |
||||
|
clickCancel(){ |
||||
|
this.env.pos.get_order().orderlines.remove(this.env.pos.get_order().selected_orderline) |
||||
|
this.env.posbus.trigger('close-popup', { |
||||
|
popupId: this.props.id |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
imageUrl() { |
||||
|
return `/web/image?model=product.product&field=image_1920&id=${this.props.product_tmpl_id}&unique=1`; |
||||
|
} |
||||
|
|
||||
|
getSelected(attr, variant) { |
||||
|
return this.state.selected_variants[attr] === variant.id |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
ProductsPopup.template = 'ProductsPopUp'; |
||||
|
ProductsPopup.defaultProps = {}; |
||||
|
Registries.Component.add(ProductsPopup); |
||||
|
@ -1,62 +1,59 @@ |
|||||
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) { |
||||
|
await super._clickProduct(...arguments) |
||||
|
if (!this.currentOrder) { |
||||
|
this.env.pos.add_new_order(); |
||||
} |
} |
||||
async _clickProduct(event) { |
const product = event.detail; |
||||
await super._clickProduct(...arguments) |
var variant_product = '' |
||||
if (!this.currentOrder) { |
await rpc.query({ |
||||
this.env.pos.add_new_order(); |
model: 'variants.tree', |
||||
} |
method: 'search_read', |
||||
const product = event.detail; |
fields: ['extra_price','attribute_id','value_ids', 'variants_id'], |
||||
var variant_product = '' |
args: [[['variants_id','=',event.detail.product_tmpl_id]]] |
||||
await rpc.query({ |
}).then(function (data) { |
||||
model: 'variants.tree', |
variant_product = data |
||||
method: 'search_read', |
}); |
||||
fields: ['extra_price','attribute_id','value_ids', 'variants_id'], |
var li=[] |
||||
args: [[['variants_id','=',event.detail.product_tmpl_id]]] |
for(var i=0; i<variant_product.length; ++i) { |
||||
}).then(function (data) { |
variant_product[i].value_ids.forEach(function (field) { |
||||
variant_product = data |
li.push(field) |
||||
}); |
}); |
||||
var li=[] |
} |
||||
for(var i=0; i<variant_product.length; ++i) { |
var variant_details = '' |
||||
variant_product[i].value_ids.forEach(function (field) { |
await rpc.query({ |
||||
li.push(field) |
model: 'product.attribute.value', |
||||
}); |
method: 'search_read', |
||||
} |
fields: ['name'], |
||||
var variant_details = '' |
domain: [['id', 'in', li]], |
||||
await rpc.query({ |
}).then(function (result) { |
||||
model: 'product.attribute.value', |
variant_details = result |
||||
method: 'search_read', |
}); |
||||
fields: ['name'], |
const options = await this._getAddProductOptions(product); |
||||
domain: [['id', 'in', li]], |
// Do not add product if options is undefined.
|
||||
}).then(function (result) { |
if (!options) return; |
||||
variant_details = result |
NumberBuffer.reset(); |
||||
}); |
if(product.is_pos_variants){ |
||||
const options = await this._getAddProductOptions(product); |
this.showPopup('ProductsPopup',{ |
||||
// Do not add product if options is undefined.
|
title: product.display_name, |
||||
if (!options) return; |
products: variant_product, |
||||
NumberBuffer.reset(); |
product_tmpl_id: event.detail.id, |
||||
if(product.is_pos_variants){ |
variant_details: variant_details, |
||||
this.showPopup('ProductsPopup',{ |
}); |
||||
title: product.display_name, |
} |
||||
products: variant_product, |
} |
||||
product_tmpl_id: event.detail.id, |
}; |
||||
variant_details: variant_details, |
|
||||
}); |
Registries.Component.extend(ProductScreen, ProductScreenExtend); |
||||
} |
|
||||
} |
|
||||
}; |
|
||||
Registries.Component.extend(ProductScreen, ProductScreenExtend); |
|
||||
return ProductScreen; |
|
||||
}); |
|
||||
|
@ -1,21 +1,15 @@ |
|||||
odoo.define('pos_multi_variant.model', function(require) { |
/** @odoo-module **/ |
||||
'use strict'; |
|
||||
/* This JavaScript code defines the VariantsPosGlobalState class |
|
||||
* extending the PosGlobalState class from the point_of_sale.models module. |
|
||||
* It adds additional functionality to process variants data. |
|
||||
*/ |
|
||||
var { |
|
||||
PosGlobalState |
|
||||
} = require('point_of_sale.models'); |
|
||||
const Registries = require('point_of_sale.Registries'); |
|
||||
const VariantsPosGlobalState = (PosGlobalState) => class VariantsPosGlobalState extends PosGlobalState { |
|
||||
async _processData(loadedData) { |
|
||||
|
|
||||
await super._processData(...arguments); |
import { PosGlobalState } from 'point_of_sale.models'; |
||||
|
import Registries from 'point_of_sale.Registries'; |
||||
|
|
||||
this.variants_tree = loadedData['variants.tree']; |
const VariantsPosGlobalState = (PosGlobalState) => class VariantsPosGlobalState extends PosGlobalState { |
||||
this.product_attribute_value = loadedData['product.attribute.value']; |
async _processData(loadedData) { |
||||
} |
|
||||
|
await super._processData(...arguments); |
||||
|
|
||||
|
this.variants_tree = loadedData['variants.tree']; |
||||
|
this.product_attribute_value = loadedData['product.attribute.value']; |
||||
} |
} |
||||
Registries.Model.extend(PosGlobalState, VariantsPosGlobalState); |
} |
||||
}); |
Registries.Model.extend(PosGlobalState, VariantsPosGlobalState); |
||||
|
@ -1,34 +1,23 @@ |
|||||
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'; |
||||
|
|
||||
constructor(obj, options) { |
export const PosMultiVariantOrderline = (Orderline) => class PosMultiVariantOrderline extends Orderline { |
||||
super(...arguments); |
constructor(obj, options) { |
||||
this.product_variants = this.product_variants || []; |
super(...arguments); |
||||
} |
this.product_variants = this.product_variants || []; |
||||
export_as_JSON() { |
|
||||
const json = super.export_as_JSON(...arguments); |
|
||||
json.product_variants = this.product_variants || []; |
|
||||
return json; |
|
||||
} |
|
||||
export_for_printing() { |
|
||||
var line = super.export_for_printing(...arguments); |
|
||||
line.product_variants = this.product_variants; |
|
||||
return line; |
|
||||
} |
|
||||
} |
} |
||||
Registries.Model.extend(Orderline, PosMultiVariantOrderline); |
export_as_JSON() { |
||||
}); |
const json = super.export_as_JSON(...arguments); |
||||
|
json.product_variants = this.product_variants || []; |
||||
|
return json; |
||||
|
} |
||||
|
export_for_printing() { |
||||
|
var line = super.export_for_printing(...arguments); |
||||
|
line.product_variants = this.product_variants; |
||||
|
return line; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
Registries.Model.extend(Orderline, PosMultiVariantOrderline); |
||||
|
@ -1,57 +1,53 @@ |
|||||
<?xml version="1.0" encoding="UTF-8"?> |
<?xml version="1.0" encoding="UTF-8"?> |
||||
<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 class="product-category"> |
||||
<div> |
<h2 class="category-title"> |
||||
<h2> |
<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="product-grid"> |
||||
<div class="" style="display:flex; flex-wrap: wrap;justify-content:center; padding:0 1.5rem;"> |
<t t-foreach="props.variant_details" t-as="variant" t-key="variant.id"> |
||||
<t t-foreach="props.variant_details" t-as="variant" |
<t t-foreach="product['value_ids']" t-as="val" t-key="val"> |
||||
t-key="variant.id"> |
<t t-if="variant['id'] == val"> |
||||
<t t-foreach="product['value_ids']" t-as="val" t-key="val"> |
<article class="product-card" |
||||
<t t-if="variant['id'] == val"> |
t-on-click="()=>this.SelectVariant(product,variant)"> |
||||
<!-- Display the product variant --> |
<div t-if="getSelected(attribute, variant)" class="ribbon">Selected</div> |
||||
<article class='product' style="flex-grow: 1;"> |
<div class="product-image"> |
||||
<div class='product-img'> |
<img t-att-src="imageUrl()" alt="Product Variant"/> |
||||
<img t-att-src="imageUrl()"/> |
<div class="price-tag"> |
||||
<span class='extra-price'>+ |
+ <t t-esc="env.pos.format_currency(product['extra_price'])"/> |
||||
<t |
</div> |
||||
t-esc="env.pos.format_currency(product['extra_price'])"/> |
<div class="variant-name"> |
||||
</span> |
<t t-esc="variant['name']"/> |
||||
<h2 class='action' data-price='' |
</div> |
||||
data-type='' |
</div> |
||||
t-att-data-category='attribute'/> |
</article> |
||||
<span class='variants'> |
</t> |
||||
<t t-esc="variant['name']"/> |
</t> |
||||
</span> |
</t> |
||||
</div> |
</div> |
||||
</article> |
</div> |
||||
</t> |
<hr class="section-divider"/> |
||||
</t> |
</t> |
||||
</t> |
</div> |
||||
</div> |
<footer class="footer"> |
||||
</div> |
<div class="button confirm" t-on-click="clickConfirm">Confirm</div> |
||||
<hr/> |
<div class="button cancel" t-on-click="clickCancel"> |
||||
</t> |
Cancel |
||||
<footer class="footer"> |
</div> |
||||
<div class="button confirm"> Confirm </div> |
</footer> |
||||
<div class="button cancel"> |
</div> |
||||
Cancel |
</Draggable> |
||||
</div> |
</t> |
||||
</footer> |
|
||||
</div> |
|
||||
</div> |
|
||||
</t> |
|
||||
</templates> |
</templates> |
Loading…
Reference in new issue