Merge branch 'dev' into 3449-client_credit
gitea/salix/pipeline/head This commit looks good Details

This commit is contained in:
Carlos Jimenez Ruiz 2021-12-24 10:48:15 +00:00
commit a969f1f234
39 changed files with 569 additions and 57 deletions

View File

@ -0,0 +1,2 @@
INSERT INTO salix.ACL (model,property,accessType,permission,principalType,principalId)
VALUES ('Sale','payBack','WRITE','ALLOW','ROLE','employee');

View File

@ -0,0 +1 @@
ALTER TABLE `vn`.`payMethod` ADD hasVerified TINYINT(1) DEFAULT 0 NULL;

View File

@ -0,0 +1,90 @@
DROP PROCEDURE IF EXISTS `vn`.`ticket_doRefund`;
DELIMITER $$
$$
CREATE DEFINER=`root`@`localhost` PROCEDURE `vn`.`ticket_doRefund`(IN vOriginTicket INT, OUT vNewTicket INT)
BEGIN
DECLARE vDone BIT DEFAULT 0;
DECLARE vCustomer MEDIUMINT;
DECLARE vWarehouse TINYINT;
DECLARE vCompany MEDIUMINT;
DECLARE vAddress MEDIUMINT;
DECLARE vRefundAgencyMode INT;
DECLARE vItemFk INT;
DECLARE vQuantity DECIMAL (10,2);
DECLARE vConcept VARCHAR(50);
DECLARE vPrice DECIMAL (10,2);
DECLARE vDiscount TINYINT;
DECLARE vSaleNew INT;
DECLARE vSaleMain INT;
DECLARE vZoneFk INT;
DECLARE vRsMainTicket CURSOR FOR
SELECT *
FROM tmp.sale;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET vDone = 1;
SELECT id INTO vRefundAgencyMode
FROM agencyMode WHERE `name` = 'ABONO';
SELECT clientFk, warehouseFk, companyFk, addressFk
INTO vCustomer, vWarehouse, vCompany, vAddress
FROM ticket
WHERE id = vOriginTicket;
SELECT id INTO vZoneFk
FROM zone WHERE agencyModeFk = vRefundAgencyMode
LIMIT 1;
INSERT INTO vn.ticket (
clientFk,
shipped,
addressFk,
agencyModeFk,
nickname,
warehouseFk,
companyFk,
landed,
zoneFk
)
SELECT
vCustomer,
CURDATE(),
vAddress,
vRefundAgencyMode,
a.nickname,
vWarehouse,
vCompany,
CURDATE(),
vZoneFk
FROM address a
WHERE a.id = vAddress;
SET vNewTicket = LAST_INSERT_ID();
SET vDone := 0;
OPEN vRsMainTicket ;
FETCH vRsMainTicket INTO vSaleMain, vItemFk, vQuantity, vConcept, vPrice, vDiscount;
WHILE NOT vDone DO
INSERT INTO vn.sale(ticketFk, itemFk, quantity, concept, price, discount)
VALUES( vNewTicket, vItemFk, vQuantity, vConcept, vPrice, vDiscount );
SET vSaleNew = LAST_INSERT_ID();
INSERT INTO vn.saleComponent(saleFk,componentFk,`value`)
SELECT vSaleNew,componentFk,`value`
FROM vn.saleComponent
WHERE saleFk = vSaleMain;
FETCH vRsMainTicket INTO vSaleMain, vItemFk, vQuantity, vConcept, vPrice, vDiscount;
END WHILE;
CLOSE vRsMainTicket;
END;
$$
DELIMITER ;

View File

@ -0,0 +1,3 @@
UPDATE `vn`.`department`
SET `notificationEmail` = 'finanzas@verdnatura.es'
WHERE `name` = 'FINANZAS';

View File

@ -0,0 +1,2 @@
UPDATE `vn`.`supplier`
SET isPayMethodChecked = TRUE;

View File

