diff --git a/front/core/components/chip/style.scss b/front/core/components/chip/style.scss
index 34ee947ba..c89cf2b82 100644
--- a/front/core/components/chip/style.scss
+++ b/front/core/components/chip/style.scss
@@ -21,6 +21,9 @@ vn-chip {
}
}
+ &.white {
+ background-color: $color-bg-panel;
+ }
&.colored {
background-color: $color-main;
color: $color-font-bg;
diff --git a/modules/entry/back/methods/entry/addBuy.js b/modules/entry/back/methods/entry/addBuy.js
new file mode 100644
index 000000000..015a6fc23
--- /dev/null
+++ b/modules/entry/back/methods/entry/addBuy.js
@@ -0,0 +1,136 @@
+
+const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
+
+module.exports = Self => {
+ Self.remoteMethodCtx('addBuy', {
+ description: 'Inserts or updates buy for the current entry',
+ accessType: 'WRITE',
+ accepts: [{
+ arg: 'id',
+ type: 'number',
+ required: true,
+ description: 'The entry id',
+ http: {source: 'path'}
+ },
+ {
+ arg: 'itemFk',
+ type: 'number',
+ required: true
+ },
+ {
+ arg: 'quantity',
+ type: 'number',
+ required: true
+ },
+ {
+ arg: 'packageFk',
+ type: 'string',
+ required: true
+ }],
+ returns: {
+ type: 'object',
+ root: true
+ },
+ http: {
+ path: `/:id/addBuy`,
+ verb: 'POST'
+ }
+ });
+
+ Self.addBuy = async(ctx, options) => {
+ const conn = Self.dataSource.connector;
+ let tx;
+ let myOptions = {};
+
+ if (typeof options == 'object')
+ Object.assign(myOptions, options);
+
+ if (!myOptions.transaction) {
+ tx = await Self.beginTransaction({});
+ myOptions.transaction = tx;
+ }
+
+ try {
+ const models = Self.app.models;
+
+ const newBuy = await models.Buy.create({
+ itemFk: ctx.args.itemFk,
+ entryFk: ctx.args.id,
+ packageFk: ctx.args.packageFk,
+ quantity: ctx.args.quantity
+ }, myOptions);
+
+ let filter = {
+ fields: [
+ 'id',
+ 'itemFk',
+ 'stickers',
+ 'packing',
+ 'grouping',
+ 'quantity',
+ 'packageFk',
+ 'weight',
+ 'buyingValue',
+ 'price2',
+ 'price3'
+ ],
+ include: {
+ relation: 'item',
+ scope: {
+ fields: [
+ 'id',
+ 'typeFk',
+ 'name',
+ 'size',
+ 'minPrice',
+ 'tag5',
+ 'value5',
+ 'tag6',
+ 'value6',
+ 'tag7',
+ 'value7',
+ 'tag8',
+ 'value8',
+ 'tag9',
+ 'value9',
+ 'tag10',
+ 'value10',
+ 'groupingMode'
+ ],
+ include: {
+ relation: 'itemType',
+ scope: {
+ fields: ['code', 'description']
+ }
+ }
+ }
+ }
+ };
+
+ let stmts = [];
+ let stmt;
+
+ stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.buyRecalc');
+ stmt = new ParameterizedSQL(
+ `CREATE TEMPORARY TABLE tmp.buyRecalc
+ (INDEX (id))
+ ENGINE = MEMORY
+ SELECT ? AS id`, [newBuy.id]);
+
+ stmts.push(stmt);
+ stmts.push('CALL buy_recalcPrices()');
+
+ const sql = ParameterizedSQL.join(stmts, ';');
+ await conn.executeStmt(sql, myOptions);
+
+ const buy = await models.Buy.findById(newBuy.id, filter, myOptions);
+
+ if (tx) await tx.commit();
+
+ return buy;
+ } catch (e) {
+ if (tx) await tx.rollback();
+ throw e;
+ }
+ };
+};
diff --git a/modules/entry/back/methods/entry/deleteBuys.js b/modules/entry/back/methods/entry/deleteBuys.js
new file mode 100644
index 000000000..de038d66e
--- /dev/null
+++ b/modules/entry/back/methods/entry/deleteBuys.js
@@ -0,0 +1,50 @@
+module.exports = Self => {
+ Self.remoteMethodCtx('deleteBuys', {
+ description: 'Deletes the selected buys',
+ accessType: 'WRITE',
+ accepts: [{
+ arg: 'buys',
+ type: ['object'],
+ required: true,
+ description: 'The buys to delete'
+ }],
+ returns: {
+ type: ['object'],
+ root: true
+ },
+ http: {
+ path: `/deleteBuys`,
+ verb: 'POST'
+ }
+ });
+
+ Self.deleteBuys = async(ctx, options) => {
+ const models = Self.app.models;
+ let tx;
+ let myOptions = {};
+
+ if (typeof options == 'object')
+ Object.assign(myOptions, options);
+
+ if (!myOptions.transaction) {
+ tx = await Self.beginTransaction({});
+ myOptions.transaction = tx;
+ }
+
+ try {
+ let promises = [];
+ for (let buy of ctx.args.buys) {
+ const buysToDelete = models.Buy.destroyById(buy.id, myOptions);
+ promises.push(buysToDelete);
+ }
+
+ const deleted = await Promise.all(promises);
+
+ if (tx) await tx.commit();
+ return deleted;
+ } catch (e) {
+ if (tx) await tx.rollback();
+ throw e;
+ }
+ };
+};
diff --git a/modules/entry/back/methods/entry/specs/addBuy.spec.js b/modules/entry/back/methods/entry/specs/addBuy.spec.js
new file mode 100644
index 000000000..ef1177ed5
--- /dev/null
+++ b/modules/entry/back/methods/entry/specs/addBuy.spec.js
@@ -0,0 +1,42 @@
+const app = require('vn-loopback/server/server');
+const LoopBackContext = require('loopback-context');
+
+describe('entry addBuy()', () => {
+ const activeCtx = {
+ accessToken: {userId: 18},
+ };
+
+ const ctx = {
+ req: activeCtx
+ };
+
+ const entryId = 2;
+ it('should create a new buy for the given entry', async() => {
+ spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
+ active: activeCtx
+ });
+ const itemId = 4;
+ const quantity = 10;
+
+ ctx.args = {
+ id: entryId,
+ itemFk: itemId,
+ quantity: quantity,
+ packageFk: 3
+ };
+
+ const tx = await app.models.Entry.beginTransaction({});
+ const options = {transaction: tx};
+
+ try {
+ const newBuy = await app.models.Entry.addBuy(ctx, options);
+
+ expect(newBuy.itemFk).toEqual(itemId);
+
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ throw e;
+ }
+ });
+});
diff --git a/modules/entry/back/methods/entry/specs/deleteBuys.spec.js b/modules/entry/back/methods/entry/specs/deleteBuys.spec.js
new file mode 100644
index 000000000..10466645a
--- /dev/null
+++ b/modules/entry/back/methods/entry/specs/deleteBuys.spec.js
@@ -0,0 +1,23 @@
+const app = require('vn-loopback/server/server');
+const LoopBackContext = require('loopback-context');
+
+describe('sale deleteBuys()', () => {
+ const activeCtx = {
+ accessToken: {userId: 18},
+ };
+
+ const ctx = {
+ req: activeCtx
+ };
+
+ it('should delete the buy', async() => {
+ spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
+ active: activeCtx
+ });
+ ctx.args = {buys: [{id: 1}]};
+
+ let result = await app.models.Buy.deleteBuys(ctx);
+
+ expect(result).toEqual([{count: 1}]);
+ });
+});
diff --git a/modules/entry/back/models/buy.js b/modules/entry/back/models/buy.js
index e110164e8..34f19e765 100644
--- a/modules/entry/back/models/buy.js
+++ b/modules/entry/back/models/buy.js
@@ -1,4 +1,5 @@
module.exports = Self => {
require('../methods/entry/editLatestBuys')(Self);
require('../methods/entry/latestBuysFilter')(Self);
+ require('../methods/entry/deleteBuys')(Self);
};
diff --git a/modules/entry/back/models/buy.json b/modules/entry/back/models/buy.json
index 65bf15fa4..8e36d0eef 100644
--- a/modules/entry/back/models/buy.json
+++ b/modules/entry/back/models/buy.json
@@ -57,14 +57,12 @@
"entry": {
"type": "belongsTo",
"model": "Entry",
- "foreignKey": "entryFk",
- "required": true
+ "foreignKey": "entryFk"
},
"item": {
"type": "belongsTo",
"model": "Item",
- "foreignKey": "itemFk",
- "required": true
+ "foreignKey": "itemFk"
},
"package": {
"type": "belongsTo",
diff --git a/modules/entry/back/models/entry.js b/modules/entry/back/models/entry.js
index f1a22fddd..88611963f 100644
--- a/modules/entry/back/models/entry.js
+++ b/modules/entry/back/models/entry.js
@@ -2,6 +2,7 @@ module.exports = Self => {
require('../methods/entry/filter')(Self);
require('../methods/entry/getEntry')(Self);
require('../methods/entry/getBuys')(Self);
+ require('../methods/entry/addBuy')(Self);
require('../methods/entry/importBuys')(Self);
require('../methods/entry/importBuysPreview')(Self);
};
diff --git a/modules/entry/front/buy/index/index.html b/modules/entry/front/buy/index/index.html
index 0ff11c8a6..4a1f0837d 100644
--- a/modules/entry/front/buy/index/index.html
+++ b/modules/entry/front/buy/index/index.html
@@ -1,9 +1,189 @@
+
+ |
+ Item | +Quantity | +Package | +Stickers | +Weight | +Packing | +Grouping | +Buying value | +Grouping price | +Packing price | +Import | +|
---|---|---|---|---|---|---|---|---|---|---|---|---|
+ |
+
+
+ {{::buy.item.id | zeroFill:6}}
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+ + + {{buy.quantity * buy.buyingValue | currency: 'EUR':2}} + + | +|
+ | ++ + {{::buy.item.itemType.code}} + + | ++ + {{::buy.item.size}} + + | ++ + {{::buy.item.minPrice | currency: 'EUR':2}} + + | +
+ |
+