mirror of
https://github.com/bringout/oca-ocb-sale.git
synced 2026-04-24 20:12:03 +02:00
Initial commit: Sale packages
This commit is contained in:
commit
14e3d26998
6469 changed files with 2479670 additions and 0 deletions
|
|
@ -0,0 +1,317 @@
|
|||
odoo.define('product.generate_pricelist', function (require) {
|
||||
'use strict';
|
||||
|
||||
var AbstractAction = require('web.AbstractAction');
|
||||
var core = require('web.core');
|
||||
var FieldMany2One = require('web.relational_fields').FieldMany2One;
|
||||
var StandaloneFieldManagerMixin = require('web.StandaloneFieldManagerMixin');
|
||||
var Widget = require('web.Widget');
|
||||
|
||||
var QWeb = core.qweb;
|
||||
var _t = core._t;
|
||||
|
||||
var QtyTagWidget = Widget.extend({
|
||||
template: 'product.report_pricelist_qty',
|
||||
events: {
|
||||
'click .o_remove_qty': '_onClickRemoveQty',
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
init: function (parent, defaulQuantities) {
|
||||
this._super.apply(this, arguments);
|
||||
this.quantities = defaulQuantities;
|
||||
this.MAX_QTY = 5;
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Handlers
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Add a quantity when add(+) button clicked.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_onClickAddQty: function () {
|
||||
if (this.quantities.length >= this.MAX_QTY) {
|
||||
this.displayNotification({ message: _.str.sprintf(
|
||||
_t("At most %d quantities can be displayed simultaneously. Remove a selected quantity to add others."),
|
||||
this.MAX_QTY
|
||||
) });
|
||||
return;
|
||||
}
|
||||
const qty = parseInt(this.$('.o_product_qty').val());
|
||||
if (qty && qty > 0) {
|
||||
// Check qty already exist
|
||||
if (this.quantities.indexOf(qty) === -1) {
|
||||
this.quantities.push(qty);
|
||||
this.quantities = this.quantities.sort((a, b) => a - b);
|
||||
this.trigger_up('qty_changed', {quantities: this.quantities});
|
||||
this.renderElement();
|
||||
} else {
|
||||
this.displayNotification({
|
||||
message: _.str.sprintf(_t("Quantity already present (%d)."), qty),
|
||||
type: 'info'
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this.displayNotification({ message: _t("Please enter a positive whole number") });
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Remove quantity.
|
||||
*
|
||||
* @private
|
||||
* @param {jQueryEvent} ev
|
||||
*/
|
||||
_onClickRemoveQty: function (ev) {
|
||||
const qty = parseInt($(ev.currentTarget).closest('.badge').data('qty'));
|
||||
this.quantities = this.quantities.filter(q => q !== qty);
|
||||
this.trigger_up('qty_changed', {quantities: this.quantities});
|
||||
this.renderElement();
|
||||
},
|
||||
});
|
||||
|
||||
var GeneratePriceList = AbstractAction.extend(StandaloneFieldManagerMixin, {
|
||||
hasControlPanel: true,
|
||||
events: {
|
||||
'click .o_action': '_onClickAction',
|
||||
'submit form': '_onSubmitForm',
|
||||
},
|
||||
custom_events: Object.assign({}, StandaloneFieldManagerMixin.custom_events, {
|
||||
field_changed: '_onFieldChanged',
|
||||
qty_changed: '_onQtyChanged',
|
||||
}),
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
init: function (parent, params) {
|
||||
this._super.apply(this, arguments);
|
||||
StandaloneFieldManagerMixin.init.call(this);
|
||||
this.context = params.context;
|
||||
// in case the window got refreshed
|
||||
if (params.params && params.params.active_ids && typeof(params.params.active_ids === 'string')) {
|
||||
try {
|
||||
this.context.active_ids = params.params.active_ids.split(',').map(id => parseInt(id));
|
||||
this.context.active_model = params.params.active_model;
|
||||
} catch(_e) {
|
||||
console.log('unable to load ids from the url fragment 🙁');
|
||||
}
|
||||
}
|
||||
if (!this.context.active_model) {
|
||||
// started without an active module, assume product templates
|
||||
this.context.active_model = 'product.template';
|
||||
}
|
||||
this.context.quantities = [1, 5, 10];
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
willStart: function () {
|
||||
let getPricelist;
|
||||
// started without a selected pricelist in context? just get the first one
|
||||
if (this.context.default_pricelist) {
|
||||
getPricelist = Promise.resolve([this.context.default_pricelist]);
|
||||
} else {
|
||||
getPricelist = this._rpc({
|
||||
model: 'product.pricelist',
|
||||
method: 'search',
|
||||
args: [[]],
|
||||
kwargs: {limit: 1}
|
||||
});
|
||||
}
|
||||
const fieldSetup = getPricelist.then(pricelistIds => {
|
||||
return this.model.makeRecord('report.product.report_pricelist', [{
|
||||
name: 'pricelist_id',
|
||||
type: 'many2one',
|
||||
relation: 'product.pricelist',
|
||||
value: pricelistIds[0],
|
||||
}]);
|
||||
}).then(recordID => {
|
||||
const record = this.model.get(recordID);
|
||||
this.many2one = new FieldMany2One(this, 'pricelist_id', record, {
|
||||
mode: 'edit',
|
||||
attrs: {
|
||||
can_create: false,
|
||||
can_write: false,
|
||||
options: {no_open: true},
|
||||
},
|
||||
});
|
||||
this._registerWidget(recordID, 'pricelist_id', this.many2one);
|
||||
});
|
||||
return Promise.all([fieldSetup, this._getHtml(), this._super()]);
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
start: function () {
|
||||
this.controlPanelProps.cp_content = this._renderComponent();
|
||||
const $content = this.controlPanelProps.cp_content;
|
||||
$content["$searchview"][0].querySelector('.o_is_visible_title').addEventListener('click', this._onClickVisibleTitle.bind(this));
|
||||
return this._super.apply(this, arguments).then(() => {
|
||||
this.$('.o_content').html(this.reportHtml);
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Include the current model (template/variant) in the state to allow refreshing without losing
|
||||
* the proper context.
|
||||
* @override
|
||||
*/
|
||||
getState: function () {
|
||||
return {
|
||||
active_model: this.context.active_model,
|
||||
};
|
||||
},
|
||||
getTitle: function () {
|
||||
return _t('Pricelist Report');
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Private
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the expected data for the report rendering call (html or pdf)
|
||||
*
|
||||
* @private
|
||||
* @returns {Object}
|
||||
*/
|
||||
_prepareActionReportParams: function () {
|
||||
return {
|
||||
active_model: this.context.active_model,
|
||||
active_ids: this.context.active_ids || '',
|
||||
is_visible_title: this.context.is_visible_title || '',
|
||||
pricelist_id: this.context.pricelist_id || '',
|
||||
quantities: this.context.quantities || [1],
|
||||
};
|
||||
},
|
||||
/**
|
||||
* Get template to display report.
|
||||
*
|
||||
* @private
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_getHtml: function () {
|
||||
return this._rpc({
|
||||
model: 'report.product.report_pricelist',
|
||||
method: 'get_html',
|
||||
kwargs: {
|
||||
data: this._prepareActionReportParams(),
|
||||
context: this.context,
|
||||
},
|
||||
}).then(result => {
|
||||
this.reportHtml = result;
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Reload report.
|
||||
*
|
||||
* @private
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_reload: function () {
|
||||
return this._getHtml().then(() => {
|
||||
this.$('.o_content').html(this.reportHtml);
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Render search view and print button.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_renderComponent: function () {
|
||||
const $buttons = $('<button>', {
|
||||
class: 'btn btn-primary',
|
||||
text: _t("Print"),
|
||||
}).on('click', this._onClickPrint.bind(this));
|
||||
|
||||
const $searchview = $(QWeb.render('product.report_pricelist_search'));
|
||||
this.many2one.appendTo($searchview.find('.o_pricelist'));
|
||||
|
||||
this.qtyTagWidget = new QtyTagWidget(this, this.context.quantities);
|
||||
this.qtyTagWidget.replace($searchview.find('.o_product_qty'));
|
||||
return { $buttons, $searchview };
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Handlers
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Checkbox is checked, the report title will show.
|
||||
*
|
||||
* @private
|
||||
* @param {Event} ev
|
||||
*/
|
||||
_onClickVisibleTitle(ev) {
|
||||
this.context.is_visible_title = ev.currentTarget.checked;
|
||||
this._reload();
|
||||
},
|
||||
|
||||
/**
|
||||
* Open form view of particular record when link clicked.
|
||||
*
|
||||
* @private
|
||||
* @param {jQueryEvent} ev
|
||||
*/
|
||||
_onClickAction: function (ev) {
|
||||
ev.preventDefault();
|
||||
this.do_action({
|
||||
type: 'ir.actions.act_window',
|
||||
res_model: $(ev.currentTarget).data('model'),
|
||||
res_id: $(ev.currentTarget).data('res-id'),
|
||||
views: [[false, 'form']],
|
||||
target: 'self',
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Print report in PDF when button clicked.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_onClickPrint: function () {
|
||||
return this.do_action({
|
||||
type: 'ir.actions.report',
|
||||
report_type: 'qweb-pdf',
|
||||
report_name: 'product.report_pricelist',
|
||||
report_file: 'product.report_pricelist',
|
||||
data: this._prepareActionReportParams(),
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Reload report when pricelist changed.
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
_onFieldChanged: function (event) {
|
||||
this.context.pricelist_id = event.data.changes.pricelist_id.id;
|
||||
StandaloneFieldManagerMixin._onFieldChanged.apply(this, arguments);
|
||||
this._reload();
|
||||
},
|
||||
/**
|
||||
* Reload report when quantities changed.
|
||||
*
|
||||
* @private
|
||||
* @param {OdooEvent} ev
|
||||
* @param {integer[]} event.data.quantities
|
||||
*/
|
||||
_onQtyChanged: function (ev) {
|
||||
this.context.quantities = ev.data.quantities;
|
||||
this._reload();
|
||||
},
|
||||
_onSubmitForm: function (ev) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
this.qtyTagWidget._onClickAddQty();
|
||||
},
|
||||
});
|
||||
|
||||
core.action_registry.add('generate_pricelist', GeneratePriceList);
|
||||
|
||||
return {
|
||||
GeneratePriceList,
|
||||
QtyTagWidget
|
||||
};
|
||||
|
||||
});
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
.o_label_sheet {
|
||||
margin-left: -4mm;
|
||||
margin-right: -4mm;
|
||||
overflow: hidden;
|
||||
width: 210mm;
|
||||
height: 297mm;
|
||||
page-break-before: always;
|
||||
&.o_label_dymo {
|
||||
font-size:90%;
|
||||
width: 57mm;
|
||||
height: 32mm;
|
||||
}
|
||||
div {
|
||||
padding: 2px 4px;
|
||||
}
|
||||
div.o_label_small_text {
|
||||
font-size: 60%;
|
||||
line-height: 130%;
|
||||
}
|
||||
div.o_label_name {
|
||||
background-color: ghostwhite;
|
||||
height: 3em;
|
||||
overflow: hidden;
|
||||
}
|
||||
div.o_label_full {
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
margin: auto;
|
||||
}
|
||||
div.o_label_left_column {
|
||||
float: left;
|
||||
font-size: .6em;
|
||||
overflow:hidden;
|
||||
width: 40%;
|
||||
&.o_label_full_with {
|
||||
width: 100%
|
||||
}
|
||||
}
|
||||
div.o_label_right_column {
|
||||
float: right;
|
||||
}
|
||||
div.o_label_small_barcode {
|
||||
font-size: .6em;
|
||||
padding: 0 4px;
|
||||
line-height: normal;
|
||||
}
|
||||
strong.o_label_price {
|
||||
font-size: 2em;
|
||||
}
|
||||
strong.o_label_price_medium {
|
||||
font-size: 1.3em;
|
||||
line-height: normal;
|
||||
padding: 0;
|
||||
padding-right: 2mm;
|
||||
}
|
||||
strong.o_label_price_small {
|
||||
font-size: 0.9em;
|
||||
padding: 0 4px;
|
||||
padding-right: 2mm;
|
||||
}
|
||||
div.o_label_extra_data {
|
||||
overflow: hidden;
|
||||
height: 2.5em;
|
||||
padding: 0;
|
||||
.img {
|
||||
max-height: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
div.o_label_clear {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
// generic 4x12 label w/ all same size font
|
||||
div.o_label_4x12 {
|
||||
padding:0;
|
||||
line-height:1;
|
||||
font-size:55%;
|
||||
overflow:hidden;
|
||||
white-space:nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates>
|
||||
|
||||
<t t-name="product.report_pricelist_qty">
|
||||
<span>
|
||||
<div class="input-group flex-nowrap w-75">
|
||||
<input type="number" name="qty_to_add" class="o_input o_product_qty form-control text-end w-auto" value="1" min="1"/>
|
||||
<button class="btn btn-secondary o_add_qty text-end form-control" type="submit" title="Add a quantity">
|
||||
<i class="fa fa-plus"/>
|
||||
</button>
|
||||
</div>
|
||||
<span class="o_badges">
|
||||
<t t-set="quantities" t-value="widget.quantities"/>
|
||||
<t t-call="product.report_pricelist_qty_badges"/>
|
||||
</span>
|
||||
</span>
|
||||
</t>
|
||||
|
||||
<t t-name="product.report_pricelist_search">
|
||||
<form class="d-flex justify-content-around align-items-center o_pricelist_report_form">
|
||||
<div>
|
||||
<label class="fw-bold">Pricelist:</label>
|
||||
<span class="o_pricelist"/>
|
||||
</div>
|
||||
<div class="d-flex align-items-center">
|
||||
<label class="fw-bold mb-4" for="qty_to_add">Quantities:</label>
|
||||
<div class="o_product_qty"/>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input o_is_visible_title ms-2" type="checkbox"/>
|
||||
<label class="form-check-label">Display Pricelist</label>
|
||||
</div>
|
||||
</form>
|
||||
</t>
|
||||
|
||||
<t t-name="product.report_pricelist_qty_badges">
|
||||
<t t-foreach="quantities" t-as="qty">
|
||||
<span class="badge rounded-pill border" t-att-data-qty="qty">
|
||||
<t t-esc="qty"/>
|
||||
<i class="fa fa-close o_remove_qty" title="Remove quantity"/>
|
||||
</span>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
Loading…
Add table
Add a link
Reference in a new issue