@ -0,0 +1,33 @@
DELIMITER $$
$$
CREATE DEFINER=`root`@`%` TRIGGER `vn`.`payment_afterInsert` AFTER INSERT ON `payment` FOR EACH ROW
BEGIN
DECLARE vIsPayMethodChecked BOOLEAN;
DECLARE vEmail VARCHAR(150);
SELECT isPayMethodChecked INTO vIsPayMethodChecked
FROM supplier
WHERE id = NEW.supplierFk;
IF vIsPayMethodChecked = FALSE THEN
SELECT notificationEmail INTO vEmail
FROM department
WHERE name = 'FINANZAS';
CALL mail_insert(
vEmail,
NULL,
'Pago con método sin verificar',
CONCAT(
'Se ha realizado el pago ',
NEW.id,
' al proveedor ',
NEW.supplierFk,
' con el método de pago sin verificar.'
)
);
END IF;
END$$
DELIMITER ;

View File

@ -0,0 +1,26 @@
DROP TRIGGER IF EXISTS `vn`.`supplier_beforeUpdate`;
DELIMITER $$
$$
CREATE DEFINER=`root`@`%` TRIGGER `vn`.`supplier_beforeUpdate`
BEFORE UPDATE ON `vn`.`supplier` FOR EACH ROW
BEGIN
DECLARE vHasChange BOOL DEFAULT FALSE;
DECLARE vPayMethodHasVerified BOOL;
SELECT hasVerified INTO vPayMethodHasVerified
FROM payMethod
WHERE id = NEW.payMethodFk;
SET vHasChange = (NEW.payDemFk <> OLD.payDemFk) OR (NEW.payDay <> OLD.payDay);
IF vPayMethodHasVerified AND !vHasChange THEN
SET vHasChange = (NEW.payMethodFk <> OLD.payMethodFk);
END IF;
IF vHasChange THEN
SET NEW.isPayMethodChecked = FALSE;
END IF;
END$$
DELIMITER ;

View File

@ -1 +0,0 @@
Delete me!

View File

@ -217,14 +217,14 @@ UPDATE `vn`.`agencyMode` SET `web` = 1, `reportMail` = 'no-reply@gothamcity.com'
UPDATE `vn`.`agencyMode` SET `code` = 'refund' WHERE `id` = 23;
INSERT INTO `vn`.`payMethod`(`id`,`code`, `name`, `graceDays`, `outstandingDebt`, `isIbanRequiredForClients`, `isIbanRequiredForSuppliers`)
INSERT INTO `vn`.`payMethod`(`id`,`code`, `name`, `graceDays`, `outstandingDebt`, `isIbanRequiredForClients`, `isIbanRequiredForSuppliers`, `hasVerified`)
VALUES
(1, NULL, 'PayMethod one', 0, 001, 0, 0),
(2, NULL, 'PayMethod two', 10, 001, 0, 0),
(3, 'compensation', 'PayMethod three', 0, 001, 0, 0),
(4, NULL, 'PayMethod with IBAN', 0, 001, 1, 0),
(5, NULL, 'PayMethod five', 10, 001, 0, 0),
(8,'wireTransfer', 'WireTransfer', 5, 001, 1, 1);
(1, NULL, 'PayMethod one', 0, 001, 0, 0, 0),
(2, NULL, 'PayMethod two', 10, 001, 0, 0, 1),
(3, 'compensation', 'PayMethod three', 0, 001, 0, 0, 0),
(4, NULL, 'PayMethod with IBAN', 0, 001, 1, 0, 0),
(5, NULL, 'PayMethod five', 10, 001, 0, 0, 0),
(8,'wireTransfer', 'WireTransfer', 5, 001, 1, 1, 0);
INSERT INTO `vn`.`payDem`(`id`, `payDem`)
VALUES

View File

