Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix into 3595-item-waste-folding
gitea/salix/pipeline/head This commit looks good
Details
gitea/salix/pipeline/head This commit looks good
Details
This commit is contained in:
commit
71875070e8
|
@ -0,0 +1,34 @@
|
|||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('setSaleQuantity', {
|
||||
description: 'Update sale quantity',
|
||||
accessType: 'WRITE',
|
||||
accepts: [{
|
||||
arg: 'saleId',
|
||||
type: 'number',
|
||||
required: true,
|
||||
description: 'The sale id'
|
||||
},
|
||||
{
|
||||
arg: 'quantity',
|
||||
type: 'number',
|
||||
required: true,
|
||||
description: 'The quantity to picked'
|
||||
}],
|
||||
returns: {
|
||||
type: 'object',
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
path: `/setSaleQuantity`,
|
||||
verb: 'POST'
|
||||
}
|
||||
});
|
||||
|
||||
Self.setSaleQuantity = async ctx => {
|
||||
const args = ctx.args;
|
||||
const models = Self.app.models;
|
||||
|
||||
const sale = await models.Sale.findById(args.saleId,);
|
||||
return await sale.updateAttribute('quantity', args.quantity);
|
||||
};
|
||||
};
|
|
@ -0,0 +1,23 @@
|
|||
const models = require('vn-loopback/server/server').models;
|
||||
|
||||
describe('setSaleQuantity()', () => {
|
||||
it('should change quantity sale', async() => {
|
||||
const saleId = 30;
|
||||
const newQuantity = 10;
|
||||
|
||||
const ctx = {
|
||||
args: {
|
||||
saleId: saleId,
|
||||
quantity: newQuantity
|
||||
}
|
||||
};
|
||||
|
||||
const originalSale = await models.Sale.findById(saleId);
|
||||
|
||||
await models.Collection.setSaleQuantity(ctx);
|
||||
const updateSale = await models.Sale.findById(saleId);
|
||||
|
||||
expect(updateSale.quantity).toBeLessThan(originalSale.quantity);
|
||||
expect(updateSale.quantity).toEqual(newQuantity);
|
||||
});
|
||||
});
|
|
@ -1,62 +0,0 @@
|
|||
const app = require('vn-loopback/server/server');
|
||||
|
||||
describe('updateCollectionSale()', () => {
|
||||
it('should return a new collection', async() => {
|
||||
const sectorOneWarehouseID = 1;
|
||||
let ctx = {req: {accessToken: {userId: 1106}}};
|
||||
ctx.args = {
|
||||
sale: 1,
|
||||
originalQuantity: 5,
|
||||
quantity: 5,
|
||||
quantityPicked: 5,
|
||||
ticketFk: 1,
|
||||
stateFk: 4,
|
||||
isNicho: false,
|
||||
shelvingFk: 'UXN',
|
||||
itemFk: 1,
|
||||
sectorFk: 1
|
||||
};
|
||||
let originalSaleTracking = await app.models.SaleTracking.findOne({
|
||||
where: {
|
||||
saleFk: ctx.args.sale,
|
||||
stateFk: ctx.args.stateFk
|
||||
}
|
||||
});
|
||||
let itemPlacement = await app.models.ItemPlacement.findOne({
|
||||
where: {
|
||||
itemFk: ctx.args.itemFk,
|
||||
warehouseFk: sectorOneWarehouseID
|
||||
}
|
||||
});
|
||||
const originalSale = await app.models.Sale.findById(ctx.args.sale);
|
||||
const originalItemShelving = await app.models.ItemShelving.findOne({where: {shelvingFk: ctx.args.shelvingFk, itemFk: ctx.args.itemFk}});
|
||||
const originalTicketLastState = await app.models.TicketLastState.findById(ctx.args.ticketFk);
|
||||
|
||||
let response = await app.models.Collection.updateCollectionSale(ctx);
|
||||
|
||||
expect(response.length).toBeGreaterThan(0);
|
||||
expect(response[0][0].id).toEqual(1);
|
||||
expect(response[0][0].quantity).toEqual(5);
|
||||
|
||||
// restores
|
||||
if (originalSaleTracking)
|
||||
await originalSaleTracking.save();
|
||||
else {
|
||||
originalSaleTracking = await app.models.SaleTracking.findOne({
|
||||
where: {
|
||||
saleFk: ctx.args.sale,
|
||||
stateFk: ctx.args.stateFk
|
||||
}
|
||||
});
|
||||
await originalSaleTracking.destroy();
|
||||
}
|
||||
await originalSale.save();
|
||||
const itemShelvingsToDestroy = await app.models.ItemShelving.find({where: {shelvingFk: ctx.args.shelvingFk, itemFk: ctx.args.itemFk}});
|
||||
for (itemShelving of itemShelvingsToDestroy)
|
||||
await itemShelving.destroy();
|
||||
|
||||
await originalItemShelving.save();
|
||||
await originalTicketLastState.save();
|
||||
await itemPlacement.save();
|
||||
});
|
||||
});
|
|
@ -1,90 +0,0 @@
|
|||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('updateCollectionSale', {
|
||||
description: 'Update sale of a collection',
|
||||
accessType: 'WRITE',
|
||||
accepts: [{
|
||||
arg: 'sale',
|
||||
type: 'Number',
|
||||
required: true,
|
||||
description: 'The sale id'
|
||||
}, {
|
||||
arg: 'originalQuantity',
|
||||
type: 'Number',
|
||||
required: true,
|
||||
description: 'The quantity to sale'
|
||||
},
|
||||
{
|
||||
arg: 'quantity',
|
||||
type: 'Number',
|
||||
required: true,
|
||||
description: 'The quantity to picked'
|
||||
},
|
||||
{
|
||||
arg: 'quantityPicked',
|
||||
type: 'Number',
|
||||
required: true,
|
||||
description: 'The quantity to picked'
|
||||
}, {
|
||||
arg: 'ticketFk',
|
||||
type: 'Number',
|
||||
required: true,
|
||||
description: 'The ticket id'
|
||||
}, {
|
||||
arg: 'stateFk',
|
||||
type: 'Number',
|
||||
required: true,
|
||||
description: 'The state id'
|
||||
}, {
|
||||
arg: 'isNicho',
|
||||
type: 'Boolean',
|
||||
required: true,
|
||||
description: 'Determine if sale is picked from nicho or not'
|
||||
}, {
|
||||
arg: 'shelvingFk',
|
||||
type: 'String',
|
||||
required: false,
|
||||
description: 'The shelving id'
|
||||
}, {
|
||||
arg: 'itemFk',
|
||||
type: 'Number',
|
||||
required: true,
|
||||
description: 'The item id'
|
||||
}, {
|
||||
arg: 'sectorFk',
|
||||
type: 'Number',
|
||||
required: true,
|
||||
description: 'The sector id'
|
||||
}],
|
||||
returns: {
|
||||
type: 'Object',
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
path: `/updateCollectionSale`,
|
||||
verb: 'POST'
|
||||
}
|
||||
});
|
||||
|
||||
Self.updateCollectionSale = async ctx => {
|
||||
const userId = ctx.req.accessToken.userId;
|
||||
const args = ctx.args;
|
||||
|
||||
if (args.originalQuantity == args.quantity) {
|
||||
query = `CALL vn.collection_updateSale(?,?,?,?,?)`;
|
||||
await Self.rawSql(query, [args.sale, args.originalQuantity, userId, args.stateFk, args.ticketFk]);
|
||||
}
|
||||
|
||||
if (!args.isNicho) {
|
||||
query = `CALL vn.collection_faults(?,?,?)`;
|
||||
await Self.rawSql(query, [args.shelvingFk, args.quantityPicked, args.itemFk]);
|
||||
} else {
|
||||
query = `CALL vn.sector_getWarehouse(?)`;
|
||||
const [result] = await Self.rawSql(query, [args.sectorFk]);
|
||||
|
||||
query = `CALL vn.itemPlacementSave(?,?,?)`;
|
||||
await Self.rawSql(query, [args.shelvingFk, args.quantityPicked, result[0]['warehouseFk']]);
|
||||
}
|
||||
query = `CALL vn.sale_updateOriginalQuantity(?,?)`;
|
||||
return await Self.rawSql(query, [args.sale, args.quantity]);
|
||||
};
|
||||
};
|
|
@ -2,6 +2,6 @@ module.exports = Self => {
|
|||
require('../methods/collection/getCollection')(Self);
|
||||
require('../methods/collection/newCollection')(Self);
|
||||
require('../methods/collection/getSectors')(Self);
|
||||
require('../methods/collection/updateCollectionSale')(Self);
|
||||
require('../methods/collection/setSaleQuantity')(Self);
|
||||
require('../methods/collection/collectionFaults')(Self);
|
||||
};
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
INSERT INTO salix.ACL
|
||||
(model, property, accessType, permission, principalType, principalId)
|
||||
VALUES('Collection', 'setSaleQuantity', '*', 'ALLOW', 'ROLE', 'employee');
|
|
@ -120,5 +120,6 @@
|
|||
"This item is not available": "This item is not available",
|
||||
"Deny buy request": "Purchase request for ticket id [{{ticketId}}]({{{url}}}) has been rejected. Reason: {{observation}}",
|
||||
"The type of business must be filled in basic data": "The type of business must be filled in basic data",
|
||||
"The worker has hours recorded that day": "The worker has hours recorded that day"
|
||||
"The worker has hours recorded that day": "The worker has hours recorded that day",
|
||||
"isWithoutNegatives": "isWithoutNegatives"
|
||||
}
|
|
@ -45,3 +45,4 @@ import './dms/edit';
|
|||
import './consumption';
|
||||
import './consumption-search-panel';
|
||||
import './defaulter';
|
||||
import './notification';
|
||||
|
|
|
@ -0,0 +1,155 @@
|
|||
<vn-crud-model
|
||||
vn-id="model"
|
||||
url="Clients"
|
||||
limit="20">
|
||||
</vn-crud-model>
|
||||
<vn-portal slot="topbar">
|
||||
<vn-searchbar
|
||||
vn-focus
|
||||
panel="vn-client-search-panel"
|
||||
placeholder="Search client"
|
||||
info="Search client by id or name"
|
||||
auto-state="false"
|
||||
model="model"
|
||||
expr-builder="$ctrl.exprBuilder(param, value)">
|
||||
</vn-searchbar>
|
||||
</vn-portal>
|
||||
<vn-card>
|
||||
<smart-table
|
||||
model="model"
|
||||
options="$ctrl.smartTableOptions"
|
||||
expr-builder="$ctrl.exprBuilder(param, value)">
|
||||
<slot-actions>
|
||||
<vn-button disabled="$ctrl.checked.length === 0"
|
||||
icon="show_chart"
|
||||
ng-click="filters.show($event)"
|
||||
vn-tooltip="Campaign consumption">
|
||||
</vn-button>
|
||||
</slot-actions>
|
||||
<slot-table>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th shrink>
|
||||
<vn-multi-check
|
||||
model="model"
|
||||
check-field="$checked">
|
||||
</vn-multi-check>
|
||||
</th>
|
||||
<th field="id">
|
||||
<span translate>Identifier</span>
|
||||
</th>
|
||||
<th field="socialName">
|
||||
<span translate>Social name</span>
|
||||
</th>
|
||||
<th field="city">
|
||||
<span translate>City</span>
|
||||
</th>
|
||||
<th field="phone">
|
||||
<span translate>Phone</span>
|
||||
</th>
|
||||
<th field="email">
|
||||
<span translate>Email</span>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="client in model.data"
|
||||
vn-anchor="::{
|
||||
state: 'item.card.summary',
|
||||
params: {id: item.id}
|
||||
}">
|
||||
<td>
|
||||
<vn-check
|
||||
ng-model="client.$checked"
|
||||
vn-click-stop>
|
||||
</vn-check>
|
||||
</td>
|
||||
<td>
|
||||
<span
|
||||
vn-click-stop="itemDescriptor.show($event, item.id)"
|
||||
class="link">
|
||||
{{::client.id}}
|
||||
</span>
|
||||
</td>
|
||||
<td>{{::client.socialName}}</td>
|
||||
<td>{{::client.city}}</td>
|
||||
<td>{{::client.phone}}</td>
|
||||
<td>{{::client.email}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</slot-table>
|
||||
</smart-table>
|
||||
</vn-card>
|
||||
|
||||
|
||||
<vn-popover vn-id="filters">
|
||||
<div class="vn-pa-lg">
|
||||
<form ng-submit="$ctrl.onSendClientConsumption()">
|
||||
<vn-horizontal><h4 translate>Campaign consumption</h4></vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-autocomplete vn-one
|
||||
url="Campaigns/latest"
|
||||
label="Campaign"
|
||||
translate-fields="['code']"
|
||||
show-field="code"
|
||||
value-field="id"
|
||||
ng-model="$ctrl.campaign.id"
|
||||
order="dated DESC"
|
||||
selection="$ctrl.campaignSelection"
|
||||
search-function="{dated: {like: '%'+ $search +'%'}}">
|
||||
<tpl-item>
|
||||
{{code}} {{dated | date: 'yyyy'}}
|
||||
</tpl-item>
|
||||
</vn-autocomplete>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-date-picker
|
||||
vn-one
|
||||
label="From"
|
||||
ng-model="$ctrl.campaign.from"
|
||||
on-change="$ctrl.onChangeDate(value)">
|
||||
</vn-date-picker>
|
||||
<vn-date-picker
|
||||
vn-one
|
||||
label="To"
|
||||
ng-model="$ctrl.campaign.to"
|
||||
on-change="$ctrl.onChangeDate(value)">
|
||||
</vn-date-picker>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal class="vn-mt-lg">
|
||||
<vn-submit label="Send"></vn-submit>
|
||||
</vn-horizontal>
|
||||
</form>
|
||||
</div>
|
||||
</vn-popover>
|
||||
<vn-contextmenu vn-id="contextmenu" targets="['smart-table']" model="model"
|
||||
expr-builder="$ctrl.exprBuilder(param, value)">
|
||||
<slot-menu>
|
||||
<vn-item translate
|
||||
ng-if="contextmenu.isFilterAllowed()"
|
||||
ng-click="contextmenu.filterBySelection()">
|
||||
Filter by selection
|
||||
</vn-item>
|
||||
<vn-item translate
|
||||
ng-if="contextmenu.isFilterAllowed()"
|
||||
ng-click="contextmenu.excludeSelection()">
|
||||
Exclude selection
|
||||
</vn-item>
|
||||
<vn-item translate
|
||||
ng-if="contextmenu.isFilterAllowed()"
|
||||
ng-click="contextmenu.removeFilter()">
|
||||
Remove filter
|
||||
</vn-item>
|
||||
<vn-item translate
|
||||
ng-click="contextmenu.removeAllFilters()">
|
||||
Remove all filters
|
||||
</vn-item>
|
||||
<vn-item translate
|
||||
ng-if="contextmenu.isActionAllowed()"
|
||||
ng-click="contextmenu.copyValue()">
|
||||
Copy value
|
||||
</vn-item>
|
||||
</slot-menu>
|
||||
</vn-contextmenu>
|
|
@ -0,0 +1,119 @@
|
|||
import ngModule from '../module';
|
||||
import Section from 'salix/components/section';
|
||||
|
||||
export default class Controller extends Section {
|
||||
constructor($element, $) {
|
||||
super($element, $);
|
||||
this.$checkAll = false;
|
||||
this.smartTableOptions = {
|
||||
activeButtons: {
|
||||
search: true
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
field: 'socialName',
|
||||
autocomplete: {
|
||||
url: 'Clients',
|
||||
showField: 'socialName',
|
||||
valueField: 'socialName'
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'city',
|
||||
autocomplete: {
|
||||
url: 'Towns',
|
||||
valueField: 'name',
|
||||
showField: 'name'
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
this.campaign = {
|
||||
id: null,
|
||||
from: null,
|
||||
to: null
|
||||
};
|
||||
|
||||
this.getUpcomingCampaing();
|
||||
}
|
||||
|
||||
get checked() {
|
||||
const clients = this.$.model.data || [];
|
||||
const checkedClients = [];
|
||||
for (const buy of clients) {
|
||||
if (buy.$checked)
|
||||
checkedClients.push(buy);
|
||||
}
|
||||
|
||||
return checkedClients;
|
||||
}
|
||||
|
||||
onChangeDate(value) {
|
||||
if (value)
|
||||
this.campaign.id = null;
|
||||
}
|
||||
|
||||
getUpcomingCampaing() {
|
||||
this.$http.get('Campaigns/upcoming')
|
||||
.then(res => this.campaign.id = res.data.id);
|
||||
}
|
||||
|
||||
get campaignSelection() {
|
||||
return this._campaignSelection;
|
||||
}
|
||||
|
||||
set campaignSelection(value) {
|
||||
this._campaignSelection = value;
|
||||
|
||||
if (value) {
|
||||
const from = new Date(value.dated);
|
||||
from.setDate(from.getDate() - value.scopeDays);
|
||||
|
||||
this.campaign.to = value.dated;
|
||||
this.campaign.from = from;
|
||||
}
|
||||
}
|
||||
|
||||
onSendClientConsumption() {
|
||||
const clientIds = this.checked.map(client => client.id);
|
||||
const params = Object.assign({
|
||||
clientIds: clientIds
|
||||
}, this.campaign);
|
||||
|
||||
this.$http.post('notify/consumption', params)
|
||||
.then(() => this.$.filters.hide())
|
||||
.then(() => this.vnApp.showSuccess(this.$t('Notifications sent!')));
|
||||
}
|
||||
|
||||
exprBuilder(param, value) {
|
||||
switch (param) {
|
||||
case 'search':
|
||||
return /^\d+$/.test(value)
|
||||
? {id: value}
|
||||
: {or: [{name: {like: `%${value}%`}}, {socialName: {like: `%${value}%`}}]};
|
||||
case 'phone':
|
||||
return {
|
||||
or: [
|
||||
{phone: value},
|
||||
{mobile: value}
|
||||
]
|
||||
};
|
||||
case 'name':
|
||||
case 'socialName':
|
||||
case 'city':
|
||||
case 'email':
|
||||
return {[param]: {like: `%${value}%`}};
|
||||
case 'id':
|
||||
case 'fi':
|
||||
case 'postcode':
|
||||
case 'salesPersonFk':
|
||||
return {[param]: value};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ngModule.vnComponent('vnClientNotification', {
|
||||
template: require('./index.html'),
|
||||
controller: Controller
|
||||
});
|
|
@ -0,0 +1,99 @@
|
|||
import './index';
|
||||
import crudModel from 'core/mocks/crud-model';
|
||||
|
||||
describe('Client notification', () => {
|
||||
describe('Component vnClientNotification', () => {
|
||||
let controller;
|
||||
let $httpBackend;
|
||||
|
||||
beforeEach(ngModule('client'));
|
||||
|
||||
beforeEach(inject(($componentController, _$httpBackend_) => {
|
||||
$httpBackend = _$httpBackend_;
|
||||
const $element = angular.element('<vn-client-notification></vn-client-notification>');
|
||||
controller = $componentController('vnClientNotification', {$element});
|
||||
controller.$.model = crudModel;
|
||||
controller.$.model.data = [
|
||||
{id: 1101},
|
||||
{id: 1102},
|
||||
{id: 1103}
|
||||
];
|
||||
$httpBackend.expect('GET', `Campaigns/upcoming`).respond(200, {id: 1});
|
||||
}));
|
||||
|
||||
describe('checked() getter', () => {
|
||||
it('should return the checked lines', () => {
|
||||
const data = controller.$.model.data;
|
||||
data[1].$checked = true;
|
||||
data[2].$checked = true;
|
||||
|
||||
const checkedRows = controller.checked;
|
||||
|
||||
const firstCheckedRow = checkedRows[0];
|
||||
const secondCheckedRow = checkedRows[1];
|
||||
|
||||
expect(firstCheckedRow.id).toEqual(1102);
|
||||
expect(secondCheckedRow.id).toEqual(1103);
|
||||
});
|
||||
});
|
||||
|
||||
describe('campaignSelection() setter', () => {
|
||||
it('should set the campaign from and to properties', () => {
|
||||
const dated = new Date();
|
||||
controller.campaignSelection = {
|
||||
dated: dated,
|
||||
scopeDays: 14
|
||||
};
|
||||
|
||||
const expectedDateTo = new Date(dated);
|
||||
expectedDateTo.setDate(expectedDateTo.getDate() - 14);
|
||||
|
||||
const campaign = controller.campaign;
|
||||
|
||||
expect(campaign.from).toEqual(expectedDateTo);
|
||||
expect(campaign.to).toEqual(dated);
|
||||
});
|
||||
});
|
||||
|
||||
describe('onSendClientConsumption()', () => {
|
||||
it('should return saved message', () => {
|
||||
jest.spyOn(controller.vnApp, 'showSuccess');
|
||||
|
||||
controller.$.filters = {hide: () => {}};
|
||||
controller.campaign = {
|
||||
id: 1,
|
||||
from: new Date(),
|
||||
to: new Date()
|
||||
};
|
||||
|
||||
const data = controller.$.model.data;
|
||||
data[0].$checked = true;
|
||||
data[1].$checked = true;
|
||||
|
||||
const params = Object.assign({
|
||||
clientIds: [1101, 1102]
|
||||
}, controller.campaign);
|
||||
|
||||
$httpBackend.expect('POST', `notify/consumption`, params).respond(200, params);
|
||||
controller.onSendClientConsumption();
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(controller.vnApp.showSuccess).toHaveBeenCalledWith('Notifications sent!');
|
||||
});
|
||||
});
|
||||
|
||||
describe('exprBuilder()', () => {
|
||||
it('should search by sales person', () => {
|
||||
let expr = controller.exprBuilder('salesPersonFk', '5');
|
||||
|
||||
expect(expr).toEqual({'salesPersonFk': '5'});
|
||||
});
|
||||
|
||||
it('should search by client social name', () => {
|
||||
let expr = controller.exprBuilder('socialName', '1foo');
|
||||
|
||||
expect(expr).toEqual({'socialName': {like: '%1foo%'}});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,2 @@
|
|||
Campaign consumption: Consumo campaña
|
||||
Send: Enviar
|
|
@ -7,6 +7,7 @@
|
|||
"menus": {
|
||||
"main": [
|
||||
{"state": "client.index", "icon": "person"},
|
||||
{"state": "client.notification", "icon": "campaign"},
|
||||
{"state": "client.defaulter.index", "icon": "icon-defaulter"}
|
||||
],
|
||||
"card": [
|
||||
|
@ -373,6 +374,12 @@
|
|||
"state": "client.defaulter.index",
|
||||
"component": "vn-client-defaulter-index",
|
||||
"description": "Defaulter"
|
||||
},
|
||||
{
|
||||
"url" : "/notification",
|
||||
"state": "client.notification",
|
||||
"component": "vn-client-notification",
|
||||
"description": "Notifications"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
</a>
|
||||
<span>
|
||||
<span translate>Basket</span> #{{$ctrl.summary.id}} - {{$ctrl.summary.client.name}}
|
||||
({{$ctrl.summary.client.salesPersonFk}})
|
||||
({{$ctrl.summary.client.id}})
|
||||
</span>
|
||||
<vn-button
|
||||
disabled="$ctrl.order.isConfirmed"
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
const db = require('vn-print/core/database');
|
||||
const Email = require('vn-print/core/email');
|
||||
|
||||
module.exports = async function(request, response, next) {
|
||||
try {
|
||||
const reqArgs = request.body;
|
||||
|
||||
if (!reqArgs.clientIds)
|
||||
throw new Error('The argument clientIds is required');
|
||||
if (!reqArgs.from)
|
||||
throw new Error('The argument from is required');
|
||||
if (!reqArgs.to)
|
||||
throw new Error('The argument to is required');
|
||||
|
||||
response.status(200).json({
|
||||
message: 'Success'
|
||||
});
|
||||
|
||||
const clientIds = reqArgs.clientIds;
|
||||
|
||||
const clients = await db.rawSql(`
|
||||
SELECT
|
||||
c.id,
|
||||
c.email,
|
||||
eu.email salesPersonEmail
|
||||
FROM client c
|
||||
JOIN account.emailUser eu ON eu.userFk = c.salesPersonFk
|
||||
JOIN ticket t ON t.clientFk = c.id
|
||||
JOIN sale s ON s.ticketFk = t.id
|
||||
JOIN item i ON i.id = s.itemFk
|
||||
JOIN itemType it ON it.id = i.typeFk
|
||||
WHERE c.id IN(?)
|
||||
AND it.isPackaging = FALSE
|
||||
AND DATE(t.shipped) BETWEEN ? AND ?
|
||||
GROUP BY c.id`, [clientIds, reqArgs.from, reqArgs.to]);
|
||||
|
||||
const clientData = new Map();
|
||||
for (const client of clients)
|
||||
clientData.set(client.id, client);
|
||||
|
||||
for (const clientId of reqArgs.clientIds) {
|
||||
const client = clientData.get(clientId);
|
||||
|
||||
if (client) {
|
||||
const args = Object.assign({
|
||||
recipientId: clientId,
|
||||
recipient: client.email,
|
||||
replyTo: client.salesPersonEmail
|
||||
}, response.locals);
|
||||
|
||||
const email = new Email('campaign-metrics', args);
|
||||
await email.send();
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
};
|
|
@ -0,0 +1,6 @@
|
|||
const express = require('express');
|
||||
const router = new express.Router();
|
||||
|
||||
router.post('/consumption', require('./consumption'));
|
||||
|
||||
module.exports = router;
|
|
@ -15,4 +15,8 @@ module.exports = [
|
|||
url: '/api/closure',
|
||||
cb: require('./closure')
|
||||
},
|
||||
{
|
||||
url: '/api/notify',
|
||||
cb: require('./notify')
|
||||
}
|
||||
];
|
||||
|
|
Loading…
Reference in New Issue