
Quite a bit of duplication with the chart of accounts (controls are just about identical) but not quite enough that the CoA can trivially be reused.
211 lines
8.1 KiB
JavaScript
211 lines
8.1 KiB
JavaScript
(function () {
|
|
'use strict';
|
|
|
|
var data = createAtom();
|
|
|
|
function toKey(s, postfix) {
|
|
if (postfix) {
|
|
s += ' ' + postfix;
|
|
}
|
|
return s.replace(/[^0-9a-z ]/gi, '').toLowerCase().split(/\s+/).join('-');
|
|
}
|
|
var Controls = React.createClass({
|
|
render: function () {
|
|
var state = this.props.p;
|
|
return React.DOM.div(null, operations.map(function (op) {
|
|
var label = op.get('label'), operations = op.get('operations');
|
|
return React.DOM.label(
|
|
{
|
|
key: toKey(label),
|
|
style: {display: 'block'},
|
|
className: (operations === state.get('active') ? 'highlight-op' : void 0)
|
|
},
|
|
React.DOM.input({
|
|
type: 'checkbox',
|
|
checked: state.get('operations').contains(operations),
|
|
onChange: function (e) {
|
|
if (e.target.checked) {
|
|
data.swap(function (d) {
|
|
return d.set('active', operations)
|
|
.update('operations', function (ops) {
|
|
return ops.add(operations);
|
|
});
|
|
});
|
|
} else {
|
|
data.swap(function (d) {
|
|
return d.set('active', null)
|
|
.update('operations', function (ops) {
|
|
return ops.remove(operations);
|
|
});
|
|
});
|
|
}
|
|
}
|
|
}),
|
|
' ',
|
|
label
|
|
);
|
|
}));
|
|
}
|
|
});
|
|
var UNIT_PRICE = 100;
|
|
function format_qty(val) {
|
|
if (val == null) { return ''; }
|
|
if (val < 0) { return val; }
|
|
return '+' + String(val);
|
|
}
|
|
function format_value(val) {
|
|
if (isNaN(val)) { return ''; }
|
|
if (val < 0) { return '-$' + String(Math.abs(val)); }
|
|
return '$' + String(val);
|
|
}
|
|
var Chart = React.createClass({
|
|
render: function () {
|
|
return React.DOM.div(
|
|
null,
|
|
React.DOM.table(
|
|
{className: 'table table-condensed'},
|
|
React.DOM.thead(
|
|
null,
|
|
React.DOM.tr(
|
|
null,
|
|
React.DOM.th(null, "Location"),
|
|
React.DOM.th({className: 'text-right'}, "Quantity"),
|
|
React.DOM.th({className: 'text-right'}, "Value"))
|
|
),
|
|
React.DOM.tbody(
|
|
null,
|
|
this.locations().map(function (data) {
|
|
var highlight = false;
|
|
return React.DOM.tr(
|
|
{key: toKey(data.get('label'))},
|
|
React.DOM.th(null, data.get('level') ? '\u2001' : '', data.get('label')),
|
|
React.DOM.td({ className: React.addons.classSet({
|
|
'text-right': true,
|
|
'highlight-op': highlight
|
|
})}, format_qty(data.get('qty'))),
|
|
React.DOM.td({ className: React.addons.classSet({
|
|
'text-right': true,
|
|
'highlight-op': highlight
|
|
})}, format_value(data.get('qty') * UNIT_PRICE))
|
|
);
|
|
})
|
|
)
|
|
)
|
|
);
|
|
},
|
|
locations: function () {
|
|
var data = this.props.p.get('operations');
|
|
|
|
// {location: total_qty}
|
|
var totals = data.toIndexedSeq().flatten(true).reduce(function(acc, op) {
|
|
return acc.update(op.get('location'), function (qty) {
|
|
return (qty || 0) + op.get('qty');
|
|
});
|
|
}, Immutable.Map());
|
|
|
|
return locations.valueSeq().flatMap(function (loc) {
|
|
var sub_locations = loc.get('locations').valueSeq().map(function (subloc) {
|
|
return subloc.set('level', 1).set('qty', totals.get(subloc));
|
|
});
|
|
|
|
return Immutable.Seq.of(loc.set('level', 0)).concat(sub_locations);
|
|
});
|
|
}
|
|
});
|
|
|
|
data.addWatch('chart', function (k, m, prev, next) {
|
|
React.render(
|
|
React.createElement(Controls, {p: next}),
|
|
document.getElementById('chart-of-locations-controls'));
|
|
React.render(
|
|
React.createElement(Chart, {p: next}),
|
|
document.getElementById('chart-of-locations'));
|
|
});
|
|
document.addEventListener('DOMContentLoaded', function () {
|
|
var chart = document.querySelector('.chart-of-locations');
|
|
if (!chart) { return; }
|
|
|
|
chart.setAttribute('id', 'chart-of-locations');
|
|
var controls = document.createElement('div');
|
|
controls.setAttribute('id', 'chart-of-locations-controls');
|
|
chart.parentNode.insertBefore(controls, chart);
|
|
|
|
data.reset(Immutable.Map({
|
|
active: null,
|
|
operations: Immutable.OrderedSet()
|
|
}));
|
|
});
|
|
var locations = Immutable.fromJS({
|
|
warehouse: {
|
|
label: "Warehouse",
|
|
locations: {
|
|
zone1: {label: "Zone 1"},
|
|
zone2: {label: "Zone 2"}
|
|
}
|
|
},
|
|
partners: {
|
|
label: "Partner Locations",
|
|
locations: {
|
|
customers: {label: "Customers"},
|
|
suppliers: {label: "Suppliers"}
|
|
}
|
|
},
|
|
virtual: {
|
|
label: "Virtual Locations",
|
|
locations: {
|
|
initial: {label: "Initial Inventory"},
|
|
loss: {label: "Inventory Loss"},
|
|
scrap: {label: "Scrapped"},
|
|
manufacturing: {label: "Manufacturing"}
|
|
}
|
|
}
|
|
}, function (k, v) {
|
|
return Immutable.Iterable.isIndexed(v)
|
|
? v.toList()
|
|
: v.toOrderedMap();
|
|
});
|
|
var operations = Immutable.fromJS([{
|
|
label: "Inventaire Initial",
|
|
operations: [
|
|
{location: locations.getIn(['virtual','locations','initial']), qty: -3},
|
|
{location: locations.getIn(['warehouse','locations','zone1']), qty: +3}
|
|
]
|
|
}, {
|
|
label: "Reception",
|
|
operations: [
|
|
{location: locations.getIn(['partners','locations','suppliers']), qty: -2},
|
|
{location: locations.getIn(['warehouse','locations','zone1']), qty: +2}
|
|
]
|
|
}, {
|
|
label: "Delivery",
|
|
operations: [
|
|
{location: locations.getIn(['warehouse','locations','zone1']), qty: -1},
|
|
{location: locations.getIn(['partners','locations','customers']), qty: +1}
|
|
]
|
|
}, {
|
|
label: "Return",
|
|
operations: [
|
|
{location: locations.getIn(['partners','locations','customers']), qty: -1},
|
|
{location: locations.getIn(['warehouse','locations','zone1']), qty: +1}
|
|
]
|
|
}, {
|
|
label: "1 product broken in Zone 1",
|
|
operations: [
|
|
{location: locations.getIn(['warehouse','locations','zone1']), qty: -1},
|
|
{location: locations.getIn(['virtual','locations','scrap']), qty: +1}
|
|
]
|
|
}, {
|
|
label: "Inventory check of Zone 1",
|
|
operations: [
|
|
{location: locations.getIn(['warehouse','locations','zone1']), qty: -1},
|
|
{location: locations.getIn(['virtual','locations','loss']), qty: +1}
|
|
]
|
|
}, {
|
|
label: "Move from Zone 1 to Zone 2",
|
|
operations: [
|
|
{location: locations.getIn(['warehouse','locations','zone1']), qty: -1},
|
|
{location: locations.getIn(['warehouse','locations','zone2']), qty: +1}
|
|
]
|
|
}]);
|
|
})();
|