Merge pull request '#7710 - clone with ticket packaging' (!2878) from 7710-cloneWithTicketPackaging into dev
gitea/salix/pipeline/head This commit looks good
Details
gitea/salix/pipeline/head This commit looks good
Details
Reviewed-on: #2878 Reviewed-by: Javier Segarra <jsegarra@verdnatura.es>
This commit is contained in:
commit
638a43d3e6
|
@ -0,0 +1,24 @@
|
||||||
|
DELETE FROM `salix`.`ACL`
|
||||||
|
WHERE `model` = 'Ticket'
|
||||||
|
AND `property` = 'refund'
|
||||||
|
AND `accessType` = 'WRITE'
|
||||||
|
AND `permission` = 'ALLOW'
|
||||||
|
AND `principalType` = 'ROLE'
|
||||||
|
AND `principalId` = 'salesAssistant';
|
||||||
|
|
||||||
|
UPDATE `salix`.`ACL`
|
||||||
|
SET `property` = 'cloneAll'
|
||||||
|
WHERE `model` = 'Ticket'
|
||||||
|
AND `property` = 'refund'
|
||||||
|
AND `accessType` = 'WRITE'
|
||||||
|
AND `permission` = 'ALLOW'
|
||||||
|
AND `principalType` = 'ROLE'
|
||||||
|
AND `principalId` IN ('invoicing', 'claimManager', 'logistic');
|
||||||
|
|
||||||
|
DELETE FROM `salix`.`ACL`
|
||||||
|
WHERE `model` = 'Ticket'
|
||||||
|
AND `property` = 'clone'
|
||||||
|
AND `accessType` = 'WRITE'
|
||||||
|
AND `permission` = 'ALLOW'
|
||||||
|
AND `principalType` = 'ROLE'
|
||||||
|
AND `principalId` = 'administrative';
|
|
@ -43,7 +43,7 @@ module.exports = Self => {
|
||||||
const tickets = await models.Ticket.find(filter, myOptions);
|
const tickets = await models.Ticket.find(filter, myOptions);
|
||||||
|
|
||||||
const ticketsIds = tickets.map(ticket => ticket.id);
|
const ticketsIds = tickets.map(ticket => ticket.id);
|
||||||
const refundedTickets = await models.Ticket.refund(ctx, ticketsIds, withWarehouse, myOptions);
|
const refundedTickets = await models.Ticket.cloneAll(ctx, ticketsIds, withWarehouse, true, myOptions);
|
||||||
|
|
||||||
if (tx) await tx.commit();
|
if (tx) await tx.commit();
|
||||||
|
|
||||||
|
|
|
@ -82,20 +82,12 @@ module.exports = Self => {
|
||||||
myOptions.transaction = tx;
|
myOptions.transaction = tx;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const filterRef = {where: {refFk: refFk}};
|
const tickets = await models.Ticket.find({where: {refFk}}, myOptions);
|
||||||
const tickets = await models.Ticket.find(filterRef, myOptions);
|
|
||||||
const ticketsIds = tickets.map(ticket => ticket.id);
|
const ticketsIds = tickets.map(ticket => ticket.id);
|
||||||
const refundTickets = await models.Ticket.refund(ctx, ticketsIds, null, myOptions);
|
const refundTickets = await models.Ticket.cloneAll(ctx, ticketsIds, false, true, myOptions);
|
||||||
|
|
||||||
const filterTicket = {where: {ticketFk: {inq: ticketsIds}}};
|
const clonedTickets = await models.Ticket.cloneAll(ctx, ticketsIds, false, false, myOptions);
|
||||||
|
|
||||||
const services = await models.TicketService.find(filterTicket, myOptions);
|
|
||||||
const servicesIds = services.map(service => service.id);
|
|
||||||
|
|
||||||
const sales = await models.Sale.find(filterTicket, myOptions);
|
|
||||||
const salesIds = sales.map(sale => sale.id);
|
|
||||||
|
|
||||||
const clonedTickets = await models.Sale.clone(ctx, salesIds, servicesIds, null, false, myOptions);
|
|
||||||
const clonedTicketIds = [];
|
const clonedTicketIds = [];
|
||||||
|
|
||||||
for (const clonedTicket of clonedTickets) {
|
for (const clonedTicket of clonedTickets) {
|
||||||
|
|
|
@ -88,28 +88,7 @@
|
||||||
translate>
|
translate>
|
||||||
Show CITES letter
|
Show CITES letter
|
||||||
</vn-item>
|
</vn-item>
|
||||||
<vn-item class="dropdown"
|
|
||||||
vn-click-stop="refundMenu.show($event, 'left')"
|
|
||||||
vn-tooltip="Create a refund ticket for each ticket on the current invoice"
|
|
||||||
vn-acl="invoicing, claimManager, salesAssistant"
|
|
||||||
vn-acl-action="remove"
|
|
||||||
translate>
|
|
||||||
Refund...
|
|
||||||
<vn-menu vn-id="refundMenu">
|
|
||||||
<vn-list>
|
|
||||||
<vn-item
|
|
||||||
ng-click="$ctrl.refundInvoiceOut(true)"
|
|
||||||
translate>
|
|
||||||
with warehouse
|
|
||||||
</vn-item>
|
|
||||||
<vn-item
|
|
||||||
ng-click="$ctrl.refundInvoiceOut(false)"
|
|
||||||
translate>
|
|
||||||
without warehouse
|
|
||||||
</vn-item>
|
|
||||||
</vn-list>
|
|
||||||
</vn-menu>
|
|
||||||
</vn-item>
|
|
||||||
</vn-list>
|
</vn-list>
|
||||||
</vn-menu>
|
</vn-menu>
|
||||||
<vn-confirm
|
<vn-confirm
|
||||||
|
|
|
@ -135,21 +135,6 @@ class Controller extends Section {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
refundInvoiceOut(withWarehouse) {
|
|
||||||
const query = 'InvoiceOuts/refund';
|
|
||||||
const params = {ref: this.invoiceOut.ref, withWarehouse: withWarehouse};
|
|
||||||
this.$http.post(query, params).then(res => {
|
|
||||||
const tickets = res.data;
|
|
||||||
const refundTickets = tickets.map(ticket => ticket.id);
|
|
||||||
|
|
||||||
this.vnApp.showSuccess(this.$t('The following refund tickets have been created', {
|
|
||||||
ticketId: refundTickets.join(',')
|
|
||||||
}));
|
|
||||||
if (refundTickets.length == 1)
|
|
||||||
this.$state.go('ticket.card.sale', {id: refundTickets[0]});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
transferInvoice() {
|
transferInvoice() {
|
||||||
const params = {
|
const params = {
|
||||||
id: this.invoiceOut.id,
|
id: this.invoiceOut.id,
|
||||||
|
|
|
@ -105,17 +105,4 @@ describe('vnInvoiceOutDescriptorMenu', () => {
|
||||||
expect(controller.vnApp.showMessage).toHaveBeenCalled();
|
expect(controller.vnApp.showMessage).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('refundInvoiceOut()', () => {
|
|
||||||
it('should make a query and show a success message', () => {
|
|
||||||
jest.spyOn(controller.vnApp, 'showSuccess');
|
|
||||||
const params = {ref: controller.invoiceOut.ref};
|
|
||||||
|
|
||||||
$httpBackend.expectPOST(`InvoiceOuts/refund`, params).respond([{id: 1}, {id: 2}]);
|
|
||||||
controller.refundInvoiceOut();
|
|
||||||
$httpBackend.flush();
|
|
||||||
|
|
||||||
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,40 +1,25 @@
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('clone', {
|
Self.remoteMethodCtx('clone', {
|
||||||
description: 'Clone sales and services provided',
|
description: 'Clone sales, services, and ticket packaging provided',
|
||||||
accessType: 'WRITE',
|
accessType: 'WRITE',
|
||||||
accepts: [
|
accepts: [
|
||||||
{
|
{arg: 'salesIds', type: ['number']},
|
||||||
arg: 'salesIds',
|
{arg: 'servicesIds', type: ['number']},
|
||||||
type: ['number'],
|
{arg: 'ticketPackagingIds', type: ['number']},
|
||||||
}, {
|
{arg: 'withWarehouse', type: 'boolean', required: true},
|
||||||
arg: 'servicesIds',
|
{arg: 'negative', type: 'boolean'}
|
||||||
type: ['number']
|
|
||||||
}, {
|
|
||||||
arg: 'withWarehouse',
|
|
||||||
type: 'boolean',
|
|
||||||
required: true
|
|
||||||
}, {
|
|
||||||
arg: 'negative',
|
|
||||||
type: 'boolean'
|
|
||||||
}
|
|
||||||
],
|
],
|
||||||
returns: {
|
returns: {type: ['object'], root: true},
|
||||||
type: ['object'],
|
http: {path: `/clone`, verb: 'POST'}
|
||||||
root: true
|
|
||||||
},
|
|
||||||
http: {
|
|
||||||
path: `/clone`,
|
|
||||||
verb: 'POST'
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
Self.clone = async(ctx, salesIds, servicesIds, withWarehouse, negative, options) => {
|
|
||||||
|
Self.clone = async(ctx, salesIds, servicesIds, ticketPackagingIds, withWarehouse, negative, options) => {
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
const myOptions = {};
|
const myOptions = {};
|
||||||
let tx;
|
let tx;
|
||||||
const newTickets = [];
|
const newTickets = [];
|
||||||
|
|
||||||
if (typeof options == 'object')
|
if (typeof options === 'object') Object.assign(myOptions, options);
|
||||||
Object.assign(myOptions, options);
|
|
||||||
|
|
||||||
if (!myOptions.transaction) {
|
if (!myOptions.transaction) {
|
||||||
tx = await Self.beginTransaction({});
|
tx = await Self.beginTransaction({});
|
||||||
|
@ -44,8 +29,9 @@ module.exports = Self => {
|
||||||
try {
|
try {
|
||||||
let sales;
|
let sales;
|
||||||
let services;
|
let services;
|
||||||
|
let ticketPackaging;
|
||||||
|
|
||||||
if (salesIds && salesIds.length) {
|
if (salesIds?.length) {
|
||||||
sales = await models.Sale.find({
|
sales = await models.Sale.find({
|
||||||
where: {id: {inq: salesIds}},
|
where: {id: {inq: salesIds}},
|
||||||
include: {
|
include: {
|
||||||
|
@ -57,12 +43,18 @@ module.exports = Self => {
|
||||||
}, myOptions);
|
}, myOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (servicesIds && servicesIds.length) {
|
if (servicesIds?.length) {
|
||||||
services = await models.TicketService.find({
|
services = await models.TicketService.find({
|
||||||
where: {id: {inq: servicesIds}}
|
where: {id: {inq: servicesIds}}
|
||||||
}, myOptions);
|
}, myOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ticketPackagingIds?.length) {
|
||||||
|
ticketPackaging = await models.TicketPackaging.find({
|
||||||
|
where: {id: {inq: ticketPackagingIds}}
|
||||||
|
}, myOptions);
|
||||||
|
}
|
||||||
|
|
||||||
let ticketsIds = sales ?
|
let ticketsIds = sales ?
|
||||||
[...new Set(sales.map(sale => sale.ticketFk))] :
|
[...new Set(sales.map(sale => sale.ticketFk))] :
|
||||||
[...new Set(services.map(service => service.ticketFk))];
|
[...new Set(services.map(service => service.ticketFk))];
|
||||||
|
@ -74,12 +66,12 @@ module.exports = Self => {
|
||||||
ctx,
|
ctx,
|
||||||
ticketId,
|
ticketId,
|
||||||
withWarehouse,
|
withWarehouse,
|
||||||
negative,
|
|
||||||
myOptions
|
myOptions
|
||||||
);
|
);
|
||||||
newTickets.push(newTicket);
|
newTickets.push(newTicket);
|
||||||
mappedTickets.set(ticketId, newTicket.id);
|
mappedTickets.set(ticketId, newTicket.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sales) {
|
if (sales) {
|
||||||
for (const sale of sales) {
|
for (const sale of sales) {
|
||||||
const newTicketId = mappedTickets.get(sale.ticketFk);
|
const newTicketId = mappedTickets.get(sale.ticketFk);
|
||||||
|
@ -116,6 +108,18 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ticketPackaging) {
|
||||||
|
for (const packaging of ticketPackaging) {
|
||||||
|
const newTicketId = mappedTickets.get(packaging.ticketFk);
|
||||||
|
|
||||||
|
await models.TicketPackaging.create({
|
||||||
|
ticketFk: newTicketId,
|
||||||
|
packagingFk: packaging.packagingFk,
|
||||||
|
quantity: negative ? -packaging.quantity : packaging.quantity
|
||||||
|
}, myOptions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (tx) await tx.commit();
|
if (tx) await tx.commit();
|
||||||
|
|
||||||
return newTickets;
|
return newTickets;
|
||||||
|
@ -124,13 +128,7 @@ module.exports = Self => {
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createTicket(
|
async function createTicket(ctx, ticketId, withWarehouse, myOptions) {
|
||||||
ctx,
|
|
||||||
ticketId,
|
|
||||||
withWarehouse,
|
|
||||||
negative,
|
|
||||||
myOptions
|
|
||||||
) {
|
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
const now = Date.vnNew();
|
const now = Date.vnNew();
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ describe('Ticket cloning - clone function', () => {
|
||||||
const servicesIds = [];
|
const servicesIds = [];
|
||||||
const withWarehouse = true;
|
const withWarehouse = true;
|
||||||
const negative = false;
|
const negative = false;
|
||||||
const newTickets = await models.Sale.clone(ctx, salesIds, servicesIds, withWarehouse, negative, options);
|
const newTickets = await models.Sale.clone(ctx, salesIds, servicesIds, null, withWarehouse, negative, options);
|
||||||
|
|
||||||
expect(newTickets).toBeDefined();
|
expect(newTickets).toBeDefined();
|
||||||
expect(newTickets.length).toBeGreaterThan(0);
|
expect(newTickets.length).toBeGreaterThan(0);
|
||||||
|
@ -30,7 +30,7 @@ describe('Ticket cloning - clone function', () => {
|
||||||
const negative = true;
|
const negative = true;
|
||||||
const salesIds = [7, 8];
|
const salesIds = [7, 8];
|
||||||
const servicesIds = [];
|
const servicesIds = [];
|
||||||
const newTickets = await models.Sale.clone(ctx, salesIds, servicesIds, false, negative, options);
|
const newTickets = await models.Sale.clone(ctx, salesIds, servicesIds, null, false, negative, options);
|
||||||
|
|
||||||
for (const ticket of newTickets) {
|
for (const ticket of newTickets) {
|
||||||
const sales = await models.Sale.find({where: {ticketFk: ticket.id}}, options);
|
const sales = await models.Sale.find({where: {ticketFk: ticket.id}}, options);
|
||||||
|
@ -43,7 +43,7 @@ describe('Ticket cloning - clone function', () => {
|
||||||
it('should create new components and services for cloned tickets', async() => {
|
it('should create new components and services for cloned tickets', async() => {
|
||||||
const servicesIds = [2];
|
const servicesIds = [2];
|
||||||
const salesIds = [5];
|
const salesIds = [5];
|
||||||
const newTickets = await models.Sale.clone(ctx, salesIds, servicesIds, false, false, options);
|
const newTickets = await models.Sale.clone(ctx, salesIds, servicesIds, null, false, false, options);
|
||||||
|
|
||||||
for (const ticket of newTickets) {
|
for (const ticket of newTickets) {
|
||||||
const sale = await models.Sale.findOne({where: {ticketFk: ticket.id}}, options);
|
const sale = await models.Sale.findOne({where: {ticketFk: ticket.id}}, options);
|
||||||
|
@ -58,7 +58,7 @@ describe('Ticket cloning - clone function', () => {
|
||||||
it('should create a ticket without sales', async() => {
|
it('should create a ticket without sales', async() => {
|
||||||
const servicesIds = [4];
|
const servicesIds = [4];
|
||||||
|
|
||||||
const tickets = await models.Sale.clone(ctx, null, servicesIds, false, false, options);
|
const tickets = await models.Sale.clone(ctx, null, servicesIds, null, false, false, options);
|
||||||
const refundedTicket = await getTicketRefund(tickets[0].id, options);
|
const refundedTicket = await getTicketRefund(tickets[0].id, options);
|
||||||
|
|
||||||
expect(refundedTicket).toBeDefined();
|
expect(refundedTicket).toBeDefined();
|
||||||
|
|
|
@ -1,54 +0,0 @@
|
||||||
module.exports = Self => {
|
|
||||||
Self.remoteMethodCtx('clone', {
|
|
||||||
description: 'clone a ticket and return the new ticket id',
|
|
||||||
accessType: 'WRITE',
|
|
||||||
accepts: [{
|
|
||||||
arg: 'id',
|
|
||||||
type: 'number',
|
|
||||||
required: true,
|
|
||||||
description: 'The ticket id',
|
|
||||||
http: {source: 'path'}
|
|
||||||
}, {
|
|
||||||
arg: 'shipped',
|
|
||||||
type: 'date',
|
|
||||||
}, {
|
|
||||||
arg: 'withWarehouse',
|
|
||||||
type: 'boolean',
|
|
||||||
}
|
|
||||||
],
|
|
||||||
returns: {
|
|
||||||
type: 'number',
|
|
||||||
root: true
|
|
||||||
},
|
|
||||||
http: {
|
|
||||||
path: `/:id/clone`,
|
|
||||||
verb: 'POST'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Self.clone = async(ctx, id, shipped, withWarehouse, options) => {
|
|
||||||
const myOptions = {userId: ctx.req.accessToken.userId};
|
|
||||||
let tx;
|
|
||||||
|
|
||||||
if (typeof options == 'object')
|
|
||||||
Object.assign(myOptions, options);
|
|
||||||
|
|
||||||
if (!myOptions.transaction) {
|
|
||||||
tx = await Self.beginTransaction({});
|
|
||||||
myOptions.transaction = tx;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const [, [{clonedTicketId}]] = await Self.rawSql(`
|
|
||||||
CALL vn.ticket_cloneAll(?, ?, ?, @clonedTicketId);
|
|
||||||
SELECT @clonedTicketId clonedTicketId;`,
|
|
||||||
[id, shipped, withWarehouse], myOptions);
|
|
||||||
|
|
||||||
if (tx) await tx.commit();
|
|
||||||
return clonedTicketId;
|
|
||||||
} catch (e) {
|
|
||||||
if (tx) await tx.rollback();
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethodCtx('cloneAll', {
|
||||||
|
description: 'Clone tickets, sales, services and packages',
|
||||||
|
accessType: 'WRITE',
|
||||||
|
accepts: [
|
||||||
|
{
|
||||||
|
arg: 'ticketsIds',
|
||||||
|
type: ['number'],
|
||||||
|
required: true,
|
||||||
|
description: 'IDs of the tickets to clone'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'withWarehouse',
|
||||||
|
type: 'boolean',
|
||||||
|
required: true,
|
||||||
|
description: 'true: keep original warehouse; false: set to null'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'negative',
|
||||||
|
type: 'boolean',
|
||||||
|
required: true,
|
||||||
|
description: 'true: invert quantities; false: keep as is.'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
returns: {
|
||||||
|
type: ['object'],
|
||||||
|
root: true,
|
||||||
|
description: 'The cloned tickets with associated data'
|
||||||
|
},
|
||||||
|
http: {
|
||||||
|
path: `/cloneAll`,
|
||||||
|
verb: 'POST'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.cloneAll = async(ctx, ticketsIds, withWarehouse, negative, options) => {
|
||||||
|
const models = Self.app.models;
|
||||||
|
const myOptions = typeof options == 'object' ? {...options} : {};
|
||||||
|
let tx;
|
||||||
|
|
||||||
|
if (!myOptions.transaction) {
|
||||||
|
tx = await Self.beginTransaction({});
|
||||||
|
myOptions.transaction = tx;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const filter = {where: {ticketFk: {inq: ticketsIds}}};
|
||||||
|
|
||||||
|
const [sales, services, ticketPackaging] = await Promise.all([
|
||||||
|
models.Sale.find(filter, myOptions),
|
||||||
|
models.TicketService.find(filter, myOptions),
|
||||||
|
models.TicketPackaging.find(filter, myOptions)
|
||||||
|
]);
|
||||||
|
|
||||||
|
const salesIds = sales.map(({id}) => id);
|
||||||
|
const servicesIds = services.map(({id}) => id);
|
||||||
|
const ticketPackagingIds = ticketPackaging.map(({id}) => id);
|
||||||
|
|
||||||
|
const clonedTickets = await models.Sale.clone(
|
||||||
|
ctx,
|
||||||
|
salesIds,
|
||||||
|
servicesIds,
|
||||||
|
ticketPackagingIds,
|
||||||
|
withWarehouse,
|
||||||
|
negative,
|
||||||
|
myOptions
|
||||||
|
);
|
||||||
|
|
||||||
|
if (tx) await tx.commit();
|
||||||
|
|
||||||
|
return clonedTickets;
|
||||||
|
} catch (e) {
|
||||||
|
if (tx) await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
|
@ -1,58 +0,0 @@
|
||||||
module.exports = Self => {
|
|
||||||
Self.remoteMethodCtx('refund', {
|
|
||||||
description: 'Create refund tickets with all their sales and services',
|
|
||||||
accessType: 'WRITE',
|
|
||||||
accepts: [
|
|
||||||
{
|
|
||||||
arg: 'ticketsIds',
|
|
||||||
type: ['number'],
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
arg: 'withWarehouse',
|
|
||||||
type: 'boolean',
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
returns: {
|
|
||||||
type: ['object'],
|
|
||||||
root: true
|
|
||||||
},
|
|
||||||
http: {
|
|
||||||
path: `/refund`,
|
|
||||||
verb: 'POST'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Self.refund = async(ctx, ticketsIds, withWarehouse, options) => {
|
|
||||||
const models = Self.app.models;
|
|
||||||
const myOptions = {};
|
|
||||||
let tx;
|
|
||||||
|
|
||||||
if (typeof options == 'object')
|
|
||||||
Object.assign(myOptions, options);
|
|
||||||
|
|
||||||
if (!myOptions.transaction) {
|
|
||||||
tx = await Self.beginTransaction({});
|
|
||||||
myOptions.transaction = tx;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const filter = {where: {ticketFk: {inq: ticketsIds}}};
|
|
||||||
const sales = await models.Sale.find(filter, myOptions);
|
|
||||||
const salesIds = sales.map(sale => sale.id);
|
|
||||||
|
|
||||||
const services = await models.TicketService.find(filter, myOptions);
|
|
||||||
const servicesIds = services.map(service => service.id);
|
|
||||||
|
|
||||||
const refundedTickets = await models.Sale.clone(ctx, salesIds, servicesIds, withWarehouse, true, myOptions);
|
|
||||||
|
|
||||||
if (tx) await tx.commit();
|
|
||||||
|
|
||||||
return refundedTickets;
|
|
||||||
} catch (e) {
|
|
||||||
if (tx) await tx.rollback();
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
|
@ -1,43 +0,0 @@
|
||||||
const models = require('vn-loopback/server/server').models;
|
|
||||||
|
|
||||||
describe('Ticket cloning - clone function', () => {
|
|
||||||
const ctx = beforeAll.getCtx();
|
|
||||||
let options;
|
|
||||||
let tx;
|
|
||||||
const ticketId = 1;
|
|
||||||
const shipped = Date.vnNew();
|
|
||||||
|
|
||||||
beforeEach(async() => {
|
|
||||||
options = {transaction: tx};
|
|
||||||
tx = await models.Ticket.beginTransaction({});
|
|
||||||
options.transaction = tx;
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(async() => {
|
|
||||||
await tx.rollback();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should clone a new ticket without warehouse', async() => {
|
|
||||||
const originalTicket = await models.Ticket.findById(ticketId, null, options);
|
|
||||||
|
|
||||||
const newTicketId = await models.Ticket.clone(ctx, ticketId, shipped, false, options);
|
|
||||||
const newTicket = await models.Ticket.findById(newTicketId, null, options);
|
|
||||||
|
|
||||||
expect(newTicket.clientFk).toEqual(originalTicket.clientFk);
|
|
||||||
expect(newTicket.companyFk).toEqual(originalTicket.companyFk);
|
|
||||||
expect(newTicket.addressFk).toEqual(originalTicket.addressFk);
|
|
||||||
expect(newTicket.warehouseFk).toBeFalsy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should clone a new ticket with warehouse', async() => {
|
|
||||||
const originalTicket = await models.Ticket.findById(ticketId, null, options);
|
|
||||||
|
|
||||||
const newTicketId = await models.Ticket.clone(ctx, ticketId, shipped, true, options);
|
|
||||||
const newTicket = await models.Ticket.findById(newTicketId, null, options);
|
|
||||||
|
|
||||||
expect(newTicket.clientFk).toEqual(originalTicket.clientFk);
|
|
||||||
expect(newTicket.companyFk).toEqual(originalTicket.companyFk);
|
|
||||||
expect(newTicket.addressFk).toEqual(originalTicket.addressFk);
|
|
||||||
expect(newTicket.warehouseFk).toEqual(originalTicket.warehouseFk);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
const models = require('vn-loopback/server/server').models;
|
||||||
|
const LoopBackContext = require('loopback-context');
|
||||||
|
|
||||||
|
describe('Ticket cloning - cloneAll function', () => {
|
||||||
|
const activeCtx = {
|
||||||
|
accessToken: {userId: 1},
|
||||||
|
http: {
|
||||||
|
req: {
|
||||||
|
headers: {origin: 'http://localhost'}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const ctx = {req: activeCtx};
|
||||||
|
let options;
|
||||||
|
let tx;
|
||||||
|
const ticketIds = [1, 2];
|
||||||
|
const withWarehouse = true;
|
||||||
|
const negative = false;
|
||||||
|
|
||||||
|
beforeEach(async() => {
|
||||||
|
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({active: ctx.req});
|
||||||
|
tx = await models.Ticket.beginTransaction({});
|
||||||
|
options = {transaction: tx};
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async() => {
|
||||||
|
if (tx)
|
||||||
|
await tx.rollback();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should clone all provided tickets with their associated sales, services, and packages', async() => {
|
||||||
|
const originalTickets = await models.Ticket.find({where: {id: {inq: ticketIds}}}, options);
|
||||||
|
const originalSales = await models.Sale.find({where: {ticketFk: {inq: ticketIds}}}, options);
|
||||||
|
const originalServices = await models.TicketService.find({where: {ticketFk: {inq: ticketIds}}}, options);
|
||||||
|
const originalTicketPackaging =
|
||||||
|
await models.TicketPackaging.find({where: {ticketFk: {inq: ticketIds}}}, options);
|
||||||
|
|
||||||
|
// Pass the ctx correctly to the cloneAll function
|
||||||
|
const clonedTickets = await models.Ticket.cloneAll(ctx, ticketIds, withWarehouse, negative, options);
|
||||||
|
|
||||||
|
expect(clonedTickets.length).toEqual(originalTickets.length);
|
||||||
|
|
||||||
|
const clonedSales = await models.Sale.find({where: {ticketFk: {inq: clonedTickets.map(t => t.id)}}}, options);
|
||||||
|
const clonedServices =
|
||||||
|
await models.TicketService.find({where: {ticketFk: {inq: clonedTickets.map(t => t.id)}}}, options);
|
||||||
|
const clonedTicketPackaging =
|
||||||
|
await models.TicketPackaging.find({where: {ticketFk: {inq: clonedTickets.map(t => t.id)}}}, options);
|
||||||
|
|
||||||
|
expect(clonedSales.length).toEqual(originalSales.length);
|
||||||
|
expect(clonedServices.length).toEqual(originalServices.length);
|
||||||
|
expect(clonedTicketPackaging.length).toEqual(originalTicketPackaging.length);
|
||||||
|
});
|
||||||
|
});
|
|
@ -26,7 +26,7 @@ module.exports = function(Self) {
|
||||||
require('../methods/ticket/isLocked')(Self);
|
require('../methods/ticket/isLocked')(Self);
|
||||||
require('../methods/ticket/freightCost')(Self);
|
require('../methods/ticket/freightCost')(Self);
|
||||||
require('../methods/ticket/getComponentsSum')(Self);
|
require('../methods/ticket/getComponentsSum')(Self);
|
||||||
require('../methods/ticket/refund')(Self);
|
require('../methods/ticket/cloneAll')(Self);
|
||||||
require('../methods/ticket/deliveryNotePdf')(Self);
|
require('../methods/ticket/deliveryNotePdf')(Self);
|
||||||
require('../methods/ticket/deliveryNoteEmail')(Self);
|
require('../methods/ticket/deliveryNoteEmail')(Self);
|
||||||
require('../methods/ticket/deliveryNoteCsv')(Self);
|
require('../methods/ticket/deliveryNoteCsv')(Self);
|
||||||
|
@ -46,5 +46,4 @@ module.exports = function(Self) {
|
||||||
require('../methods/ticket/invoiceTicketsAndPdf')(Self);
|
require('../methods/ticket/invoiceTicketsAndPdf')(Self);
|
||||||
require('../methods/ticket/docuwareDownload')(Self);
|
require('../methods/ticket/docuwareDownload')(Self);
|
||||||
require('../methods/ticket/myLastModified')(Self);
|
require('../methods/ticket/myLastModified')(Self);
|
||||||
require('../methods/ticket/clone')(Self);
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -287,15 +287,24 @@ class Controller extends Section {
|
||||||
}
|
}
|
||||||
|
|
||||||
refund(withWarehouse) {
|
refund(withWarehouse) {
|
||||||
const params = {ticketsIds: [this.id], withWarehouse: withWarehouse};
|
const params = {
|
||||||
const query = 'Tickets/refund';
|
ticketsIds: [this.id],
|
||||||
|
withWarehouse: withWarehouse,
|
||||||
|
negative: true // Asumimos que queremos cantidades negativas para reembolsos
|
||||||
|
};
|
||||||
|
const query = 'Tickets/cloneAll';
|
||||||
return this.$http.post(query, params)
|
return this.$http.post(query, params)
|
||||||
.then(res => {
|
.then(res => {
|
||||||
const [refundTicket] = res.data;
|
const [refundTicket] = res.data;
|
||||||
this.vnApp.showSuccess(this.$t('The following refund ticket have been created', {
|
this.vnApp.showSuccess(this.$t('The following refund ticket has been created', {
|
||||||
ticketId: refundTicket.id
|
ticketId: refundTicket.id
|
||||||
}));
|
}));
|
||||||
this.$state.go('ticket.card.sale', {id: refundTicket.id});
|
this.$state.go('ticket.card.sale', {id: refundTicket.id});
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
this.vnApp.showError(this.$t('Error creating refund ticket', {
|
||||||
|
error: error.data?.error?.message || 'Unknown error'
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -217,24 +217,6 @@ describe('Ticket Component vnTicketDescriptorMenu', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('refund()', () => {
|
|
||||||
it('should make a query and go to ticket.card.sale', () => {
|
|
||||||
controller.$state.go = jest.fn();
|
|
||||||
|
|
||||||
controller._id = ticket.id;
|
|
||||||
|
|
||||||
const params = {
|
|
||||||
ticketsIds: [16]
|
|
||||||
};
|
|
||||||
const response = {id: 99};
|
|
||||||
$httpBackend.expectPOST('Tickets/refund', params).respond([response]);
|
|
||||||
controller.refund();
|
|
||||||
$httpBackend.flush();
|
|
||||||
|
|
||||||
expect(controller.$state.go).toHaveBeenCalledWith('ticket.card.sale', response);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('sendChangesSms()', () => {
|
describe('sendChangesSms()', () => {
|
||||||
it('should make a query and open the sms dialog', () => {
|
it('should make a query and open the sms dialog', () => {
|
||||||
controller.$.sms = {open: () => {}};
|
controller.$.sms = {open: () => {}};
|
||||||
|
|
Loading…
Reference in New Issue