Move all OCA POS modules from oca-technical to dedicated oca-pos submodule

Reorganized 74 POS-related modules for better structure:
- Moved all odoo-bringout-oca-pos-* packages from packages/oca-technical/
- Now organized in dedicated packages/oca-pos/ submodule
- Includes payment, receipt, loyalty, order, product, and partner modules
- Maintains all module functionality while improving project organization

This creates a cleaner separation between general technical modules
and Point of Sale specific functionality.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Ernad Husremovic 2025-08-30 17:15:35 +02:00
parent 3791451dc1
commit 377f346a99
2675 changed files with 93308 additions and 0 deletions

View file

@ -0,0 +1,24 @@
/*
Copyright (C) 2022-Today GRAP (http://www.grap.coop)
@author Sylvain LE GAL (https://twitter.com/legalsylvain)
License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html
*/
.pos .product .membership-forbidden-tag {
color: white;
background: rgba(255, 76, 76, 0.5);
border-radius: 2px;
padding: 1px 4px;
}
.product-info-popup .allowed-membership-category {
display: inline-block;
max-width: 100%;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
vertical-align: top;
max-width: 200px;
color: inherit;
line-height: 1.1;
}

View file

@ -0,0 +1,22 @@
odoo.define("pos_membership_extension.ProductItem", function (require) {
"use strict";
const ProductItem = require("point_of_sale.ProductItem");
const Registries = require("point_of_sale.Registries");
// eslint-disable-next-line no-shadow
const OverloadProductItem = (ProductItem) =>
// eslint-disable-next-line no-shadow
class OverloadProductItem extends ProductItem {
get membership_allowed() {
var res = this.props.product.get_membership_allowed(
this.env.pos.get_order().partner
);
return res;
}
};
Registries.Component.extend(ProductItem, OverloadProductItem);
return ProductItem;
});

View file

@ -0,0 +1,29 @@
odoo.define("pos_membership_extension.ProductScreen", function (require) {
"use strict";
const ProductScreen = require("point_of_sale.ProductScreen");
const Registries = require("point_of_sale.Registries");
// eslint-disable-next-line no-shadow
const OverloadProductScreen = (ProductScreen) =>
// eslint-disable-next-line no-shadow
class OverloadProductScreen extends ProductScreen {
async _getAddProductOptions(product) {
var self = this;
if (!product.get_membership_allowed(this.env.pos.get_order().partner)) {
await this.showPopup("ErrorPopup", {
title: self.env._t("Incorrect Membership"),
body: self.env._t(
"Please select a customer that belong to one of the related membership categories."
),
});
return;
}
return await super._getAddProductOptions(...arguments);
}
};
Registries.Component.extend(ProductScreen, OverloadProductScreen);
return ProductScreen;
});

View file

@ -0,0 +1,76 @@
odoo.define("pos_membership_extension.models", function (require) {
"use strict";
const {Order, Product} = require("point_of_sale.models");
var core = require("web.core");
var {Gui} = require("point_of_sale.Gui");
const Registries = require("point_of_sale.Registries");
var _t = core._t;
const OverloadProduct = (OriginalProduct) =>
class extends OriginalProduct {
/**
* Return if it's allowed to sell the product to the partner.
*
* @param {partner} partner to test. (can be undefined)
* @returns {Boolean} True if the sell is allowed, false otherwise.
*/
get_membership_allowed(partner) {
// No categories means no restriction
if (this.allowed_membership_category_ids.length === 0) {
return true;
}
// If categories are set, but no partner, sell is forbidden in any case
if (!partner) {
return false;
}
var common_categories = this.allowed_membership_category_ids.filter(
function (categ) {
return partner.membership_category_ids.indexOf(categ) !== -1;
}
);
return common_categories.length !== 0;
}
};
Registries.Model.extend(Product, OverloadProduct);
const OverloadOrder = (OriginalOrder) =>
class extends OriginalOrder {
/**
* Overloaded function.
* Check if the product of the order lines are allowed by the
* new selected partner.
* If not, remove according lines and raise a PopUp Error to
* inform the cashier of the removal.
* @param {partner} partner to set to the order. (can be undefined)
* @returns {Boolean} In any case, return the result of the super function.
*/
set_partner(partner) {
var bad_product_list = [];
var i = this.orderlines.length;
while (i--) {
var orderline = this.orderlines[i];
if (!orderline.product.get_membership_allowed(partner)) {
bad_product_list.push(orderline.product.display_name);
this.orderlines.splice(i, 1);
}
}
if (bad_product_list.length !== 0) {
var bad_product_text = bad_product_list.join(", ");
Gui.showPopup("ErrorPopup", {
title: _t("Order Line Removal"),
body: _t(
`The following lines has been removed, as the product cannot be sold to this partner: ${bad_product_text}`
),
});
}
return super.set_partner(...arguments);
}
};
Registries.Model.extend(Order, OverloadOrder);
});

View file

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
Copyright (C) 2022-Today GRAP (http://www.grap.coop)
@author Sylvain LE GAL (https://twitter.com/legalsylvain)
License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html
-->
<templates id="template" xml:space="preserve">
<t
t-name="ProductInfoPopup"
t-inherit="point_of_sale.ProductInfoPopup"
t-inherit-mode="extension"
owl="1"
>
<xpath
expr="//div[hasclass('section-product-info-title')]/div[1]/span[last()]"
position="after"
>
<span
t-if="productInfo.allowed_membership_categories.length"
>Membership Categories:
<t
t-foreach="productInfo.allowed_membership_categories"
t-as="category"
t-key="category.name"
>
<t t-esc="category.name" />
<t
t-if="category_index lt productInfo.allowed_membership_categories.length - 1"
>, </t>
</t>
</span>
</xpath>
</t>
</templates>

View file

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
Copyright (C) 2022-Today GRAP (http://www.grap.coop)
@author Sylvain LE GAL (https://twitter.com/legalsylvain)
License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html
-->
<templates id="template" xml:space="preserve">
<t
t-name="ProductItem"
t-inherit="point_of_sale.ProductItem"
t-inherit-mode="extension"
owl="1"
>
<xpath expr="//span[hasclass('price-tag')]" position="inside">
<i
role="img"
class="membership-forbidden-tag fa fa-ban"
t-if="!membership_allowed"
title="This product can not be sold because of membership rules"
/>
</xpath>
</t>
</templates>