summaryrefslogtreecommitdiff
path: root/addons/sale_stock/static/src/js/qty_at_date_widget.js
blob: 384a963c3991f649632ca5c0d8acc1d7098258f4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
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;
});