Merge pull request #5223 from expsa/fix_table_width_form

[UDP] update catalog button action, add "Add" custom button to window…
This commit is contained in:
Tahir Hassan 2025-11-05 13:17:16 +04:00 committed by GitHub
commit 925d0becc6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 243 additions and 170 deletions

View File

@ -7303,6 +7303,18 @@ msgstr ""
"<i class=\"fa fa-shopping-cart\"/>\n"
" <span>إضافة</span>"
#. module: odex_takaful
#: code:addons/odex_takaful/models/donation_details_lines.py:0
#: code:addons/odex_takaful/models/takaful_sponorship_model.py:0
#, python-format
msgid "Choose Donation Items"
msgstr "أختر بنود التبرع"
#. module: odex_takaful
#: model_terms:ir.ui.view,arch_db:odex_takaful.product_template_view_kanban_odex_takaful
msgid "<span>Amount:</span>"
msgstr "<span>القيمة:</span>"
#. module: odex_takaful
#: model_terms:ir.ui.view,arch_db:odex_takaful.family_member_form_inherit_donation_button
msgid "Kafala Information"

View File

@ -252,8 +252,10 @@ class TakafulSponsorship(models.Model):
('donation_category', '=', 'donation')],
'context': {
# '_quantity_change': True,
'sponsorship_id': self.id,
'create': False
'sponsorship_id': self._origin.id,
'create': False,
'hide_breadcrumbs': True,
'action_code': 'donation_items'
},
'target': "new",
'help': _("""<p class="o_view_nocontent_smiling_face">

View File

@ -0,0 +1,62 @@
odoo.define('odex_takaful.custom_dialog', function (require) {
"use strict";
const AbstractAction = require('web.AbstractAction');
const Dialog = require('web.Dialog');
const core = require('web.core');
const _t = core._t;
const ActionManager = require('web.ActionManager');
Dialog.include({
/**
* @override
*/
open: function() {
var self = this;
this.opened(function() {
setTimeout(function () {
var parent = self.getParent();
if (parent instanceof ActionManager) {
var action = parent.getCurrentActionInDialog();
if (action) {
if(action.context){
let context = action.context;
if(context.action_code){
if(context.action_code === 'donation_items'){
if (self.$footer) {
const closeBtn = $('<button/>', {
text: _t('Add'),
class: 'btn btn-primary btn-close-custom',
}).on('click', () => self.close());
self.$footer.append(closeBtn);
}
}
}
if(context.sponsorship_id){
$('#cspid').remove();
$('body').append(`<input type="hidden" id="cspid" value="${context.sponsorship_id}"/>`)
}
}
}
if(self.$modal){
self.$modal.find('.modal-header button.close').hide();
self.$modal.find('.modal-header .modal-title').css("width","100%");
}
if(self.$el){
self.$el.find('.o_cp_top_left').hide();
self.$el.find('.o_cp_bottom_left').hide();
self.$el.find('.o_cp_top_right').css("width","100%");
self.$el.find('.o_cp_bottom_right').css("width","100%");
}
}
}, 0);
});
return this._super.apply(this, arguments);
},
});
});

View File

@ -1,171 +1,165 @@
// Donation Catalog Quantity Controls
// Handles plus/minus buttons in the donation catalog popup
odoo.define('odex_takaful.DonationQuantityWidget', function (require) {
"use strict";
// Attach event handlers immediately using event delegation
// Odoo converts 'name' attribute to 'data-name' in rendered HTML
$(document).on('click', 'button[data-name="add_quantity_button_request"]', function(e) {
e.preventDefault();
e.stopPropagation();
handleQuantityChange($(this), 'first_add');
return false; // ensure Odoo's default kanban action handler does not run
});
$(document).on('click', 'button[data-name="add_quantity_button_so"]', function(e) {
e.preventDefault();
e.stopPropagation();
handleQuantityChange($(this), 'add');
return false; // ensure Odoo's default kanban action handler does not run
});
$(document).on('click', 'button[data-name="remove_quantity_button_so"]', function(e) {
e.preventDefault();
e.stopPropagation();
handleQuantityChange($(this), 'remove');
return false; // ensure Odoo's default kanban action handler does not run
});
// Expose imperative handlers as a reliable fallback (callable from inline onclick if needed)
window.__dc_add = function(btn) {
return __dc_handle(btn, 'add');
};
window.__dc_remove = function(btn) {
return __dc_handle(btn, 'remove');
};
window.__dc_first_add = function(btn) {
return __dc_handle(btn, 'first_add');
};
function __dc_handle(btn, operation) {
try {
const $btn = $(btn);
handleQuantityChange($btn, operation);
} catch (_e) {
// no-op
}
return false;
}
async function handleQuantityChange($button, operation) {
// Get the kanban record (product card) container
const $kanbanRecord = $button.closest('.o_kanban_record');
if (!$kanbanRecord.length) {
return;
}
// Get quantity display element from the button's parent container using stable custom classes
let $container = $button.closest('.dc-qty-controls');
if (operation === 'first_add'){
$container = $kanbanRecord.find('.dc-qty-controls')
}
let $quantityDisplay = $container.find('.dc-qty-badge');
// if (!$quantityDisplay.length) {
// return;
// }
// Extract product ID from the image URL
const $img = $kanbanRecord.find('.o_kanban_image img');
// if (!$img.length) {
// return;
// }
const imgSrc = $img.attr('src');
const match = imgSrc.match(/[&?]id=(\d+)/);
// if (!match || !match[1]) {
// return;
// }
const productId = parseInt(match[1]);
let currentQuantity = parseFloat($quantityDisplay.text()) || 0;
const Widget = require('web.Widget');
const rpc = require('web.rpc');
const core = require('web.core');
const QWeb = core.qweb;
const _t = core._t;
// Prevent going below zero for remove operation
if (operation === 'remove' && currentQuantity <= 0) {
return;
let o_cart_kanban_donation = $('.o_cart_kanban_donation');
if(o_cart_kanban_donation){
$(o_cart_kanban_donation).parents('.o_act_window').find('.o_cp_top_left').remove();
}
// Resolve sponsorship id from multiple sources (robust against context differences)
let sponsorship_id;
try {
const stored = sessionStorage.getItem("current_action");
if (stored) {
const parsed = JSON.parse(stored);
const data = JSON.parse(parsed);
sponsorship_id = data?.context?.params?.id || data?.context?.sponsorship_id;
$(document).on('click', 'button[data-name="add_quantity_button_request"]', function (e) {
e.preventDefault();
e.stopPropagation();
handleQuantityChange($(this), 'first_add');
return false; // ensure Odoo's default kanban action handler does not run
});
$(document).on('click', 'button[data-name="add_quantity_button_so"]', function (e) {
e.preventDefault();
e.stopPropagation();
handleQuantityChange($(this), 'add');
return false; // ensure Odoo's default kanban action handler does not run
});
$(document).on('click', 'button[data-name="remove_quantity_button_so"]', function (e) {
e.preventDefault();
e.stopPropagation();
handleQuantityChange($(this), 'remove');
return false; // ensure Odoo's default kanban action handler does not run
});
// Expose imperative handlers as a reliable fallback (callable from inline onclick if needed)
window.__dc_add = function (btn) {
return __dc_handle(btn, 'add');
};
window.__dc_remove = function (btn) {
return __dc_handle(btn, 'remove');
};
window.__dc_first_add = function (btn) {
return __dc_handle(btn, 'first_add');
};
function __dc_handle(btn, operation) {
try {
const $btn = $(btn);
handleQuantityChange($btn, operation);
} catch (_e) {
// no-op
}
} catch (_err) {
// ignore, fall back to other strategies
}
if (!sponsorship_id) {
const $form = $('.o_form_view');
const resIdFromDom = $form && ($form.data('res-id') || $form.attr('data-res-id'));
if (resIdFromDom) sponsorship_id = parseInt(resIdFromDom);
}
if (!sponsorship_id) {
const hash = window.location.hash || '';
const m = hash.match(/[&#]id=(\d+)/);
if (m) sponsorship_id = parseInt(m[1]);
}
if (!sponsorship_id) {
return;
}
sponsorship_id = parseInt(sponsorship_id);
// Disable buttons during operation
$button.prop('disabled', true).addClass('o_btn_loading');
$button.siblings('button').prop('disabled', true);
// Calculate new quantity
let newQuantity = currentQuantity;
if (operation === 'first_add'){
$button.addClass('d-none');
$kanbanRecord.find('.dc-qty-controls').removeClass("d-none");
return false;
}
if (operation === 'add' || operation === 'first_add') {
newQuantity = newQuantity + 1;
}
if (operation === 'remove') {
newQuantity = newQuantity - 1;
}
// Update display optimistically
$quantityDisplay.text(newQuantity.toFixed(2));
try {
// Call server to update quantity
await $.ajax({
url: "/qtyupdatecart_so",
method: "GET",
dataType: 'json',
data: {
quantity: newQuantity,
product_id: productId,
sponsorship_id: sponsorship_id
}
}).catch(function(jqXHR) {
// Re-throw with more details
const info = {
status: jqXHR?.status,
responseText: jqXHR?.responseText,
readyState: jqXHR?.readyState
};
throw info;
});
} catch (error) {
// Revert on error
$quantityDisplay.text(currentQuantity.toFixed(2));
// $quantityDisplay.addClass('bg-danger').removeClass('bg-secondary');
setTimeout(() => {
// $quantityDisplay.removeClass('bg-danger').addClass('bg-secondary');
}, 1000);
} finally {
// Re-enable buttons
$button.prop('disabled', false).removeClass('o_btn_loading');
$button.siblings('button').prop('disabled', false);
}
}
async function handleQuantityChange($button, operation) {
// Get the kanban record (product card) container
const $kanbanRecord = $button.closest('.o_kanban_record');
if (!$kanbanRecord.length) {
return;
}
// Get quantity display element from the button's parent container using stable custom classes
let $container = $button.closest('.dc-qty-controls');
if (operation === 'first_add') {
$container = $kanbanRecord.find('.dc-qty-controls')
}
let $quantityDisplay = $container.find('.dc-qty-badge');
// if (!$quantityDisplay.length) {
// return;
// }
// Extract product ID from the image URL
const $img = $kanbanRecord.find('.o_kanban_image img');
// if (!$img.length) {
// return;
// }
const imgSrc = $img.attr('src');
const match = imgSrc.match(/[&?]id=(\d+)/);
// if (!match || !match[1]) {
// return;
// }
const productId = parseInt(match[1]);
let currentQuantity = parseFloat($quantityDisplay.text()) || 0;
// Prevent going below zero for remove operation
if (operation === 'remove' && currentQuantity <= 0) {
return;
}
// Resolve sponsorship id from multiple sources (robust against context differences)
let sponsorship_id;
if (!sponsorship_id) {
const $input = $('#cspid');
if ($input) sponsorship_id = parseInt($input.val());
}
if (!sponsorship_id) {
return;
}
// Disable buttons during operation
$button.prop('disabled', true).addClass('o_btn_loading');
$button.siblings('button').prop('disabled', true);
// Calculate new quantity
let newQuantity = currentQuantity;
if (operation === 'first_add') {
$button.addClass('d-none');
$kanbanRecord.find('.dc-qty-controls').removeClass("d-none");
}
if (operation === 'add' || operation === 'first_add') {
newQuantity = newQuantity + 1;
}
if (operation === 'remove') {
newQuantity = newQuantity - 1;
}
// Update display optimistically
$quantityDisplay.text(newQuantity);
try {
// Call server to update quantity
await $.ajax({
url: "/qtyupdatecart_so",
method: "GET",
dataType: 'json',
data: {
quantity: newQuantity,
product_id: productId,
sponsorship_id: sponsorship_id
}
}).catch(function (jqXHR) {
// Re-throw with more details
const info = {
status: jqXHR?.status,
responseText: jqXHR?.responseText,
readyState: jqXHR?.readyState
};
throw info;
});
} catch (error) {
// Revert on error
$quantityDisplay.text(currentQuantity);
// $quantityDisplay.addClass('bg-danger').removeClass('bg-secondary');
setTimeout(() => {
// $quantityDisplay.removeClass('bg-danger').addClass('bg-secondary');
}, 1000);
} finally {
// Re-enable buttons
$button.prop('disabled', false).removeClass('o_btn_loading');
$button.siblings('button').prop('disabled', false);
}
}
});

View File

@ -6,6 +6,7 @@
<link rel="stylesheet" type="text/scss" href="/odex_takaful/static/src/scss/donation_item_views.scss"/>
<link rel="stylesheet" type="text/scss" href="/odex_takaful/static/src/scss/fix_table_overflow.scss"/>
<script type="text/javascript" src="/odex_takaful/static/src/js/product_product_views.js"/>
<script type="text/javascript" src="/odex_takaful/static/src/js/donation_catalog_button.js"/>
<script type="text/javascript" src="/odex_takaful/static/src/js/donation_catalog_controls.js"/>
<!-- <script type="text/javascript" src="/odex_takaful/static/src/js/catalog_kanban_dynamic_button.js"/>-->
</xpath>

View File

@ -3,7 +3,7 @@
<field name="name">product.template.view.kanban.odex.takaful</field>
<field name="model">product.template</field>
<field name="arch" type="xml">
<kanban edit="0" create="0" class="o_kanban_mobile o_cart_kanban">
<kanban edit="0" create="0" class="o_kanban_mobile o_cart_kanban o_cart_kanban_donation">
<field name="id" />
<field name="name" />
<field name="_quantity" />
@ -17,7 +17,8 @@
</div>
<h5 style="margin-top: 5px;text-align: center;">
<span>Amount:</span>
<field name="lst_price" widget="monetary" options="{'currency_field': 'currency_id'}" />
<field name="lst_price" widget="monetary"/>
<svg id="Layer_1" data-name="Layer 1" viewBox="0 0 1124.14 1256.39" style="height: 11px"><defs><style>.cls-1 {fill: #231f20;}</style></defs><path class="cls-1" d="M699.62,1113.02h0c-20.06,44.48-33.32,92.75-38.4,143.37l424.51-90.24c20.06-44.47,33.31-92.75,38.4-143.37l-424.51,90.24Z"/><path class="cls-1" d="M1085.73,895.8c20.06-44.47,33.32-92.75,38.4-143.37l-330.68,70.33v-135.2l292.27-62.11c20.06-44.47,33.32-92.75,38.4-143.37l-330.68,70.27V66.13c-50.67,28.45-95.67,66.32-132.25,110.99v403.35l-132.25,28.11V0c-50.67,28.44-95.67,66.32-132.25,110.99v525.69l-295.91,62.88c-20.06,44.47-33.33,92.75-38.42,143.37l334.33-71.05v170.26l-358.3,76.14c-20.06,44.47-33.32,92.75-38.4,143.37l375.04-79.7c30.53-6.35,56.77-24.4,73.83-49.24l68.78-101.97v-.02c7.14-10.55,11.3-23.27,11.3-36.97v-149.98l132.25-28.11v270.4l424.53-90.28Z"/></svg>
</h5>
<div class="oe_kanban_details p-2 d-flex">
<div class="o_kanban_record_top flex-column w-100 "
@ -56,7 +57,6 @@
<i class="fa fa-shopping-cart" />
<span>Add</span>
</button>
<div class="dc-qty-controls d-none">
<div class="input-group">
@ -71,7 +71,7 @@
<i class="fa fa-minus" />
</button>
</div>
<div class="form-control dc-qty-badge">
<div class="form-control dc-qty-badge" style="min-width: 40px;text-align: center;">
<field name="_quantity" />
</div>
<div class="input-group-prepend">
@ -114,6 +114,7 @@
</tree>
</field>
</record>
<record id="odex_takaful_product_template_form_view" model="ir.ui.view">
<field name="name">odex.takaful.product.template.form.view</field>
<field name="model">product.template</field>
@ -124,4 +125,5 @@
</field>
</field>
</record>
</odoo>