diff --git a/addons/gift_card/__manifest__.py b/addons/gift_card/__manifest__.py
index 67c583400e9..66150174ae0 100644
--- a/addons/gift_card/__manifest__.py
+++ b/addons/gift_card/__manifest__.py
@@ -3,16 +3,14 @@
{
'name': "Gift Card",
- 'summary': "Use gift card in your sales orders",
- 'description': """Integrate gift card mechanism in sales orders.""",
+ 'summary': "Use gift card",
+ 'description': """Integrate gift card mechanism""",
'category': 'Sales/Sales',
'version': '1.0',
- 'depends': ['sale'],
+ 'depends': ['product'],
'data': [
- 'data/mail_template_data.xml',
'data/gift_card_data.xml',
'views/views.xml',
- 'views/templates.xml',
'security/ir.model.access.csv',
],
'license': 'LGPL-3',
diff --git a/addons/gift_card/data/gift_card_data.xml b/addons/gift_card/data/gift_card_data.xml
index 3fb702da8b6..4697d4e7f17 100644
--- a/addons/gift_card/data/gift_card_data.xml
+++ b/addons/gift_card/data/gift_card_data.xml
@@ -8,8 +8,6 @@
service
-
-
@@ -20,7 +18,6 @@
service
-
diff --git a/addons/gift_card/models/__init__.py b/addons/gift_card/models/__init__.py
index b65c1780b11..798243253f1 100644
--- a/addons/gift_card/models/__init__.py
+++ b/addons/gift_card/models/__init__.py
@@ -3,4 +3,3 @@
from . import gift_card
from . import product
-from . import sale_order
diff --git a/addons/gift_card/models/gift_card.py b/addons/gift_card/models/gift_card.py
index 8acc1763a51..516a392c7ed 100644
--- a/addons/gift_card/models/gift_card.py
+++ b/addons/gift_card/models/gift_card.py
@@ -13,10 +13,10 @@ class GiftCard(models.Model):
@api.model
def _generate_code(self):
- return str(uuid4())[4:-8]
+ return '044' + str(uuid4())[4:-8][3:]
name = fields.Char(compute='_compute_name')
- code = fields.Char(default=_generate_code, required=True, readonly=True, copy=False)
+ code = fields.Char(default=lambda x: x._generate_code(), required=True, readonly=True, copy=False)
partner_id = fields.Many2one('res.partner', help="If empty, all users can use it")
company_id = fields.Many2one('res.company', default=lambda self: self.env.company)
currency_id = fields.Many2one('res.currency', readonly=True, related='company_id.currency_id')
@@ -29,10 +29,6 @@ class GiftCard(models.Model):
copy=False
)
- buy_line_id = fields.Many2one("sale.order.line", copy=False, readonly=True,
- help="Sale Order line where this gift card has been bought.")
- redeem_line_ids = fields.One2many('sale.order.line', 'gift_card_id', string="Redeems")
-
_sql_constraints = [
('unique_gift_card_code', 'UNIQUE(code)', 'The gift card code must be unique.'),
('check_amount', 'CHECK(initial_amount >= 0)', 'The initial amount must be positive.')
@@ -42,17 +38,6 @@ class GiftCard(models.Model):
for record in self:
record.name = _("Gift #%s", record.id)
- @api.depends("initial_amount", "redeem_line_ids")
- def _compute_balance(self):
- for record in self:
- balance = record.initial_amount
- confirmed_line = record.redeem_line_ids.filtered(lambda l: l.state == 'sale')
- if confirmed_line:
- balance -= sum(confirmed_line.mapped(
- lambda line: line.currency_id._convert(line.price_unit, record.currency_id, record.env.company, line.create_date) * -1
- ))
- record.balance = balance
-
@api.autovacuum
def _gc_mark_expired_gift_card(self):
self.env['gift.card'].search([
@@ -66,3 +51,8 @@ class GiftCard(models.Model):
def can_be_used(self):
# expired state are computed once a day, so can be not synchro
return self.state == 'valid' and self.balance > 0 and self.expired_date >= fields.Date.today()
+
+ @api.depends("initial_amount")
+ def _compute_balance(self):
+ for record in self:
+ record.balance = record.initial_amount
diff --git a/addons/gift_card/security/ir.model.access.csv b/addons/gift_card/security/ir.model.access.csv
index 8fbe0cc2d13..64ddf646f26 100644
--- a/addons/gift_card/security/ir.model.access.csv
+++ b/addons/gift_card/security/ir.model.access.csv
@@ -1,4 +1,2 @@
id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink
access_gift_card_all,Gift Card Program All,model_gift_card,,0,0,0,0
-access_gift_card_sales,Gift Card Program Salesman,model_gift_card,sales_team.group_sale_salesman,1,0,0,0
-access_gift_card_manager,Gift Card Program Sale Manager,model_gift_card,sales_team.group_sale_manager,1,1,1,1
diff --git a/addons/gift_card/views/views.xml b/addons/gift_card/views/views.xml
index 28ed6652848..19169d9f60b 100644
--- a/addons/gift_card/views/views.xml
+++ b/addons/gift_card/views/views.xml
@@ -1,13 +1,6 @@
-
- Gift Cards
- gift.card
- tree,form
- [('buy_line_id.order_id', '=', active_id)]
-
-
Gift Cards
gift.card
@@ -39,25 +32,17 @@
-
-
+
-
-
-
-
-
-
-
@@ -74,21 +59,4 @@
-
-
- sale.order.view.form.inherit.gift.card
- sale.order
-
-
-
-
-
-
-
diff --git a/addons/point_of_sale/models/pos_order.py b/addons/point_of_sale/models/pos_order.py
index d3198afa72e..db3de94b718 100644
--- a/addons/point_of_sale/models/pos_order.py
+++ b/addons/point_of_sale/models/pos_order.py
@@ -741,13 +741,7 @@ class PosOrder(models.Model):
'target': 'current',
}
- def action_receipt_to_customer(self, name, client, ticket):
- if not self:
- return False
- if not client.get('email'):
- return False
-
- message = _("
diff --git a/addons/pos_gift_card/__init__.py b/addons/pos_gift_card/__init__.py
new file mode 100644
index 00000000000..dc5e6b693d1
--- /dev/null
+++ b/addons/pos_gift_card/__init__.py
@@ -0,0 +1,4 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from . import models
diff --git a/addons/pos_gift_card/__manifest__.py b/addons/pos_gift_card/__manifest__.py
new file mode 100644
index 00000000000..d248853a47c
--- /dev/null
+++ b/addons/pos_gift_card/__manifest__.py
@@ -0,0 +1,32 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+{
+ 'name': "Gift Card for point of sales module",
+ 'summary': "Use gift card in your sales orders",
+ 'description': """Integrate gift card mechanism in sales orders.""",
+ 'category': 'Sales/Sales',
+ 'version': '1.0',
+ 'depends': ['gift_card', 'point_of_sale'],
+ 'auto_install': True,
+ 'data': [
+ 'data/gift_card_data.xml',
+ 'views/gift_card_views.xml',
+ 'views/res_config_settings_views.xml',
+ 'views/pos_config_views.xml',
+ 'security/ir.model.access.csv',
+ ],
+ 'assets': {
+ 'point_of_sale.assets': [
+ 'pos_gift_card/static/src/css/giftCard.css',
+ 'pos_gift_card/static/src/js/models.js',
+ 'pos_gift_card/static/src/js/GiftCardButton.js',
+ 'pos_gift_card/static/src/js/GiftCardPopup.js',
+ 'pos_gift_card/static/src/js/PaymentScreen.js',
+ ],
+ 'web.assets_qweb': [
+ 'pos_gift_card/static/src/xml/**/*',
+ ],
+ },
+ 'license': 'LGPL-3',
+}
diff --git a/addons/pos_gift_card/data/gift_card_data.xml b/addons/pos_gift_card/data/gift_card_data.xml
new file mode 100644
index 00000000000..4ec5db91191
--- /dev/null
+++ b/addons/pos_gift_card/data/gift_card_data.xml
@@ -0,0 +1,54 @@
+
+
+
+
+
+ True
+
+
+
+ Gift Card
+ gift.card
+ qweb-pdf
+ pos_gift_card.gift_card_template
+ report
+
+
+
+
+
+
+
+
+ Here is your gift card!
+
+
+

+
+
+
+
+
+
+
+
+ Gift Card Code
+
+
+
+
+
+
+
+ Card expires
+
+
+
+
![Barcode]()
+
+
+
+
+
+
+
diff --git a/addons/pos_gift_card/models/__init__.py b/addons/pos_gift_card/models/__init__.py
new file mode 100644
index 00000000000..93c9b33a1cb
--- /dev/null
+++ b/addons/pos_gift_card/models/__init__.py
@@ -0,0 +1,8 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from . import barcode_rule
+from . import gift_card
+from . import pos_config
+from . import pos_order
+from . import pos_order_line
diff --git a/addons/pos_gift_card/models/barcode_rule.py b/addons/pos_gift_card/models/barcode_rule.py
new file mode 100644
index 00000000000..bf4a97eec01
--- /dev/null
+++ b/addons/pos_gift_card/models/barcode_rule.py
@@ -0,0 +1,14 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import models, fields
+
+
+class BarcodeRule(models.Model):
+ _inherit = 'barcode.rule'
+
+ type = fields.Selection(selection_add=[
+ ('gift_card', 'Gift Card'),
+ ], ondelete={
+ 'gift_card': 'set default',
+ })
diff --git a/addons/pos_gift_card/models/gift_card.py b/addons/pos_gift_card/models/gift_card.py
new file mode 100644
index 00000000000..0f54772179d
--- /dev/null
+++ b/addons/pos_gift_card/models/gift_card.py
@@ -0,0 +1,47 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import api, fields, models
+
+
+class GiftCard(models.Model):
+ _inherit = "gift.card"
+
+ buy_pos_order_line_id = fields.Many2one(
+ "pos.order.line",
+ copy=False,
+ readonly=True,
+ help="Pos Order line where this gift card has been bought.",
+ )
+ redeem_pos_order_line_ids = fields.One2many(
+ "pos.order.line", "gift_card_id", string="Pos Redeems"
+ )
+
+ @api.depends("redeem_pos_order_line_ids")
+ def _compute_balance(self):
+ super()._compute_balance()
+ for record in self:
+ confirmed_line = record.redeem_pos_order_line_ids.filtered(
+ lambda l: l.order_id.state == "paid"
+ )
+ balance = record.balance
+ if confirmed_line:
+ balance -= sum(
+ confirmed_line.mapped(
+ lambda line: line.currency_id._convert(
+ line.price_unit,
+ record.currency_id,
+ record.env.company,
+ line.create_date,
+ )
+ * -1
+ )
+ )
+ record.balance = balance
+
+ def can_be_used_in_pos(self):
+ # expired state are computed once a day, so can be not synchro
+ return self.state == 'valid' and self.balance > 0 and self.expired_date >= fields.Date.today() and self.buy_pos_order_line_id
+
+ def can_be_used(self):
+ return super(GiftCard, self).can_be_used() and not self.buy_pos_order_line_id
diff --git a/addons/pos_gift_card/models/pos_config.py b/addons/pos_gift_card/models/pos_config.py
new file mode 100644
index 00000000000..ef55d864364
--- /dev/null
+++ b/addons/pos_gift_card/models/pos_config.py
@@ -0,0 +1,36 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import api, fields, models
+
+
+class PosConfig(models.Model):
+ _inherit = "pos.config"
+
+ use_gift_card = fields.Boolean(string="Gift Card")
+
+ gift_card_product_id = fields.Many2one(
+ "product.product",
+ string="Gift Card Product",
+ help="This product is used as reference on customer receipts.",
+ )
+
+ gift_card_settings = fields.Selection(
+ [
+ ("create_set", "Generate a new barcode and set a price"),
+ ("scan_set", "Scan an existing barcode and set a price"),
+ ("scan_use", "Scan an existing barcode with an existing price"),
+ ],
+ string="Gift Cards settings",
+ default="create_set",
+ help="Defines the way you want to set your gift cards.",
+ )
+
+ @api.onchange("use_gift_card")
+ def _onchange_giftproduct(self):
+ if self.use_gift_card:
+ self.gift_card_product_id = self.env.ref(
+ "gift_card.pay_with_gift_card_product", False
+ )
+ else:
+ self.gift_card_product_id = False
diff --git a/addons/pos_gift_card/models/pos_order.py b/addons/pos_gift_card/models/pos_order.py
new file mode 100644
index 00000000000..dace6d2a7da
--- /dev/null
+++ b/addons/pos_gift_card/models/pos_order.py
@@ -0,0 +1,64 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import api, fields, models
+import base64
+
+
+class PosOrder(models.Model):
+ _inherit = "pos.order"
+
+ gift_card_count = fields.Integer(compute="_compute_gift_card_count")
+
+ @api.depends("lines.generated_gift_card_ids")
+ def _compute_gift_card_count(self):
+ for record in self:
+ record.gift_card_count = len(record.lines.mapped("generated_gift_card_ids"))
+
+ @api.model
+ def create_from_ui(self, orders, draft=False):
+ order_ids = super(PosOrder, self).create_from_ui(orders, draft)
+ for order in self.sudo().browse([o["id"] for o in order_ids]):
+ gift_card_config = order.config_id.gift_card_settings
+ for line in order.lines:
+ if line.product_id.id == order.config_id.gift_card_product_id.id:
+ if not line.gift_card_id:
+ if gift_card_config == "create_set":
+ new_card = line._create_gift_cards()
+ new_card.partner_id = order.partner_id or False
+ line.generated_gift_card_ids = new_card
+ else:
+ gift_card = self.env["gift.card"].search(
+ [("id", "=", line.generated_gift_card_ids.id)]
+ )
+ gift_card.buy_pos_order_line_id = line.id
+ gift_card.expired_date = fields.Date.add(
+ fields.Date.today(), years=1
+ )
+ gift_card.partner_id = order.partner_id or False
+
+ if gift_card_config == "scan_set":
+ gift_card.initial_amount = line.price_unit
+
+ return order_ids
+
+ def get_new_card_ids(self):
+ return self.lines.mapped("generated_gift_card_ids").ids
+
+ def _add_mail_attachment(self, name, ticket):
+ attachment = super()._add_mail_attachment(name, ticket)
+ if self.config_id.use_gift_card and len(self.get_new_card_ids()) > 0:
+ report = self.env.ref('pos_gift_card.gift_card_report_pdf')._render_qweb_pdf(self.get_new_card_ids())
+ filename = name + '.pdf'
+ gift_card = self.env['ir.attachment'].create({
+ 'name': filename,
+ 'type': 'binary',
+ 'datas': base64.b64encode(report[0]),
+ 'store_fname': filename,
+ 'res_model': 'pos.order',
+ 'res_id': self.ids[0],
+ 'mimetype': 'application/x-pdf'
+ })
+ attachment += [(4, gift_card.id)]
+
+ return attachment
diff --git a/addons/pos_gift_card/models/pos_order_line.py b/addons/pos_gift_card/models/pos_order_line.py
new file mode 100644
index 00000000000..0f4bc9e9eb7
--- /dev/null
+++ b/addons/pos_gift_card/models/pos_order_line.py
@@ -0,0 +1,34 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import fields, models
+
+
+class PosOrderLine(models.Model):
+ _inherit = "pos.order.line"
+
+ generated_gift_card_ids = fields.One2many(
+ "gift.card", "buy_pos_order_line_id", string="Bought Gift Card"
+ )
+ gift_card_id = fields.Many2one(
+ "gift.card", help="Deducted from this Gift Card", copy=False
+ )
+
+ def _is_not_sellable_line(self):
+ return self.gift_card_id or super()._is_not_sellable_line()
+
+ def _create_gift_cards(self):
+ return self.env["gift.card"].create(
+ [self._build_gift_card() for _ in range(int(self.qty))]
+ )
+
+ def _build_gift_card(self):
+ return {
+ "initial_amount": self.order_id.currency_id._convert(
+ self.price_unit,
+ self.company_id.currency_id,
+ self.company_id,
+ fields.Date.today(),
+ ),
+ "buy_pos_order_line_id": self.id,
+ }
diff --git a/addons/pos_gift_card/security/ir.model.access.csv b/addons/pos_gift_card/security/ir.model.access.csv
new file mode 100644
index 00000000000..1468d9216f2
--- /dev/null
+++ b/addons/pos_gift_card/security/ir.model.access.csv
@@ -0,0 +1,3 @@
+id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink
+access_gift_card_sales,Gift Card Program Salesman,model_gift_card,point_of_sale.group_pos_manager,1,0,0,0
+access_pos_gift_card_manager,Gift Card Program Sale Manager,model_gift_card,point_of_sale.group_pos_manager,1,1,1,1
diff --git a/addons/pos_gift_card/static/src/css/giftCard.css b/addons/pos_gift_card/static/src/css/giftCard.css
new file mode 100644
index 00000000000..9332b7a1a2b
--- /dev/null
+++ b/addons/pos_gift_card/static/src/css/giftCard.css
@@ -0,0 +1,45 @@
+.giftCardPopupMain {
+ display: flex;
+ flex-wrap: wrap;
+
+ justify-content: space-around;
+
+ text-align: center;
+ padding: 1em;
+}
+
+.giftCardPopupContainer {
+ display: flex;
+ justify-content: center;
+ flex-wrap: wrap;
+ padding: 5px;
+
+ font-weight: bold;
+ text-align: center;
+ width: 100%;
+}
+
+.giftCardPopupFooter {
+ display: flex;
+ justify-content: space-evenly;
+
+ text-align: center;
+}
+
+.giftCardPopupInput {
+ border-bottom: 1px solid !important;
+ background-color: #F0EEEE !important;
+ box-shadow: none !important;
+ text-align: right !important;
+}
+
+.giftCardPopupButton {
+ width: 50% !important;
+ margin: 6px;
+}
+
+.giftCardPopupConfirmButton {
+ width: 40% !important;
+}
+
+.giftCardPopupIn
diff --git a/addons/pos_gift_card/static/src/js/GiftCardButton.js b/addons/pos_gift_card/static/src/js/GiftCardButton.js
new file mode 100644
index 00000000000..a8782865291
--- /dev/null
+++ b/addons/pos_gift_card/static/src/js/GiftCardButton.js
@@ -0,0 +1,31 @@
+odoo.define("pos_gift_card.GiftCardButton", function (require) {
+ "use strict";
+
+ const PosComponent = require("point_of_sale.PosComponent");
+ const ProductScreen = require("point_of_sale.ProductScreen");
+ const { useListener } = require("web.custom_hooks");
+ const Registries = require("point_of_sale.Registries");
+ const { Gui } = require("point_of_sale.Gui");
+
+ class GiftCardButton extends PosComponent {
+ constructor() {
+ super(...arguments);
+ useListener("click", this.onClick);
+ }
+ async onClick() {
+ this.showPopup("GiftCardPopup", {});
+ }
+ }
+ GiftCardButton.template = "GiftCardButton";
+
+ ProductScreen.addControlButton({
+ component: GiftCardButton,
+ condition: function () {
+ return this.env.pos.config.use_gift_card;
+ },
+ });
+
+ Registries.Component.add(GiftCardButton);
+
+ return GiftCardButton;
+});
diff --git a/addons/pos_gift_card/static/src/js/GiftCardPopup.js b/addons/pos_gift_card/static/src/js/GiftCardPopup.js
new file mode 100644
index 00000000000..1adfdbb919f
--- /dev/null
+++ b/addons/pos_gift_card/static/src/js/GiftCardPopup.js
@@ -0,0 +1,151 @@
+odoo.define("pos_gift_card.GiftCardPopup", function (require) {
+ "use strict";
+
+ const { useState, useRef } = owl.hooks;
+ const AbstractAwaitablePopup = require("point_of_sale.AbstractAwaitablePopup");
+ const Registries = require("point_of_sale.Registries");
+
+ class GiftCardPopup extends AbstractAwaitablePopup {
+ constructor() {
+ super(...arguments);
+ this.state = useState({
+ giftCardConfig: this.env.pos.config.gift_card_settings,
+ showBarcodeGeneration: false,
+ showNewGiftCardMenu: false,
+ showUseGiftCardMenu: false,
+ showGiftCardDetails: false,
+ amountToSet: 0,
+ giftCardBarcode: "",
+ });
+ }
+
+ switchBarcodeView() {
+ this.state.showBarcodeGeneration = !this.state.showBarcodeGeneration;
+ if (this.state.showUseGiftCardMenu)
+ this.state.showUseGiftCardMenu = false;
+ if (this.state.showGiftCardDetails)
+ this.state.showGiftCardDetails = false;
+ }
+
+ switchToUseGiftCardMenu() {
+ this.switchBarcodeView();
+ this.state.showUseGiftCardMenu = true;
+ }
+
+ switchToShowGiftCardDetails() {
+ this.switchBarcodeView();
+ this.state.showGiftCardDetails = true;
+ }
+
+ addGiftCardProduct(giftCard) {
+ let gift =
+ this.env.pos.db.product_by_id[
+ this.env.pos.config.gift_card_product_id[0]
+ ];
+ this.env.pos.get_order().add_product(gift, {
+ price: this.state.amountToSet,
+ quantity: 1,
+ merge: false,
+ generated_gift_card_ids: giftCard ? giftCard.id : false,
+ });
+ }
+
+ async getGiftCard() {
+ if (this.state.giftCardBarcode == "") return;
+
+ let giftCard = this.env.pos.giftCard.find(
+ (gift) => gift.code === this.state.giftCardBarcode
+ );
+
+ if (!giftCard) {
+ giftCard = await this.rpc({
+ model: "gift.card",
+ method: "search_read",
+ args: [[["code", "=", this.state.giftCardBarcode]]],
+ fields: ["code", "initial_amount", "balance"],
+ });
+ if (giftCard.length) {
+ this.env.pos.giftCard.push(giftCard[0])
+ giftCard = giftCard[0];
+ } else {
+ return false;
+ }
+ }
+
+ return giftCard;
+ }
+
+ async scanAndUseGiftCard() {
+ let giftCard = await this.getGiftCard();
+ if (!giftCard) return;
+
+ if (this.state.giftCardConfig === "scan_use")
+ this.state.amountToSet = giftCard.initial_amount;
+
+ this.addGiftCardProduct(giftCard);
+ this.cancel();
+ }
+
+ async generateBarcode() {
+ this.addGiftCardProduct(false);
+ this.confirm();
+ }
+
+ async isGiftCardAlreadyUsed() {
+ let order = this.env.pos.get_order();
+ let giftProduct =
+ this.env.pos.db.product_by_id[
+ this.env.pos.config.gift_card_product_id[0]
+ ];
+
+ for (let line of order.orderlines.models) {
+ if (line.product.id === giftProduct.id && line.price < 0) {
+ if (line.gift_card_id === await this.getGiftCard().id) return line;
+ }
+ }
+ return false;
+ }
+
+ getPriceToRemove(giftCard) {
+ let currentOrder = this.env.pos.get_order();
+ return currentOrder.get_total_with_tax() > giftCard.balance
+ ? -giftCard.balance
+ : -currentOrder.get_total_with_tax();
+ }
+
+ async payWithGiftCard() {
+ let giftCard = await this.getGiftCard();
+ if (!giftCard) return;
+
+ let gift =
+ this.env.pos.db.product_by_id[
+ this.env.pos.config.gift_card_product_id[0]
+ ];
+
+ let currentOrder = this.env.pos.get_order();
+ let lineUsed = this.isGiftCardAlreadyUsed()
+ if (lineUsed) currentOrder.remove_orderline(lineUsed);
+
+ currentOrder.add_product(gift, {
+ price: this.getPriceToRemove(giftCard),
+ quantity: 1,
+ merge: false,
+ gift_card_id: giftCard.id,
+ });
+
+ this.cancel();
+ }
+
+ async ShowRemainingAmount() {
+ let giftCard = await this.getGiftCard();
+ if (!giftCard) return;
+
+ this.state.amountToSet = giftCard.balance;
+ }
+ }
+ GiftCardPopup.template = "GiftCardPopup";
+
+ Registries.Component.add(GiftCardPopup);
+
+ return GiftCardPopup;
+});
diff --git a/addons/pos_gift_card/static/src/js/PaymentScreen.js b/addons/pos_gift_card/static/src/js/PaymentScreen.js
new file mode 100644
index 00000000000..edb3d0f516e
--- /dev/null
+++ b/addons/pos_gift_card/static/src/js/PaymentScreen.js
@@ -0,0 +1,76 @@
+odoo.define('pos_gift_card.PaymentScreen', function(require) {
+ "use strict";
+
+ const PaymentScreen = require('point_of_sale.PaymentScreen');
+ const Registries = require('point_of_sale.Registries');
+ var core = require('web.core');
+ var _t = core._t;
+
+
+ const PosGiftCardPaymentScreen = PaymentScreen => class extends PaymentScreen {
+ //@Override
+ async validateOrder(isForceValidate) {
+ if(this.env.pos.config.use_gift_card) {
+ if (await this._isOrderValid(isForceValidate)) {
+ try {
+ let giftProduct = this.env.pos.db.product_by_id[this.env.pos.config.gift_card_product_id[0]];
+
+ for (let line of this.currentOrder.orderlines.models) {
+ if(line.product.id === giftProduct.id && line.price <= 0) {
+ let is_valid = await this.rpc({
+ model: "gift.card",
+ method: 'can_be_used_in_pos',
+ args: [line.gift_card_id],
+ });
+
+ if(!is_valid) {
+ await this.showPopup('ErrorPopup', {
+ 'title': _t("Gift Card Error"),
+ 'body': _t("Gift card is not valid."),
+ });
+ return;
+ }
+
+ let gift_card = await this.rpc({
+ model: "gift.card",
+ method: 'search_read',
+ domain: [['id', '=', line.gift_card_id]],
+ fields: ['balance'],
+ });
+
+ if(Math.abs(line.price) > gift_card[0].balance) {
+ await this.showPopup('ErrorPopup', {
+ 'title': _t("Gift Card Error"),
+ 'body': _t("Gift card balance is too low."),
+ });
+ return;
+ }
+ this.env.pos.giftCard.find(gift => gift.id === gift_card[0].id).balance += line.price;
+ }
+ }
+ } catch (e) {
+ // do nothing with the error
+ }
+ }
+ }
+ await super.validateOrder(...arguments);
+ }
+
+ async _postPushOrderResolve(order, server_ids) {
+ if(this.env.pos.config.use_gift_card) {
+ let ids = await this.rpc({
+ model: 'pos.order',
+ method: 'get_new_card_ids',
+ args: [server_ids]
+ });
+ if(ids.length > 0)
+ this.env.pos.print_gift_pdf(ids);
+ }
+ return super._postPushOrderResolve(order, server_ids);
+ }
+ };
+
+ Registries.Component.extend(PaymentScreen, PosGiftCardPaymentScreen);
+
+ return PosGiftCardPaymentScreen;
+});
diff --git a/addons/pos_gift_card/static/src/js/models.js b/addons/pos_gift_card/static/src/js/models.js
new file mode 100644
index 00000000000..609b0842593
--- /dev/null
+++ b/addons/pos_gift_card/static/src/js/models.js
@@ -0,0 +1,86 @@
+odoo.define("pos_gift_card.gift_card", function (require) {
+ "use strict";
+
+ const models = require("point_of_sale.models");
+
+ models.load_fields("pos.order.line", "generated_gift_card_ids");
+ models.load_fields("pos.order.line", "redeem_pos_order_line_ids");
+
+ // Load the products used for creating program reward lines.
+ var existing_models = models.PosModel.prototype.models;
+ var product_index = _.findIndex(existing_models, function (model) {
+ return model.model === 'product.product';
+ });
+ var product_model = existing_models[product_index];
+
+ models.load_models([
+ {
+ model: "gift.card",
+ fields: ["code", "initial_amount", "balance"],
+ loaded: function (self, giftCard) {
+ self.giftCard = giftCard;
+ },
+ }, {
+ model: product_model.model,
+ fields: product_model.fields,
+ order: product_model.order,
+ domain: function (self) {
+ return [['id', '=', self.config.gift_card_product_id[0]]];
+ },
+ context: product_model.context,
+ loaded: product_model.loaded,
+ },
+ ]);
+
+ var _order_super = models.Order.prototype;
+ models.Order = models.Order.extend({
+ set_orderline_options: function (orderline, options) {
+ _order_super.set_orderline_options.apply(this, [orderline, options]);
+ if (options && options.generated_gift_card_ids) {
+ orderline.generated_gift_card_ids = [options.generated_gift_card_ids];
+ }
+ if (options && options.gift_card_id) {
+ orderline.gift_card_id = options.gift_card_id;
+ }
+ },
+ wait_for_push_order: function () {
+ if(this.pos.config.use_gift_card) {
+ let giftProduct = this.pos.db.product_by_id[this.pos.config.gift_card_product_id[0]];
+ for (let line of this.orderlines.models) {
+ if(line.product.id === giftProduct.id)
+ return true;
+ }
+ }
+ return _order_super.wait_for_push_order.apply(this, arguments);
+ },
+ });
+
+ var _super_orderline = models.Orderline;
+ models.Orderline = models.Orderline.extend({
+ export_as_JSON: function () {
+ var json = _super_orderline.prototype.export_as_JSON.apply(
+ this,
+ arguments
+ );
+ json.generated_gift_card_ids = this.generated_gift_card_ids;
+ json.gift_card_id = this.gift_card_id;
+ return json;
+ },
+ init_from_JSON: function (json) {
+ _super_orderline.prototype.init_from_JSON.apply(this, arguments);
+ this.generated_gift_card_ids = json.generated_gift_card_ids;
+ this.gift_card_id = json.gift_card_id;
+ },
+ });
+
+ var _posmodel_super = models.PosModel.prototype;
+ models.PosModel = models.PosModel.extend({
+ print_gift_pdf: function (giftCardIds) {
+ this.do_action('pos_gift_card.gift_card_report_pdf', {
+ additional_context: {
+ active_ids: [giftCardIds],
+ },
+ })
+ }
+ });
+});
diff --git a/addons/pos_gift_card/static/src/xml/GiftCardButton.xml b/addons/pos_gift_card/static/src/xml/GiftCardButton.xml
new file mode 100644
index 00000000000..0ac7c533ac0
--- /dev/null
+++ b/addons/pos_gift_card/static/src/xml/GiftCardButton.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+ Gift Card
+
+
+
+
diff --git a/addons/pos_gift_card/static/src/xml/GiftCardPopup.xml b/addons/pos_gift_card/static/src/xml/GiftCardPopup.xml
new file mode 100644
index 00000000000..a61f3a7e0cf
--- /dev/null
+++ b/addons/pos_gift_card/static/src/xml/GiftCardPopup.xml
@@ -0,0 +1,140 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/addons/pos_gift_card/views/gift_card_views.xml b/addons/pos_gift_card/views/gift_card_views.xml
new file mode 100644
index 00000000000..8904e07c1fd
--- /dev/null
+++ b/addons/pos_gift_card/views/gift_card_views.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+ gift.card.form Website
+ gift.card
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/addons/pos_gift_card/views/pos_config_views.xml b/addons/pos_gift_card/views/pos_config_views.xml
new file mode 100644
index 00000000000..9ae52ae0a82
--- /dev/null
+++ b/addons/pos_gift_card/views/pos_config_views.xml
@@ -0,0 +1,29 @@
+
+
+
+ pos.config.form
+ pos.config
+
+
+
+
+
+
+
+
diff --git a/addons/pos_gift_card/views/res_config_settings_views.xml b/addons/pos_gift_card/views/res_config_settings_views.xml
new file mode 100644
index 00000000000..702b619466d
--- /dev/null
+++ b/addons/pos_gift_card/views/res_config_settings_views.xml
@@ -0,0 +1,15 @@
+
+
+
+ res.config.form.inherit.pos.coupon
+ res.config.settings
+
+
+
+
+
+
+
+
+
+
diff --git a/addons/sale_gift_card/__init__.py b/addons/sale_gift_card/__init__.py
new file mode 100644
index 00000000000..dc5e6b693d1
--- /dev/null
+++ b/addons/sale_gift_card/__init__.py
@@ -0,0 +1,4 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from . import models
diff --git a/addons/sale_gift_card/__manifest__.py b/addons/sale_gift_card/__manifest__.py
new file mode 100644
index 00000000000..328f215e89e
--- /dev/null
+++ b/addons/sale_gift_card/__manifest__.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+{
+ 'name': "Gift Card for sales module",
+ 'summary': "Use gift card in your sales orders",
+ 'description': """Integrate gift card mechanism in sales orders.""",
+ 'category': 'Sales/Sales',
+ 'version': '1.0',
+ 'depends': ['gift_card', 'sale'],
+ 'auto_install': True,
+ 'data': [
+ 'data/gift_card_data.xml',
+ 'data/mail_template_data.xml',
+ 'views/sale_order_view.xml',
+ 'views/templates.xml',
+ 'security/ir.model.access.csv',
+ ],
+ 'license': 'LGPL-3',
+}
diff --git a/addons/sale_gift_card/data/gift_card_data.xml b/addons/sale_gift_card/data/gift_card_data.xml
new file mode 100644
index 00000000000..1830684c64e
--- /dev/null
+++ b/addons/sale_gift_card/data/gift_card_data.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/addons/gift_card/data/mail_template_data.xml b/addons/sale_gift_card/data/mail_template_data.xml
similarity index 100%
rename from addons/gift_card/data/mail_template_data.xml
rename to addons/sale_gift_card/data/mail_template_data.xml
diff --git a/addons/sale_gift_card/models/__init__.py b/addons/sale_gift_card/models/__init__.py
new file mode 100644
index 00000000000..733bcda9ea8
--- /dev/null
+++ b/addons/sale_gift_card/models/__init__.py
@@ -0,0 +1,5 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from . import gift_card
+from . import sale_order
diff --git a/addons/sale_gift_card/models/gift_card.py b/addons/sale_gift_card/models/gift_card.py
new file mode 100644
index 00000000000..1949f03e3dd
--- /dev/null
+++ b/addons/sale_gift_card/models/gift_card.py
@@ -0,0 +1,24 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import api, fields, models
+
+
+class GiftCard(models.Model):
+ _inherit = "gift.card"
+
+ buy_line_id = fields.Many2one("sale.order.line", copy=False, readonly=True,
+ help="Sale Order line where this gift card has been bought.")
+ redeem_line_ids = fields.One2many('sale.order.line', 'gift_card_id', string="Redeems")
+
+ @api.depends("redeem_line_ids")
+ def _compute_balance(self):
+ super()._compute_balance()
+ for record in self:
+ confirmed_line = record.redeem_line_ids.filtered(lambda l: l.state == 'sale')
+ balance = record.balance
+ if confirmed_line:
+ balance -= sum(confirmed_line.mapped(
+ lambda line: line.currency_id._convert(line.price_unit, record.currency_id, record.env.company, line.create_date) * -1
+ ))
+ record.balance = balance
diff --git a/addons/gift_card/models/sale_order.py b/addons/sale_gift_card/models/sale_order.py
similarity index 97%
rename from addons/gift_card/models/sale_order.py
rename to addons/sale_gift_card/models/sale_order.py
index 8329b6fc14a..9c0fbc53d1d 100644
--- a/addons/gift_card/models/sale_order.py
+++ b/addons/sale_gift_card/models/sale_order.py
@@ -51,7 +51,7 @@ class SaleOrder(models.Model):
return error
def _send_gift_card_mail(self):
- template = self.env.ref('gift_card.mail_template_gift_card', raise_if_not_found=False)
+ template = self.env.ref('sale_gift_card.mail_template_gift_card', raise_if_not_found=False)
if template and self.gift_card_count:
for gift in self.order_line.mapped("generated_gift_card_ids"):
template.send_mail(gift.id, force_send=True, notif_layout='mail.mail_notification_light')
diff --git a/addons/sale_gift_card/security/ir.model.access.csv b/addons/sale_gift_card/security/ir.model.access.csv
new file mode 100644
index 00000000000..99bb48e912b
--- /dev/null
+++ b/addons/sale_gift_card/security/ir.model.access.csv
@@ -0,0 +1,3 @@
+id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink
+access_gift_card_sales,Gift Card Program Salesman,model_gift_card,sales_team.group_sale_salesman,1,0,0,0
+access_gift_card_manager,Gift Card Program Sale Manager,model_gift_card,sales_team.group_sale_manager,1,1,1,1
diff --git a/addons/gift_card/tests/__init__.py b/addons/sale_gift_card/tests/__init__.py
similarity index 100%
rename from addons/gift_card/tests/__init__.py
rename to addons/sale_gift_card/tests/__init__.py
diff --git a/addons/gift_card/tests/common.py b/addons/sale_gift_card/tests/common.py
similarity index 100%
rename from addons/gift_card/tests/common.py
rename to addons/sale_gift_card/tests/common.py
diff --git a/addons/gift_card/tests/test_buy_gift_card.py b/addons/sale_gift_card/tests/test_buy_gift_card.py
similarity index 96%
rename from addons/gift_card/tests/test_buy_gift_card.py
rename to addons/sale_gift_card/tests/test_buy_gift_card.py
index 9c26ff425a1..c3593ec6192 100644
--- a/addons/gift_card/tests/test_buy_gift_card.py
+++ b/addons/sale_gift_card/tests/test_buy_gift_card.py
@@ -2,7 +2,7 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import Command
-from odoo.addons.gift_card.tests.common import TestSaleGiftCardCommon
+from odoo.addons.sale_gift_card.tests.common import TestSaleGiftCardCommon
class TestBuyGiftCard(TestSaleGiftCardCommon):
diff --git a/addons/gift_card/tests/test_pay_with_gift_card.py b/addons/sale_gift_card/tests/test_pay_with_gift_card.py
similarity index 96%
rename from addons/gift_card/tests/test_pay_with_gift_card.py
rename to addons/sale_gift_card/tests/test_pay_with_gift_card.py
index 1a96561f605..c92cd5df128 100644
--- a/addons/gift_card/tests/test_pay_with_gift_card.py
+++ b/addons/sale_gift_card/tests/test_pay_with_gift_card.py
@@ -2,7 +2,7 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import Command
-from odoo.addons.gift_card.tests.common import TestSaleGiftCardCommon
+from odoo.addons.sale_gift_card.tests.common import TestSaleGiftCardCommon
from odoo.exceptions import UserError
class TestPayWithGiftCard(TestSaleGiftCardCommon):
diff --git a/addons/sale_gift_card/views/sale_order_view.xml b/addons/sale_gift_card/views/sale_order_view.xml
new file mode 100644
index 00000000000..4c5afdab155
--- /dev/null
+++ b/addons/sale_gift_card/views/sale_order_view.xml
@@ -0,0 +1,47 @@
+
+
+
+
+ Gift Cards
+ gift.card
+ tree,form
+ [('buy_line_id.order_id', '=', active_id)]
+
+
+
+ sale.order.view.form.inherit.gift.card
+ sale.order
+
+
+
+
+
+
+
+
+
+ gift.card.form Website
+ gift.card
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/addons/gift_card/views/templates.xml b/addons/sale_gift_card/views/templates.xml
similarity index 91%
rename from addons/gift_card/views/templates.xml
rename to addons/sale_gift_card/views/templates.xml
index b69f1ad6a7a..45f0e65051b 100644
--- a/addons/gift_card/views/templates.xml
+++ b/addons/sale_gift_card/views/templates.xml
@@ -1,7 +1,7 @@
-
+
Code:
@@ -17,8 +17,8 @@
-
-
+
+
diff --git a/addons/website_sale_gift_card/__manifest__.py b/addons/website_sale_gift_card/__manifest__.py
index 9e20de458c9..314da51ab03 100644
--- a/addons/website_sale_gift_card/__manifest__.py
+++ b/addons/website_sale_gift_card/__manifest__.py
@@ -7,7 +7,7 @@
'description': """Integrate gift card mechanism in your ecommerce.""",
'category': 'Website/Website',
'version': '1.0',
- 'depends': ['website_sale', 'gift_card'],
+ 'depends': ['website_sale', 'sale_gift_card'],
'data': [
'views/template.xml',
'views/gift_card_views.xml',
diff --git a/addons/website_sale_gift_card/views/template.xml b/addons/website_sale_gift_card/views/template.xml
index 12496b757d1..872bc8b936d 100644
--- a/addons/website_sale_gift_card/views/template.xml
+++ b/addons/website_sale_gift_card/views/template.xml
@@ -25,7 +25,7 @@
-
+
@@ -51,13 +51,13 @@
-
+
-
+