@ -524,7 +524,7 @@ export default {
saturdayButton: '.vn-popup.shown vn-tool-bar > vn-button:nth-child(6)',
acceptDialog: '.vn-dialog.shown button[response="accept"]',
acceptChangeHourButton: '.vn-dialog.shown button[response="accept"]',
descriptorDeliveryDate: 'vn-ticket-descriptor slot-body > .attributes > vn-label-value:nth-child(3) > section > span',
descriptorDeliveryDate: 'vn-ticket-descriptor slot-body > .attributes > vn-label-value:nth-child(4) > section > span',
acceptInvoiceOutButton: '.vn-confirm.shown button[response="accept"]',
acceptDeleteStowawayButton: '.vn-dialog.shown button[response="accept"]'
},
@ -558,6 +558,7 @@ export default {
moreMenuUnmarkReseved: 'vn-item[name="unreserve"]',
moreMenuUpdateDiscount: 'vn-item[name="discount"]',
moreMenuRecalculatePrice: 'vn-item[name="calculatePrice"]',
moreMenuPayBack: 'vn-item[name="payBack"]',
moreMenuUpdateDiscountInput: 'vn-input-number[ng-model="$ctrl.edit.discount"] input',
transferQuantityInput: '.vn-popover.shown vn-table > div > vn-tbody > vn-tr > vn-td-editable > span > text',
transferQuantityCell: '.vn-popover.shown vn-table > div > vn-tbody > vn-tr > vn-td-editable',

View File

@ -206,7 +206,16 @@ describe('Ticket Edit sale path', () => {
expect(message.text).toContain('Data saved!');
});
it('should select the third sale and create a pay back', async() => {
await page.waitToClick(selectors.ticketSales.firstSaleCheckbox);
await page.waitToClick(selectors.ticketSales.moreMenu);
await page.waitToClick(selectors.ticketSales.moreMenuPayBack);
await page.waitForState('ticket.card.sale');
});
it('should select the third sale and create a claim of it', async() => {
await page.accessToSearchResult('16');
await page.accessToSection('ticket.card.sale');
await page.waitToClick(selectors.ticketSales.thirdSaleCheckbox);
await page.waitToClick(selectors.ticketSales.moreMenu);
await page.waitToClick(selectors.ticketSales.moreMenuCreateClaim);

View File

@ -8,7 +8,7 @@ describe('Supplier basic data path', () => {
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('administrative', 'supplier');
await page.loginAndModule('financial', 'supplier');
await page.accessToSearchResult('1');
await page.accessToSection('supplier.card.basicData');
});

View File

@ -44,16 +44,10 @@ export default class SmartTable extends Component {
set model(value) {
this._model = value;
if (value)
if (value) {
this.$.model = value;
this.defaultOrder();
}
get viewConfigId() {
return this._viewConfigId;
}
set viewConfigId(value) {
this._viewConfigId = value;
}
getDefaultViewConfig() {
@ -163,6 +157,40 @@ export default class SmartTable extends Component {
}
}
defaultOrder() {
const order = this.model.order;
if (!order) return;
const orderFields = order.split(', ');
for (const fieldString of orderFields) {
const field = fieldString.split(' ');
const fieldName = field[0];
let sortType = 'ASC';
if (field.length === 2)
sortType = field[1];
const column = this.columns.find(column => column.field == fieldName);
if (column) {
this.sortCriteria.push({field: fieldName, sortType: sortType});
const isASC = sortType == 'ASC';
const isDESC = sortType == 'DESC';
if (isDESC) {
column.element.classList.remove('asc');
column.element.classList.add('desc');
}
if (isASC) {
column.element.classList.remove('desc');
column.element.classList.add('asc');
}
}
}
}
registerColumns() {
const header = this.element.querySelector('thead > tr');
if (!header) return;
@ -175,7 +203,7 @@ export default class SmartTable extends Component {
const columnElement = angular.element(column);
const caption = columnElement.text().trim();
this.columns.push({field, caption, index});
this.columns.push({field, caption, index, element: column});
column.addEventListener('click', () => this.orderHandler(column));
}

View File

@ -80,6 +80,45 @@ describe('Component smartTable', () => {
});
});
describe('defaultOrder', () => {
it('should insert a new object to the controller sortCriteria with a sortType value of "ASC"', () => {
const element = document.createElement('div');
controller.model = {order: 'id'};
controller.columns = [
{field: 'id', element: element},
{field: 'test1', element: element},
{field: 'test2', element: element}
];
controller.defaultOrder();
const firstSortCriteria = controller.sortCriteria[0];
expect(firstSortCriteria.field).toEqual('id');
expect(firstSortCriteria.sortType).toEqual('ASC');
});
it('should insert two new objects to the controller sortCriteria with a sortType values of "ASC" and "DESC"', () => {
const element = document.createElement('div');
controller.model = {order: 'test1, id DESC'};
controller.columns = [
{field: 'id', element: element},
{field: 'test1', element: element},
{field: 'test2', element: element}
];
controller.defaultOrder();
const firstSortCriteria = controller.sortCriteria[0];
const secondSortCriteria = controller.sortCriteria[1];
expect(firstSortCriteria.field).toEqual('test1');
expect(firstSortCriteria.sortType).toEqual('ASC');
expect(secondSortCriteria.field).toEqual('id');
expect(secondSortCriteria.sortType).toEqual('DESC');
});
});
describe('addFilter()', () => {
it('should call the model addFilter() with a basic where filter if exprBuilder() was not received', () => {
controller.model = {addFilter: jest.fn()};

View File

@ -214,5 +214,6 @@
"The type of business must be filled in basic data": "El tipo de negocio debe estar rellenado en datos básicos",
"You can't create a claim from a ticket delivered more than seven days ago": "No puedes crear una reclamación de un ticket entregado hace más de siete días",
"The worker has hours recorded that day": "El trabajador tiene horas fichadas ese día",
"The worker has a marked absence that day": "El trabajador tiene marcada una ausencia ese día"
"The worker has a marked absence that day": "El trabajador tiene marcada una ausencia ese día",
"You can not modify is pay method checked": "No se puede modificar el campo método de pago validado"
}

View File

@ -82,7 +82,7 @@ class Controller extends Dialog {
}
set amountToReturn(value) {
if (!value) return;
if (isNaN(value)) return;
value = value.toFixed(2);

View File

@ -1,6 +1,7 @@
<vn-crud-model
vn-id="model"
url="Buys/latestBuysFilter"
order="itemFk DESC"
limit="20"
data="$ctrl.buys"
auto-load="true">
@ -34,8 +35,8 @@
</vn-multi-check>
</th>
<th translate>Picture</th>
<th field="id">
<span translate>Identifier</span>
<th field="itemFk">
<span translate>Item ID</span>
</th>
<th field="packing" number>
<span translate>Packing</span>

View File

@ -112,7 +112,10 @@ module.exports = Self => {
case 'search':
return /^\d+$/.test(value)
? {or: [{'i.id': value}, codeWhere]}
: {or: [{'i.name': {like: `%${value}%`}}, codeWhere]};
: {or: [
{'i.name': {like: `%${value}%`}},
{'i.longName': {like: `%${value}%`}},
codeWhere]};
case 'id':
case 'isActive':
case 'typeFk':

View File

@ -2,7 +2,7 @@
vn-id="model"
url="SalesMonitors/salesFilter"
limit="20"
order="shippedDate DESC, theoreticalhour, id">
order="shipped DESC, theoreticalHour, id">
</vn-crud-model>
<vn-portal slot="topbar">
<vn-searchbar
@ -27,11 +27,13 @@
view-config-id="ticketsMonitor"
options="$ctrl.smartTableOptions"
expr-builder="$ctrl.exprBuilder(param, value)">
<slot-actions><vn-check
<slot-actions>
<vn-check
label="Auto-refresh"
vn-tooltip="Toggle auto-refresh every 2 minutes"
on-change="$ctrl.autoRefresh(value)">
</vn-check></slot-actions>
</vn-check>
</slot-actions>
<slot-table>
<table>
<thead>

View File

@ -29,7 +29,7 @@ vn-monitor-sales-tickets {
height: 736px
}
vn-tbody a[ng-repeat].vn-tr:focus {
tbody tr[ng-repeat]:focus {
background-color: $color-primary-light
}

View File

@ -8,6 +8,19 @@ describe('loopback model Supplier', () => {
beforeAll(async() => {
supplierOne = await models.Supplier.findById(1);
supplierTwo = await models.Supplier.findById(442);
const activeCtx = {
accessToken: {userId: 9},
http: {
req: {
headers: {origin: 'http://localhost'}
}
}
};
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
active: activeCtx
});
});
afterAll(async() => {
@ -32,19 +45,6 @@ describe('loopback model Supplier', () => {
});
it('should not throw if the payMethod id is valid', async() => {
const activeCtx = {
accessToken: {userId: 9},
http: {
req: {
headers: {origin: 'http://localhost'}
}
}
};
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
active: activeCtx
});
let error;
const supplier = await models.Supplier.findById(442);
await supplier.updateAttribute('payMethodFk', 4)
@ -54,5 +54,40 @@ describe('loopback model Supplier', () => {
expect(error).not.toBeDefined();
});
it('should have checked isPayMethodChecked for payMethod hasVerfified is false', async() => {
const supplier = await models.Supplier.findById(442);
await supplier.updateAttribute('isPayMethodChecked', true);
await supplier.updateAttribute('payMethodFk', 5);
const result = await models.Supplier.findById(442);
expect(result.isPayMethodChecked).toEqual(true);
});
it('should have unchecked isPayMethodChecked for payMethod hasVerfified is true', async() => {
const supplier = await models.Supplier.findById(442);
await supplier.updateAttribute('isPayMethodChecked', true);
await supplier.updateAttribute('payMethodFk', 2);
const result = await models.Supplier.findById(442);
expect(result.isPayMethodChecked).toEqual(false);
});
it('should have unchecked isPayMethodChecked for payDay and peyDemFk', async() => {
const supplier = await models.Supplier.findById(442);
await supplier.updateAttribute('isPayMethodChecked', true);
await supplier.updateAttribute('payDay', 5);
const firstResult = await models.Supplier.findById(442);
await supplier.updateAttribute('isPayMethodChecked', true);
await supplier.updateAttribute('payDemFk', 1);
const secondResult = await models.Supplier.findById(442);
expect(firstResult.isPayMethodChecked).toEqual(false);
expect(secondResult.isPayMethodChecked).toEqual(false);
});
});
});

View File

@ -7,12 +7,14 @@ module.exports = Self => {
});
async function ibanValidation(err, done) {
let filter = {
const supplier = await Self.app.models.Supplier.findById(this.supplierFk);
const filter = {
fields: ['code'],
where: {id: this.countryFk}
where: {id: supplier.countryFk}
};
let country = await Self.app.models.Country.findOne(filter);
let code = country ? country.code.toLowerCase() : null;
const country = await Self.app.models.Country.findOne(filter);
const code = country ? country.code.toLowerCase() : null;
if (code != 'es')
return done();

View File

@ -1,5 +1,6 @@
const UserError = require('vn-loopback/util/user-error');
const validateTin = require('vn-loopback/util/validateTin');
const LoopBackContext = require('loopback-context');
module.exports = Self => {
require('../methods/supplier/filter')(Self);
@ -88,8 +89,24 @@ module.exports = Self => {
}
Self.observe('before save', async function(ctx) {
let changes = ctx.data || ctx.instance;
let orgData = ctx.currentInstance;
const loopbackContext = LoopBackContext.getCurrentContext();
const changes = ctx.data || ctx.instance;
const orgData = ctx.currentInstance;
const userId = loopbackContext.active.accessToken.userId;
const isNotFinancial = !await Self.app.models.Account.hasRole(userId, 'financial');
const isPayMethodChecked = changes.isPayMethodChecked || orgData.isPayMethodChecked;
const hasChanges = orgData && changes;
const isPayMethodCheckedChanged = hasChanges
&& orgData.isPayMethodChecked != isPayMethodChecked;
if (isNotFinancial && isPayMethodCheckedChanged)
throw new UserError('You can not modify is pay method checked');
});
Self.observe('before save', async function(ctx) {
const changes = ctx.data || ctx.instance;
const orgData = ctx.currentInstance;
const socialName = changes.name || orgData.name;
const hasChanges = orgData && changes;

View File

@ -38,7 +38,8 @@
</vn-check>
<vn-check
label="PayMethodChecked"
ng-model="$ctrl.supplier.isPayMethodChecked">
ng-model="$ctrl.supplier.isPayMethodChecked"
vn-acl="financial">
</vn-check>
</vn-horizontal>
<vn-horizontal>

View File

@ -11,7 +11,8 @@ export default class Controller extends Section {
}
onSubmit() {
return this.$.watcher.submit();
this.$.watcher.submit()
.then(() => this.card.reload());
}
}
@ -20,5 +21,8 @@ ngModule.vnComponent('vnSupplierBillingData', {
controller: Controller,
bindings: {
supplier: '<'
},
require: {
card: '^vnSupplierCard'
}
});

View File

@ -0,0 +1,70 @@
module.exports = Self => {
Self.remoteMethodCtx('payBack', {
description: 'Create ticket with the selected lines changing the sign to the quantites',
accessType: 'WRITE',
accepts: [{
arg: 'sales',
description: 'The sales',
type: ['object'],
required: true
},
{
arg: 'ticketId',
type: 'number',
required: true,
description: 'The ticket id'
}],
returns: {
type: 'number',
root: true
},
http: {
path: `/payBack`,
verb: 'post'
}
});
Self.payBack = async(ctx, sales, ticketId, options) => {
const myOptions = {};
let tx;
if (typeof options == 'object')
Object.assign(myOptions, options);
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
try {
const salesIds = [];
const params = [];
sales.forEach(sale => {
salesIds.push(sale.id);
params.push('?');
});
const paramsString = params.join();
const query = `
DROP TEMPORARY TABLE IF EXISTS tmp.sale;
CREATE TEMPORARY TABLE tmp.sale
SELECT s.id, s.itemFk, - s.quantity, s.concept, s.price, s.discount
FROM sale s
WHERE s.id IN (${paramsString});
CALL vn.ticket_doRefund(${ticketId}, @newTicket);
DROP TEMPORARY TABLE tmp.sale;`;
await Self.rawSql(query, salesIds, myOptions);
const [newTicket] = await Self.rawSql('SELECT @newTicket id', null, myOptions);
ticketId = newTicket.id;
if (tx) await tx.commit();
return ticketId;
} catch (e) {
if (tx) await tx.rollback();
throw e;
}
};
};

View File

@ -0,0 +1,26 @@
const models = require('vn-loopback/server/server').models;
describe('sale payBack()', () => {
it('should create ticket with the selected lines changing the sign to the quantites', async() => {
const tx = await models.Sale.beginTransaction({});
const ticketId = 11;
const sales = [
{id: 7, ticketFk: 11},
{id: 8, ticketFk: 11}
];
try {
const options = {transaction: tx};
const ctx = {req: {accessToken: {userId: 9}}};
const response = await models.Sale.payBack(ctx, sales, ticketId, options);
const [newTicketId] = await models.Sale.rawSql('SELECT MAX(t.id) id FROM vn.ticket t;', null, options);
expect(response).toEqual(newTicketId.id);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
});

View File

@ -83,6 +83,11 @@ module.exports = Self => {
type: 'boolean',
description: `Whether to show only tickets with problems`
},
{
arg: 'hasRoute',
type: 'boolean',
description: `Whether to show only tickets with route`
},
{
arg: 'pending',
type: 'boolean',
@ -188,6 +193,10 @@ module.exports = Self => {
case 'alertLevel':
return {'ts.alertLevel': value};
case 'hasRoute':
if (value == true)
return {'t.routeFk': {neq: null}};
return {'t.routeFk': null};
case 'pending':
if (value) {
return {and: [
@ -266,7 +275,8 @@ module.exports = Self => {
LEFT JOIN state st ON st.id = ts.stateFk
LEFT JOIN client c ON c.id = t.clientFk
LEFT JOIN worker wk ON wk.id = c.salesPersonFk
LEFT JOIN account.user u ON u.id = wk.userFk`);
LEFT JOIN account.user u ON u.id = wk.userFk
LEFT JOIN route r ON r.id = t.routeFk`);
if (args.orderFk) {
stmt.merge({

View File

@ -221,4 +221,61 @@ describe('ticket filter()', () => {
throw e;
}
});
it('should return the tickets matching the route on true', async() => {
const tx = await models.Ticket.beginTransaction({});
try {
const options = {transaction: tx};
const ctx = {req: {accessToken: {userId: 9}}, args: {hasRoute: true}};
const filter = {};
const result = await models.Ticket.filter(ctx, filter, options);
expect(result.length).toEqual(22);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
it('should return the tickets matching the route on false', async() => {
const tx = await models.Ticket.beginTransaction({});
try {
const options = {transaction: tx};
const ctx = {req: {accessToken: {userId: 9}}, args: {hasRoute: false}};
const filter = {};
const result = await models.Ticket.filter(ctx, filter, options);
expect(result.length).toEqual(5);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
it('should return the tickets matching the route on null', async() => {
const tx = await models.Ticket.beginTransaction({});
try {
const options = {transaction: tx};
const ctx = {req: {accessToken: {userId: 9}}, args: {hasRoute: null}};
const filter = {};
const result = await models.Ticket.filter(ctx, filter, options);
expect(result.length).toEqual(27);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
});

View File

@ -6,6 +6,7 @@ module.exports = Self => {
require('../methods/sale/updateQuantity')(Self);
require('../methods/sale/updateConcept')(Self);
require('../methods/sale/recalculatePrice')(Self);
require('../methods/sale/payBack')(Self);
require('../methods/sale/canEdit')(Self);
Self.validatesPresenceOf('concept', {

View File

@ -490,4 +490,9 @@
ng-if="$ctrl.isEditable && $ctrl.hasReserves()">
Unmark as reserved
</vn-item>
<vn-item translate
name="payBack"
ng-click="$ctrl.createPayBack()">
Pay Back
</vn-item>
</vn-menu>

View File

@ -460,6 +460,18 @@ class Controller extends Section {
});
}
createPayBack() {
const sales = this.selectedValidSales();
if (!sales) return;
const params = {sales: sales, ticketId: this.ticket.id};
const query = `Sales/payBack`;
this.resetChanges();
this.$http.post(query, params).then(res => {
this.$state.go('ticket.card.sale', {id: res.data});
});
}
itemSearchFunc($search) {
return /^\d+$/.test($search)
? {id: $search}

View File

@ -701,6 +701,22 @@ describe('Ticket', () => {
});
});
describe('createPayBack()', () => {
it('should make an HTTP POST query and then call to the $state go() method', () => {
jest.spyOn(controller, 'selectedValidSales').mockReturnValue(controller.sales);
jest.spyOn(controller, 'resetChanges');
jest.spyOn(controller.$state, 'go');
const expectedId = 9999;
$httpBackend.expect('POST', `Sales/payBack`).respond(200, expectedId);
controller.createPayBack();
$httpBackend.flush();
expect(controller.resetChanges).toHaveBeenCalledWith();
expect(controller.$state.go).toHaveBeenCalledWith('ticket.card.sale', {id: expectedId});
});
});
describe('itemSearchFunc()', () => {
it('should return the filter by id property for an input of a number', () => {
const itemId = 1;

View File

@ -36,3 +36,4 @@ Warehouse: Almacen
Agency: Agencia
Shipped: F. envio
Packaging: Encajado
Pay Back: Abono

View File

@ -135,6 +135,12 @@
ng-model="filter.pending"
triple-state="true">
</vn-check>
<vn-check
vn-one
label="Has route"
ng-model="filter.hasRoute"
triple-state="true">
</vn-check>
</vn-horizontal>
<vn-horizontal class="vn-px-lg vn-pb-lg vn-mt-lg">
<vn-submit label="Search"></vn-submit>

View File

@ -12,6 +12,7 @@ Order id: Id cesta
Grouped States: Estado agrupado
Days onward: Días adelante
With problems: Con problemas
Has route: Con ruta
Pending: Pendiente
FREE: Libre
DELIVERED: Servido

View File

@ -23,6 +23,10 @@
<td class="font gray uppercase">{{$t('clientId')}}</td>
<th>{{client.id}}</th>
</tr>
<tr>
<td class="font gray uppercase">{{$t('phone')}}</td>
<th>{{client.phone}}</th>
</tr>
<tr>
<td class="font gray uppercase">{{$t('date')}}</td>
<th>{{dated}}</th>

View File

@ -9,6 +9,7 @@ reference: Referencia
concept: Concepto
clientSignature: Firma del cliente
claim: Reclamación {0}
phone: Teléfono
sections:
agency:
description: 'Para agilizar su recogida, por favor, póngase en contacto con la oficina

View File

@ -8,7 +8,8 @@ SELECT
a.street,
a.nickname,
p.name AS province,
ct.country
ct.country,
IFNULL(c.phone, cc.phone) AS phone
FROM claim cl
JOIN client c ON c.id = cl.clientFk
JOIN account.user u ON u.id = c.id
@ -17,4 +18,6 @@ FROM claim cl
LEFT JOIN province p ON p.id = a.provinceFk
LEFT JOIN autonomy amy ON amy.id = p.autonomyFk
LEFT JOIN country ct ON ct.id = amy.countryFk
LEFT JOIN clientContact cc ON cc.clientFk = c.id
WHERE cl.id = ?
LIMIT 1;