diff options
| author | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
|---|---|---|
| committer | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
| commit | 3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch) | |
| tree | a44932296ef4a9b71d5f010906253d8c53727726 /addons/sale_stock/static | |
| parent | 0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff) | |
initial commit 2
Diffstat (limited to 'addons/sale_stock/static')
| -rw-r--r-- | addons/sale_stock/static/src/js/qty_at_date_widget.js | 141 | ||||
| -rw-r--r-- | addons/sale_stock/static/src/xml/sale_stock.xml | 74 |
2 files changed, 215 insertions, 0 deletions
diff --git a/addons/sale_stock/static/src/js/qty_at_date_widget.js b/addons/sale_stock/static/src/js/qty_at_date_widget.js new file mode 100644 index 00000000..384a963c --- /dev/null +++ b/addons/sale_stock/static/src/js/qty_at_date_widget.js @@ -0,0 +1,141 @@ +odoo.define('sale_stock.QtyAtDateWidget', function (require) { +"use strict"; + +var core = require('web.core'); +var QWeb = core.qweb; + +var Widget = require('web.Widget'); +var widget_registry = require('web.widget_registry'); +var utils = require('web.utils'); + +var _t = core._t; +var time = require('web.time'); + +var QtyAtDateWidget = Widget.extend({ + template: 'sale_stock.qtyAtDate', + events: _.extend({}, Widget.prototype.events, { + 'click .fa-area-chart': '_onClickButton', + }), + + /** + * @override + * @param {Widget|null} parent + * @param {Object} params + */ + init: function (parent, params) { + this.data = params.data; + this.fields = params.fields; + this._updateData(); + this._super(parent); + }, + + start: function () { + var self = this; + return this._super.apply(this, arguments).then(function () { + self._setPopOver(); + }); + }, + + _updateData: function() { + // add some data to simplify the template + if (this.data.scheduled_date) { + // The digit info need to get from free_qty_today in master (instead of virtual_available_at_date) + var qty_considered = this.data.state === 'sale' ? this.data.free_qty_today : this.data.virtual_available_at_date; + this.data.will_be_fulfilled = utils.round_decimals(qty_considered, this.fields.virtual_available_at_date.digits[1]) >= utils.round_decimals(this.data.qty_to_deliver, this.fields.qty_to_deliver.digits[1]); + this.data.will_be_late = this.data.forecast_expected_date && this.data.forecast_expected_date > this.data.scheduled_date; + if (['draft', 'sent'].includes(this.data.state)){ + // Moves aren't created yet, then the forecasted is only based on virtual_available of quant + this.data.forecasted_issue = !this.data.will_be_fulfilled && !this.data.is_mto; + } else { + // Moves are created, using the forecasted data of related moves + this.data.forecasted_issue = !this.data.will_be_fulfilled || this.data.will_be_late; + } + } + }, + + updateState: function (state) { + this.$el.popover('dispose'); + var candidate = state.data[this.getParent().currentRow]; + if (candidate) { + this.data = candidate.data; + this._updateData(); + this.renderElement(); + this._setPopOver(); + } + }, + /** + * Redirect to the product graph view. + * + * @private + * @param {MouseEvent} event + * @returns {Promise} action loaded + */ + async _openForecast(ev) { + ev.stopPropagation(); + // TODO: in case of kit product, the forecast view should show the kit's components (get_component) + // The forecast_report doesn't not allow for now multiple products + var action = await this._rpc({ + model: 'product.product', + method: 'action_product_forecast_report', + args: [[this.data.product_id.data.id]] + }); + action.context = { + active_model: 'product.product', + active_id: this.data.product_id.data.id, + warehouse: this.data.warehouse_id && this.data.warehouse_id.res_id + }; + return this.do_action(action); + }, + + _getContent() { + if (!this.data.scheduled_date) { + return; + } + this.data.delivery_date = this.data.scheduled_date.clone().add(this.getSession().getTZOffset(this.data.scheduled_date), 'minutes').format(time.getLangDateFormat()); + if (this.data.forecast_expected_date) { + this.data.forecast_expected_date_str = this.data.forecast_expected_date.clone().add(this.getSession().getTZOffset(this.data.forecast_expected_date), 'minutes').format(time.getLangDateFormat()); + } + const $content = $(QWeb.render('sale_stock.QtyDetailPopOver', { + data: this.data, + })); + $content.on('click', '.action_open_forecast', this._openForecast.bind(this)); + return $content; + }, + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + /** + * Set a bootstrap popover on the current QtyAtDate widget that display available + * quantity. + */ + _setPopOver() { + const $content = this._getContent(); + if (!$content) { + return; + } + const options = { + content: $content, + html: true, + placement: 'left', + title: _t('Availability'), + trigger: 'focus', + delay: {'show': 0, 'hide': 100 }, + }; + this.$el.popover(options); + }, + + //-------------------------------------------------------------------------- + // Handlers + //-------------------------------------------------------------------------- + _onClickButton: function () { + // We add the property special click on the widget link. + // This hack allows us to trigger the popover (see _setPopOver) without + // triggering the _onRowClicked that opens the order line form view. + this.$el.find('.fa-area-chart').prop('special_click', true); + }, +}); + +widget_registry.add('qty_at_date_widget', QtyAtDateWidget); + +return QtyAtDateWidget; +}); diff --git a/addons/sale_stock/static/src/xml/sale_stock.xml b/addons/sale_stock/static/src/xml/sale_stock.xml new file mode 100644 index 00000000..23b85b3b --- /dev/null +++ b/addons/sale_stock/static/src/xml/sale_stock.xml @@ -0,0 +1,74 @@ +<templates> + <div t-name="sale_stock.qtyAtDate"> + <div t-att-class="!widget.data.display_qty_widget ? 'invisible' : ''"> + <a tabindex="0" t-attf-class="fa fa-area-chart {{ widget.data.forecasted_issue ? 'text-danger' : 'text-primary' }}"/> + </div> + </div> + + <div t-name="sale_stock.QtyDetailPopOver"> + <table class="table table-borderless table-sm"> + <tbody> + <t t-if="!data.is_mto and ['draft', 'sent'].includes(data.state)"> + <tr> + <td><strong>Forecasted Stock</strong><br /><small>On <span t-esc="data.delivery_date"/></small></td> + <td><b t-esc='data.virtual_available_at_date'/> + <t t-esc='data.product_uom.data.display_name'/></td> + </tr> + <tr> + <td><strong>Available</strong><br /><small>All planned operations included</small></td> + <td><b t-esc='data.free_qty_today' t-att-class="!data.will_be_fulfilled ? 'text-danger': ''"/> + <t t-esc='data.product_uom.data.display_name'/></td> + </tr> + </t> + <t t-elif="data.is_mto and ['draft', 'sent'].includes(data.state)"> + <tr> + <td><strong>Expected Delivery</strong></td> + <td class="oe-right"><span t-esc="data.delivery_date"/></td> + </tr> + <tr> + <p>This product is replenished on demand.</p> + </tr> + </t> + <t t-elif="data.state == 'sale'"> + <tr> + <td> + <strong>Reserved</strong><br/> + </td> + <td style="min-width: 50px; text-align: right;"> + <b t-esc='data.qty_available_today'/> <t t-esc='data.product_uom.data.display_name'/> + </td> + </tr> + <tr t-if="data.qty_available_today < data.qty_to_deliver"> + <td> + <span t-if="data.will_be_fulfilled and data.forecast_expected_date_str"> + Remaining demand available at <b t-esc="data.forecast_expected_date_str" t-att-class="data.scheduled_date < data.forecast_expected_date ? 'text-danger' : ''"/> + </span> + <span t-elif="!data.will_be_fulfilled and data.forecast_expected_date_str" class="text-danger"> + No enough future availaibility + </span> + <span t-elif="!data.will_be_fulfilled" class="text-danger"> + No future availaibility + </span> + <span t-else=""> + Available in stock + </span> + </td> + </tr> + </t> + </tbody> + </table> + <button t-if="!data.is_mto" class="text-left btn btn-link action_open_forecast" + type="button"> + <i class="fa fa-fw o_button_icon fa-arrow-right"></i> + View Forecast + </button> + </div> + + <div t-name="sale_stock.DelayAlertWidget"> + <p>The delivery + <t t-foreach="late_elements" t-as="late_element"> + <a t-esc="late_element.name" href="#" t-att-element-id="late_element.id" t-att-element-model="model"/>, + </t> will be late. + </p> + </div> +</templates> |
