Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix into 2575-image_download
gitea/salix/pipeline/head There was a failure building this commit
Details
gitea/salix/pipeline/head There was a failure building this commit
Details
This commit is contained in:
commit
e4e4f37609
|
@ -2,15 +2,11 @@ const app = require('vn-loopback/server/server');
|
|||
|
||||
describe('campaign upcoming()', () => {
|
||||
it('should return the upcoming campaign but from the last year', async() => {
|
||||
let response = await app.models.Campaign.upcoming();
|
||||
|
||||
const lastYearDate = new Date();
|
||||
lastYearDate.setFullYear(lastYearDate.getFullYear() - 1);
|
||||
const lastYear = lastYearDate.getFullYear();
|
||||
|
||||
const response = await app.models.Campaign.upcoming();
|
||||
const campaignDated = response.dated;
|
||||
const campaignYear = campaignDated.getFullYear();
|
||||
const now = new Date();
|
||||
|
||||
expect(campaignYear).toEqual(lastYear);
|
||||
expect(campaignDated).toEqual(jasmine.any(Date));
|
||||
expect(campaignDated).toBeLessThanOrEqual(now);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,13 +1,62 @@
|
|||
const app = require('vn-loopback/server/server');
|
||||
|
||||
// #2495 updateCollectionSale reparar polución de unitarios
|
||||
xdescribe('updateCollectionSale()', () => {
|
||||
it('return a new collection', async() => {
|
||||
describe('updateCollectionSale()', () => {
|
||||
it('should return a new collection', async() => {
|
||||
const sectorOneWarehouseID = 1;
|
||||
let ctx = {req: {accessToken: {userId: 106}}};
|
||||
let response = await app.models.Collection.updateCollectionSale(ctx, 1, 5, 5, 5, 1, 4, false, 'UXN', 1, 1);
|
||||
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();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -65,25 +65,26 @@ module.exports = Self => {
|
|||
}
|
||||
});
|
||||
|
||||
Self.updateCollectionSale = async(ctx, sale, originalQuantity, quantity, quantityPicked, ticketFk, stateFk, isNicho, shelvingFk, itemFk, sectorFk) => {
|
||||
Self.updateCollectionSale = async ctx => {
|
||||
const userId = ctx.req.accessToken.userId;
|
||||
const args = ctx.args;
|
||||
|
||||
if (originalQuantity == quantity) {
|
||||
if (args.originalQuantity == args.quantity) {
|
||||
query = `CALL vn.collection_updateSale(?,?,?,?,?)`;
|
||||
await Self.rawSql(query, [sale, originalQuantity, userId, stateFk, ticketFk]);
|
||||
await Self.rawSql(query, [args.sale, args.originalQuantity, userId, args.stateFk, args.ticketFk]);
|
||||
}
|
||||
|
||||
if (!isNicho) {
|
||||
if (!args.isNicho) {
|
||||
query = `CALL vn.collection_faults(?,?,?)`;
|
||||
await Self.rawSql(query, [shelvingFk, quantityPicked, itemFk]);
|
||||
await Self.rawSql(query, [args.shelvingFk, args.quantityPicked, args.itemFk]);
|
||||
} else {
|
||||
query = `CALL vn.sector_getWarehouse(?)`;
|
||||
const [result] = await Self.rawSql(query, [sectorFk]);
|
||||
const [result] = await Self.rawSql(query, [args.sectorFk]);
|
||||
|
||||
query = `CALL vn.itemPlacementSave(?,?,?)`;
|
||||
await Self.rawSql(query, [shelvingFk, quantityPicked, result[0]['warehouseFk']]);
|
||||
await Self.rawSql(query, [args.shelvingFk, args.quantityPicked, result[0]['warehouseFk']]);
|
||||
}
|
||||
query = `CALL vn.sale_updateOriginalQuantity(?,?)`;
|
||||
return await Self.rawSql(query, [sale, quantity]);
|
||||
return await Self.rawSql(query, [args.sale, args.quantity]);
|
||||
};
|
||||
};
|
||||
|
|
|
@ -56,6 +56,9 @@
|
|||
"Sip": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
"SageWithholding": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
"UserConfigView": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"name": "SageWithholding",
|
||||
"base": "VnModel",
|
||||
"options": {
|
||||
"mysql": {
|
||||
"table": "sage.TiposRetencion"
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "Number",
|
||||
"id": true,
|
||||
"description": "Identifier",
|
||||
"mysql": {
|
||||
"columnName": "CodigoRetencion"
|
||||
}
|
||||
},
|
||||
"withholding": {
|
||||
"type": "string",
|
||||
"mysql": {
|
||||
"columnName": "Retencion"
|
||||
}
|
||||
}
|
||||
},
|
||||
"acls": [
|
||||
{
|
||||
"accessType": "READ",
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$everyone",
|
||||
"permission": "ALLOW"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES ('Supplier', '*', 'READ', 'ALLOW', 'ROLE', 'employee');
|
||||
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES ('Supplier', '*', 'WRITE', 'ALLOW', 'ROLE', 'administrative');
|
||||
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES ('SupplierLog', '*', 'READ', 'ALLOW', 'ROLE', 'employee');
|
||||
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES ('SupplierContact', '*', 'WRITE', 'ALLOW', 'ROLE', 'administrative');
|
|
@ -0,0 +1,43 @@
|
|||
DROP PROCEDURE IF EXISTS `vn`.`timeControl_calculate`;
|
||||
DELIMITER $$
|
||||
CREATE DEFINER=`root`@`%` PROCEDURE `vn`.`timeControl_calculate`(vDatedFrom DATETIME, vDatedTo DATETIME)
|
||||
BEGIN
|
||||
SET @vIsOdd := TRUE;
|
||||
SET @vUser := NULL;
|
||||
SET @vDated := NULL;
|
||||
|
||||
DROP TEMPORARY TABLE IF EXISTS tmp.timeControlCalculate;
|
||||
|
||||
CREATE TEMPORARY TABLE tmp.timeControlCalculate
|
||||
SELECT
|
||||
userFk,
|
||||
dated,
|
||||
IF( timeWork >= 18000, @timeWork:=timeWork + 1200, @timeWork:=timeWork) timeWorkSeconds,
|
||||
SEC_TO_TIME(@timeWork ) timeWorkSexagesimal,
|
||||
@timeWork / 3600 timeWorkDecimal,
|
||||
timed
|
||||
FROM (SELECT SUM(timeWork) timeWork,
|
||||
userFk,
|
||||
dated,
|
||||
GROUP_CONCAT(DATE_FORMAT(sub.timed,"%H:%i") ORDER BY sub.timed ASC SEPARATOR ' - ') timed
|
||||
FROM (SELECT IF(@vUser = wtc.userFk, @vUser :=@vUser, @vUser := wtc.userFk),
|
||||
IF(@vIsOdd, @vIsOdd := FALSE, @vIsOdd := TRUE),
|
||||
IF(direction='in', @vIsOdd := TRUE, @vIsOdd := @vIsOdd),
|
||||
IF(@vIsOdd, @vLastTimed:=UNIX_TIMESTAMP(timed),@vLastTimed:=@vLastTimed),
|
||||
IF(@vIsOdd, 0, UNIX_TIMESTAMP(timed)-@vLastTimed) timeWork,
|
||||
IF(direction='in', @vDated := DATE(wtc.timed), @vDated :=@vDated) dated,
|
||||
wtc.timed timed,
|
||||
wtc.userFk,
|
||||
direction
|
||||
FROM (SELECT DISTINCT(wtc.id), wtc.userFk, wtc.timed, wtc.direction
|
||||
FROM workerTimeControl wtc
|
||||
JOIN tmp.`user` w ON w.userFk = wtc.userFk
|
||||
WHERE wtc.timed BETWEEN vDatedFrom AND vDatedTo
|
||||
ORDER BY userFk, timed ASC
|
||||
) wtc
|
||||
WHERE wtc.timed BETWEEN vDatedFrom AND vDatedTo
|
||||
) sub
|
||||
GROUP BY userFk, dated
|
||||
)sub2;
|
||||
END$$
|
||||
DELIMITER ;
|
|
@ -1,2 +0,0 @@
|
|||
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES ('Supplier', '*', 'WRITE', 'ALLOW', 'ROLE', 'administrative');
|
||||
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES ('SupplierContact', '*', 'WRITE', 'ALLOW', 'ROLE', 'administrative');
|
|
@ -604,6 +604,24 @@ INSERT INTO `TiposTransacciones` VALUES (1,'Rég.general/Oper.interiores bienes
|
|||
UNLOCK TABLES;
|
||||
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `TiposRetencion`
|
||||
--
|
||||
|
||||
LOCK TABLES `TiposRetencion` WRITE;
|
||||
/*!40000 ALTER TABLE `TiposRetencion` DISABLE KEYS */;
|
||||
INSERT INTO `TiposRetencion` (`CodigoRetencion`, `Retencion`, `PorcentajeRetencion`, `CuentaCargo`, `CuentaAbono`, `ClaveIrpf`, `CuentaCargoANT_`, `CuentaAbonoANT_`, `IdTipoRetencion`) VALUES
|
||||
(1, 'RETENCION ESTIMACION OBJETIVA', '1.0000000000', '4730000000', '4751000000', NULL, NULL, NULL, '03811652-0F3A-44A1-AE1C-B19624525D7F'),
|
||||
(2, 'ACTIVIDADES AGRICOLAS O GANADERAS', '2.0000000000', '4730000000', '4751000000', NULL, NULL, NULL, 'F3F91EF3-FED6-444D-B03C-75B639D13FB4'),
|
||||
(9, 'ACTIVIDADES PROFESIONALES 2 PRIMEROS AÑOS', '9.0000000000', '4730000000', '4751000000', NULL, NULL, NULL, '73F95642-E951-4C91-970A-60C503A4792B'),
|
||||
(15, 'ACTIVIDADES PROFESIONALES', '15.0000000000', '4730000000', '4751000000', '6', NULL, NULL, 'F6BDE0EE-3B01-4023-8FFF-A73AE9AC50D7'),
|
||||
(19, 'ARRENDAMIENTO Y SUBARRENDAMIENTO', '19.0000000000', '4730000000', '4751000000', '8', NULL, NULL, '09B033AE-16E5-4057-8D4A-A7710C8A4FB9');
|
||||
/*!40000 ALTER TABLE `TiposRetencion` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
|
||||
|
||||
|
||||
|
||||
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
|
||||
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
|
||||
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
|
||||
|
|
|
@ -1211,11 +1211,11 @@ INSERT INTO `vn`.`annualAverageInvoiced`(`clientFk`, `invoiced`)
|
|||
(104, 500),
|
||||
(105, 5000);
|
||||
|
||||
INSERT INTO `vn`.`supplier`(`id`, `name`, `nickname`,`account`,`countryFk`,`nif`,`isFarmer`,`commission`, `created`, `isActive`, `street`, `city`, `provinceFk`, `postCode`, `payMethodFk`, `payDemFk`, `payDay`, `taxTypeSageFk`, `transactionTypeSageFk`)
|
||||
INSERT INTO `vn`.`supplier`(`id`, `name`, `nickname`,`account`,`countryFk`,`nif`,`isFarmer`,`commission`, `created`, `isActive`, `street`, `city`, `provinceFk`, `postCode`, `payMethodFk`, `payDemFk`, `payDay`, `taxTypeSageFk`, `withholdingSageFk`, `transactionTypeSageFk`)
|
||||
VALUES
|
||||
(1, 'Plants SL', 'Plants nick', 4100000001, 1, '06089160W', 0, 0, CURDATE(), 1, 'supplier address 1', 'PONTEVEDRA', 1, 15214, 1, 1, 15, NULL, NULL),
|
||||
(2, 'Farmer King', 'The farmer', 4000020002, 1, 'B22222222', 1, 0, CURDATE(), 1, 'supplier address 2', 'SILLA', 2, 43022, 1, 2, 10, 93, 8),
|
||||
(442, 'Verdnatura Levante SL', 'Verdnatura', 5115000442, 1, 'C33333333', 0, 0, CURDATE(), 1, 'supplier address 3', 'SILLA', 1, 43022, 1, 2, 15, NULL, NULL);
|
||||
(1, 'Plants SL', 'Plants nick', 4100000001, 1, '06089160W', 0, 0, CURDATE(), 1, 'supplier address 1', 'PONTEVEDRA', 1, 15214, 1, 1, 15, 4, 1, 1),
|
||||
(2, 'Farmer King', 'The farmer', 4000020002, 1, '87945234L', 1, 0, CURDATE(), 1, 'supplier address 2', 'SILLA', 2, 43022, 1, 2, 10, 93, 2, 8),
|
||||
(442, 'Verdnatura Levante SL', 'Verdnatura', 5115000442, 1, '06815934E', 0, 0, CURDATE(), 1, 'supplier address 3', 'SILLA', 1, 43022, 1, 2, 15, 6, 9, 3);
|
||||
|
||||
INSERT INTO `vn`.`supplierContact`(`id`, `supplierFk`, `phone`, `mobile`, `email`, `observation`, `name`)
|
||||
VALUES
|
||||
|
|
|
@ -123,15 +123,20 @@ let actions = {
|
|||
},
|
||||
|
||||
waitForState: async function(state) {
|
||||
await this.waitFor(state => {
|
||||
await this.waitForFunction(state => {
|
||||
let $state = angular.element(document.body).injector().get('$state');
|
||||
return !$state.transition && $state.is(state);
|
||||
}, {}, state);
|
||||
await this.waitForFunction(() => {
|
||||
return angular.element(() => {
|
||||
return true;
|
||||
});
|
||||
});
|
||||
await this.waitForSpinnerLoad();
|
||||
},
|
||||
|
||||
waitForTransition: async function() {
|
||||
await this.waitFor(() => {
|
||||
await this.waitForFunction(() => {
|
||||
const $state = angular.element(document.body).injector().get('$state');
|
||||
return !$state.transition;
|
||||
});
|
||||
|
@ -522,7 +527,7 @@ let actions = {
|
|||
},
|
||||
|
||||
waitForSpinnerLoad: async function() {
|
||||
await this.waitFor('vn-topbar vn-spinner', {hidden: true});
|
||||
await this.waitForSelector('vn-topbar vn-spinner', {hidden: true});
|
||||
},
|
||||
|
||||
waitForWatcherData: async function(selector) {
|
||||
|
|
|
@ -923,5 +923,24 @@ export default {
|
|||
thirdContactNotes: 'vn-supplier-contact div:nth-child(3) vn-textfield[ng-model="contact.observation"]',
|
||||
saveButton: 'vn-supplier-contact button[type="submit"]',
|
||||
thirdContactDeleteButton: 'vn-supplier-contact div:nth-child(3) vn-icon-button[icon="delete"]'
|
||||
},
|
||||
supplierBasicData: {
|
||||
alias: 'vn-supplier-basic-data vn-textfield[ng-model="$ctrl.supplier.nickname"]',
|
||||
isOfficial: 'vn-supplier-basic-data vn-check[ng-model="$ctrl.supplier.isOfficial"]',
|
||||
isActive: 'vn-supplier-basic-data vn-check[ng-model="$ctrl.supplier.isActive"]',
|
||||
notes: 'vn-supplier-basic-data vn-textarea[ng-model="$ctrl.supplier.note"]',
|
||||
saveButton: 'vn-supplier-basic-data button[type="submit"]',
|
||||
},
|
||||
supplierFiscalData: {
|
||||
socialName: 'vn-supplier-fiscal-data vn-textfield[ng-model="$ctrl.supplier.name"]',
|
||||
taxNumber: 'vn-supplier-fiscal-data vn-textfield[ng-model="$ctrl.supplier.nif"]',
|
||||
account: 'vn-supplier-fiscal-data vn-textfield[ng-model="$ctrl.supplier.account"]',
|
||||
sageTaxType: 'vn-supplier-fiscal-data vn-autocomplete[ng-model="$ctrl.supplier.sageTaxTypeFk"]',
|
||||
sageWihholding: 'vn-supplier-fiscal-data vn-autocomplete[ng-model="$ctrl.supplier.sageWithholdingFk"]',
|
||||
postCode: 'vn-supplier-fiscal-data vn-datalist[ng-model="$ctrl.supplier.postCode"]',
|
||||
city: 'vn-supplier-fiscal-data vn-datalist[ng-model="$ctrl.supplier.city"]',
|
||||
province: 'vn-supplier-fiscal-data vn-autocomplete[ng-model="$ctrl.supplier.provinceFk"]',
|
||||
country: 'vn-supplier-fiscal-data vn-autocomplete[ng-model="$ctrl.supplier.countryFk"]',
|
||||
saveButton: 'vn-supplier-fiscal-data button[type="submit"]',
|
||||
}
|
||||
};
|
||||
|
|
|
@ -57,6 +57,8 @@ describe('Client lock verified data path', () => {
|
|||
});
|
||||
|
||||
it('should check the Verified data checkbox', async() => {
|
||||
await page.autocompleteSearch(selectors.clientFiscalData.sageTax, 'operaciones no sujetas');
|
||||
await page.autocompleteSearch(selectors.clientFiscalData.sageTransaction, 'regularización de inversiones');
|
||||
await page.waitToClick(selectors.clientFiscalData.verifiedDataCheckbox);
|
||||
await page.waitToClick(selectors.clientFiscalData.saveButton);
|
||||
await page.waitToClick(selectors.globalItems.acceptButton);
|
||||
|
|
|
@ -48,7 +48,7 @@ describe('Ticket Create new tracking state path', () => {
|
|||
});
|
||||
|
||||
it('should now access to the create state view by clicking the create floating button', async() => {
|
||||
await page.waitFor('.vn-popup', {hidden: true}),
|
||||
await page.waitFor('.vn-popup', {hidden: true});
|
||||
await page.waitToClick(selectors.ticketTracking.createStateButton);
|
||||
await page.waitForState('ticket.card.tracking.edit');
|
||||
});
|
||||
|
|
|
@ -103,8 +103,7 @@ describe('Ticket descriptor path', () => {
|
|||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
xit(`should check the state of the stowaway ticket is embarked`, async() => {
|
||||
await page.wait(500);
|
||||
it(`should check the state of the stowaway ticket is embarked`, async() => {
|
||||
const state = await page.waitToGetProperty(selectors.ticketDescriptor.stateLabelValue, 'innerText');
|
||||
|
||||
expect(state).toEqual('State Embarcando');
|
||||
|
|
|
@ -79,6 +79,6 @@ describe('Supplier summary & descriptor path', () => {
|
|||
});
|
||||
|
||||
it(`should check the client button isn't present since this supplier should not be a client`, async() => {
|
||||
await page.waitForSelector(selectors.supplierDescriptor.clientButton, {hidden: true});
|
||||
await page.waitForSelector(selectors.supplierDescriptor.clientButton, {visible: false});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
import selectors from '../../helpers/selectors.js';
|
||||
import getBrowser from '../../helpers/puppeteer';
|
||||
|
||||
describe('Supplier basic data path', () => {
|
||||
let browser;
|
||||
let page;
|
||||
|
||||
beforeAll(async() => {
|
||||
browser = await getBrowser();
|
||||
page = browser.page;
|
||||
await page.loginAndModule('administrative', 'supplier');
|
||||
await page.accessToSearchResult('1');
|
||||
await page.accessToSection('supplier.card.basicData');
|
||||
});
|
||||
|
||||
afterAll(async() => {
|
||||
await browser.close();
|
||||
});
|
||||
|
||||
it('should edit the basic data', async() => {
|
||||
await page.clearInput(selectors.supplierBasicData.alias);
|
||||
await page.write(selectors.supplierBasicData.alias, 'Plants Nick SL');
|
||||
await page.waitToClick(selectors.supplierBasicData.isOfficial);
|
||||
await page.waitToClick(selectors.supplierBasicData.isActive);
|
||||
await page.write(selectors.supplierBasicData.notes, 'Some notes');
|
||||
|
||||
await page.waitToClick(selectors.supplierBasicData.saveButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should reload the section', async() => {
|
||||
await page.reloadSection('supplier.card.basicData');
|
||||
});
|
||||
|
||||
it('should check the alias was edited', async() => {
|
||||
const result = await page.waitToGetProperty(selectors.supplierBasicData.alias, 'value');
|
||||
|
||||
expect(result).toEqual('Plants Nick SL');
|
||||
});
|
||||
|
||||
it('should check the isOffical checkbox is now unchecked', async() => {
|
||||
const result = await page.checkboxState(selectors.supplierBasicData.isOfficial);
|
||||
|
||||
expect(result).toBe('unchecked');
|
||||
});
|
||||
|
||||
it('should check the isActive checkbox is now unchecked', async() => {
|
||||
const result = await page.checkboxState(selectors.supplierBasicData.isActive);
|
||||
|
||||
expect(result).toBe('unchecked');
|
||||
});
|
||||
|
||||
it('should check the notes were edited', async() => {
|
||||
const result = await page.waitToGetProperty(selectors.supplierBasicData.notes, 'value');
|
||||
|
||||
expect(result).toEqual('Some notes');
|
||||
});
|
||||
|
||||
it('should navigate to the log section', async() => {
|
||||
await page.accessToSection('supplier.card.log');
|
||||
});
|
||||
|
||||
it('should check the changes have been recorded', async() => {
|
||||
const result = await page.waitToGetProperty('#newInstance:nth-child(3)', 'innerText');
|
||||
|
||||
expect(result).toEqual('note: Some notes');
|
||||
});
|
||||
});
|
|
@ -0,0 +1,108 @@
|
|||
import selectors from '../../helpers/selectors.js';
|
||||
import getBrowser from '../../helpers/puppeteer';
|
||||
|
||||
describe('Supplier fiscal data path', () => {
|
||||
let browser;
|
||||
let page;
|
||||
|
||||
beforeAll(async() => {
|
||||
browser = await getBrowser();
|
||||
page = browser.page;
|
||||
await page.loginAndModule('administrative', 'supplier');
|
||||
await page.accessToSearchResult('2');
|
||||
await page.accessToSection('supplier.card.fiscalData');
|
||||
});
|
||||
|
||||
afterAll(async() => {
|
||||
await browser.close();
|
||||
});
|
||||
|
||||
it('should attempt to edit the fiscal data but fail as the tax number is invalid', async() => {
|
||||
await page.clearInput(selectors.supplierFiscalData.city);
|
||||
await page.clearInput(selectors.supplierFiscalData.province);
|
||||
await page.clearInput(selectors.supplierFiscalData.country);
|
||||
await page.clearInput(selectors.supplierFiscalData.postCode);
|
||||
await page.write(selectors.supplierFiscalData.city, 'Valencia');
|
||||
await page.clearInput(selectors.supplierFiscalData.socialName);
|
||||
await page.write(selectors.supplierFiscalData.socialName, 'Farmer King SL');
|
||||
await page.clearInput(selectors.supplierFiscalData.taxNumber);
|
||||
await page.write(selectors.supplierFiscalData.taxNumber, 'invalid tax number');
|
||||
await page.clearInput(selectors.supplierFiscalData.account);
|
||||
await page.write(selectors.supplierFiscalData.account, 'edited account number');
|
||||
await page.autocompleteSearch(selectors.supplierFiscalData.sageWihholding, 'retencion estimacion objetiva');
|
||||
await page.autocompleteSearch(selectors.supplierFiscalData.sageTaxType, 'operaciones no sujetas');
|
||||
|
||||
await page.waitToClick(selectors.supplierFiscalData.saveButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.text).toBe('Invalid Tax number');
|
||||
});
|
||||
|
||||
it('should save the changes as the tax number is valid this time', async() => {
|
||||
await page.clearInput(selectors.supplierFiscalData.taxNumber);
|
||||
await page.write(selectors.supplierFiscalData.taxNumber, '12345678Z');
|
||||
|
||||
await page.waitToClick(selectors.supplierFiscalData.saveButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should reload the section', async() => {
|
||||
await page.reloadSection('supplier.card.fiscalData');
|
||||
});
|
||||
|
||||
it('should check the socialName was edited', async() => {
|
||||
const result = await page.waitToGetProperty(selectors.supplierFiscalData.socialName, 'value');
|
||||
|
||||
expect(result).toEqual('Farmer King SL');
|
||||
});
|
||||
|
||||
it('should check the taxNumber was edited', async() => {
|
||||
const result = await page.waitToGetProperty(selectors.supplierFiscalData.taxNumber, 'value');
|
||||
|
||||
expect(result).toEqual('12345678Z');
|
||||
});
|
||||
|
||||
it('should check the account was edited', async() => {
|
||||
const result = await page.waitToGetProperty(selectors.supplierFiscalData.account, 'value');
|
||||
|
||||
expect(result).toEqual('edited account number');
|
||||
});
|
||||
|
||||
it('should check the sageWihholding was edited', async() => {
|
||||
const result = await page.waitToGetProperty(selectors.supplierFiscalData.sageWihholding, 'value');
|
||||
|
||||
expect(result).toEqual('RETENCION ESTIMACION OBJETIVA');
|
||||
});
|
||||
|
||||
it('should check the sageTaxType was edited', async() => {
|
||||
const result = await page.waitToGetProperty(selectors.supplierFiscalData.sageTaxType, 'value');
|
||||
|
||||
expect(result).toEqual('Operaciones no sujetas');
|
||||
});
|
||||
|
||||
it('should check the postCode was edited', async() => {
|
||||
const result = await page.waitToGetProperty(selectors.supplierFiscalData.postCode, 'value');
|
||||
|
||||
expect(result).toEqual('46000');
|
||||
});
|
||||
|
||||
it('should check the city was edited', async() => {
|
||||
const result = await page.waitToGetProperty(selectors.supplierFiscalData.city, 'value');
|
||||
|
||||
expect(result).toEqual('Valencia');
|
||||
});
|
||||
|
||||
it('should check the province was edited', async() => {
|
||||
const result = await page.waitToGetProperty(selectors.supplierFiscalData.province, 'value');
|
||||
|
||||
expect(result).toEqual('Province one (España)');
|
||||
});
|
||||
|
||||
it('should check the country was edited', async() => {
|
||||
const result = await page.waitToGetProperty(selectors.supplierFiscalData.country, 'value');
|
||||
|
||||
expect(result).toEqual('España');
|
||||
});
|
||||
});
|
|
@ -69,6 +69,10 @@ describe('Supplier contact path', () => {
|
|||
|
||||
it(`should remove the created contact`, async() => {
|
||||
await page.waitToClick(selectors.supplierContact.thirdContactDeleteButton, 'value');
|
||||
const result = await page.countElement(selectors.supplierContact.anyContact);
|
||||
|
||||
expect(result).toEqual(2);
|
||||
|
||||
await page.waitToClick(selectors.supplierContact.saveButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
|
@ -21,6 +21,7 @@ let buildDir = 'dist';
|
|||
|
||||
let backSources = [
|
||||
'!node_modules',
|
||||
'!loopback/locale/*.json',
|
||||
'loopback',
|
||||
'modules/*/back/**',
|
||||
'modules/*/back/*',
|
||||
|
|
|
@ -81,5 +81,8 @@
|
|||
"shipped": "Shipped",
|
||||
"landed": "Landed",
|
||||
"addressFk": "Address",
|
||||
"companyFk": "Company"
|
||||
"companyFk": "Company",
|
||||
"You need to fill sage information before you check verified data": "You need to fill sage information before you check verified data",
|
||||
"The social name cannot be empty": "The social name cannot be empty",
|
||||
"The nif cannot be empty": "The nif cannot be empty"
|
||||
}
|
|
@ -156,5 +156,8 @@
|
|||
"shipped": "F. envío",
|
||||
"landed": "F. entrega",
|
||||
"addressFk": "Consignatario",
|
||||
"companyFk": "Empresa"
|
||||
"companyFk": "Empresa",
|
||||
"The social name cannot be empty": "La razón social no puede quedar en blanco",
|
||||
"The nif cannot be empty": "El NIF no puede quedar en blanco",
|
||||
"You need to fill sage information before you check verified data": "Debes rellenar la información de sage antes de marcar datos comprobados"
|
||||
}
|
|
@ -2,9 +2,11 @@ const app = require('vn-loopback/server/server');
|
|||
|
||||
describe('Client updateFiscalData', () => {
|
||||
const clientId = 101;
|
||||
const employeeId = 1;
|
||||
const salesAssistantId = 21;
|
||||
const administrativeId = 5;
|
||||
afterAll(async done => {
|
||||
const clientId = 101;
|
||||
const ctx = {req: {accessToken: {userId: 5}}};
|
||||
const ctx = {req: {accessToken: {userId: administrativeId}}};
|
||||
ctx.args = {postcode: 46460};
|
||||
|
||||
await app.models.Client.updateFiscalData(ctx, clientId);
|
||||
|
@ -12,8 +14,8 @@ describe('Client updateFiscalData', () => {
|
|||
done();
|
||||
});
|
||||
|
||||
it('should return an error if the user is not administrative and the isTaxDataChecked value is true', async() => {
|
||||
const ctx = {req: {accessToken: {userId: 1}}};
|
||||
it('should return an error if the user is not salesAssistant and the isTaxDataChecked value is true', async() => {
|
||||
const ctx = {req: {accessToken: {userId: employeeId}}};
|
||||
ctx.args = {};
|
||||
|
||||
let error;
|
||||
|
@ -22,11 +24,30 @@ describe('Client updateFiscalData', () => {
|
|||
error = e;
|
||||
});
|
||||
|
||||
expect(error.message).toBeDefined();
|
||||
expect(error.message).toEqual(`You can't make changes on a client with verified data`);
|
||||
});
|
||||
|
||||
it('should return an error if the salesAssistant did not fill the sage data before checking verified data', async() => {
|
||||
const client = await app.models.Client.findById(clientId);
|
||||
await client.updateAttribute('isTaxDataChecked', false);
|
||||
|
||||
const ctx = {req: {accessToken: {userId: salesAssistantId}}};
|
||||
ctx.args = {isTaxDataChecked: true};
|
||||
|
||||
let error;
|
||||
await app.models.Client.updateFiscalData(ctx, clientId)
|
||||
.catch(e => {
|
||||
error = e;
|
||||
});
|
||||
|
||||
expect(error.message).toEqual('You need to fill sage information before you check verified data');
|
||||
|
||||
// Restores
|
||||
await client.updateAttribute('isTaxDataChecked', true);
|
||||
});
|
||||
|
||||
it('should update the client fiscal data and return the count if changes made', async() => {
|
||||
const ctx = {req: {accessToken: {userId: 5}}};
|
||||
const ctx = {req: {accessToken: {userId: administrativeId}}};
|
||||
ctx.args = {postcode: 46680};
|
||||
|
||||
const client = await app.models.Client.findById(clientId);
|
||||
|
|
|
@ -45,15 +45,15 @@ module.exports = Self => {
|
|||
},
|
||||
{
|
||||
arg: 'sageTaxTypeFk',
|
||||
type: 'number'
|
||||
type: 'any'
|
||||
},
|
||||
{
|
||||
arg: 'sageTransactionTypeFk',
|
||||
type: 'number'
|
||||
type: 'any'
|
||||
},
|
||||
{
|
||||
arg: 'transferorFk',
|
||||
type: 'number'
|
||||
type: 'any'
|
||||
},
|
||||
{
|
||||
arg: 'hasToInvoiceByAddress',
|
||||
|
@ -118,6 +118,15 @@ module.exports = Self => {
|
|||
if (!isSalesAssistant && client.isTaxDataChecked)
|
||||
throw new UserError(`You can't make changes on a client with verified data`);
|
||||
|
||||
// Sage data validation
|
||||
const taxDataChecked = args.isTaxDataChecked;
|
||||
const sageTaxChecked = client.sageTaxTypeFk || args.sageTaxTypeFk;
|
||||
const sageTransactionChecked = client.sageTransactionTypeFk || args.sageTransactionTypeFk;
|
||||
const hasSageData = sageTaxChecked && sageTransactionChecked;
|
||||
|
||||
if (taxDataChecked && !hasSageData)
|
||||
throw new UserError(`You need to fill sage information before you check verified data`);
|
||||
|
||||
if (args.despiteOfClient) {
|
||||
const logRecord = {
|
||||
originFk: clientId,
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
let request = require('request-promise-native');
|
||||
let UserError = require('vn-loopback/util/user-error');
|
||||
let getFinalState = require('vn-loopback/util/hook').getFinalState;
|
||||
let isMultiple = require('vn-loopback/util/hook').isMultiple;
|
||||
const request = require('request-promise-native');
|
||||
const UserError = require('vn-loopback/util/user-error');
|
||||
const getFinalState = require('vn-loopback/util/hook').getFinalState;
|
||||
const isMultiple = require('vn-loopback/util/hook').isMultiple;
|
||||
const validateTin = require('vn-loopback/util/validateTin');
|
||||
const validateIban = require('vn-loopback/util/validateIban');
|
||||
const LoopBackContext = require('loopback-context');
|
||||
|
||||
module.exports = Self => {
|
||||
|
@ -63,7 +65,7 @@ module.exports = Self => {
|
|||
Self.validateAsync('iban', ibanNeedsValidation, {
|
||||
message: 'The IBAN does not have the correct format'
|
||||
});
|
||||
let validateIban = require('../validations/validateIban');
|
||||
|
||||
async function ibanNeedsValidation(err, done) {
|
||||
let filter = {
|
||||
fields: ['code'],
|
||||
|
@ -83,7 +85,6 @@ module.exports = Self => {
|
|||
message: 'Invalid TIN'
|
||||
});
|
||||
|
||||
let validateTin = require('../validations/validateTin');
|
||||
async function tinIsValid(err, done) {
|
||||
if (!this.isTaxDataChecked)
|
||||
return done();
|
||||
|
@ -187,7 +188,7 @@ module.exports = Self => {
|
|||
|
||||
// Validate socialName format
|
||||
const hasChanges = orgData && changes;
|
||||
const socialName = changes.socialName || orgData.socialName;
|
||||
const socialName = changes && changes.socialName || orgData && orgData.socialName;
|
||||
const isTaxDataChecked = hasChanges && (changes.isTaxDataChecked || orgData.isTaxDataChecked);
|
||||
|
||||
const socialNameChanged = hasChanges
|
||||
|
|
|
@ -52,7 +52,7 @@ export default class Controller extends Section {
|
|||
if (!this.address.provinceFk)
|
||||
this.address.provinceFk = province.id;
|
||||
|
||||
if (postcodes.length === 1)
|
||||
if (!this.address.postalCode && postcodes.length === 1)
|
||||
this.address.postalCode = postcodes[0].code;
|
||||
}
|
||||
|
||||
|
|
|
@ -39,7 +39,8 @@
|
|||
label="Social name"
|
||||
ng-model="$ctrl.client.socialName"
|
||||
rule
|
||||
info="You can use letters and spaces">
|
||||
info="You can use letters and spaces"
|
||||
required="true">
|
||||
</vn-textfield>
|
||||
<vn-textfield
|
||||
vn-one
|
||||
|
@ -63,6 +64,7 @@
|
|||
show-field="vat"
|
||||
value-field="id"
|
||||
label="Sage tax type"
|
||||
vn-acl="salesAssistant"
|
||||
rule>
|
||||
</vn-autocomplete>
|
||||
<vn-autocomplete vn-one
|
||||
|
@ -71,6 +73,7 @@
|
|||
show-field="transaction"
|
||||
value-field="id"
|
||||
label="Sage transaction type"
|
||||
vn-acl="salesAssistant"
|
||||
rule>
|
||||
</vn-autocomplete>
|
||||
<vn-autocomplete vn-one
|
||||
|
@ -82,6 +85,7 @@
|
|||
value-field="id"
|
||||
label="Previous client"
|
||||
info="In case of a company succession, specify the grantor company"
|
||||
vn-acl="salesAssistant"
|
||||
rule>
|
||||
</vn-autocomplete>
|
||||
</vn-horizontal>
|
||||
|
|
|
@ -128,7 +128,7 @@ export default class Controller extends Section {
|
|||
if (!this.client.countryFk)
|
||||
this.client.countryFk = country.id;
|
||||
|
||||
if (postcodes.length === 1)
|
||||
if (!this.client.postcode && postcodes.length === 1)
|
||||
this.client.postcode = postcodes[0].code;
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ Has to invoice: Factura
|
|||
Notify by email: Notificar vía e-mail
|
||||
Country: País
|
||||
Street: Domicilio fiscal
|
||||
City: Municipio
|
||||
City: Ciudad
|
||||
Postcode: Código postal
|
||||
Province: Provincia
|
||||
Address: Consignatario
|
||||
|
|
|
@ -7,8 +7,18 @@
|
|||
}
|
||||
},
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "Number",
|
||||
"id": true
|
||||
},
|
||||
"code": {
|
||||
"type": "String"
|
||||
},
|
||||
"itemFk": {
|
||||
"type": "Number"
|
||||
},
|
||||
"warehouseFk": {
|
||||
"type": "Number"
|
||||
}
|
||||
},
|
||||
"relations": {
|
||||
|
|
|
@ -8,18 +8,24 @@
|
|||
},
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "number",
|
||||
"type": "Number",
|
||||
"id": true,
|
||||
"description": "Identifier"
|
||||
},
|
||||
"shelve": {
|
||||
"type": "string"
|
||||
"type": "String"
|
||||
},
|
||||
"shelvingFk": {
|
||||
"type": "String"
|
||||
},
|
||||
"itemFk": {
|
||||
"type": "Number"
|
||||
},
|
||||
"deep": {
|
||||
"type": "number"
|
||||
"type": "Number"
|
||||
},
|
||||
"quantity": {
|
||||
"type": "number"
|
||||
"type": "Number"
|
||||
},
|
||||
"created": {
|
||||
"type": "Date"
|
||||
|
|
|
@ -7,7 +7,7 @@ describe('Supplier getSummary()', () => {
|
|||
expect(supplier.id).toEqual(1);
|
||||
expect(supplier.name).toEqual('Plants SL');
|
||||
expect(supplier.nif).toEqual('06089160W');
|
||||
expect(supplier.account).toEqual(4100000001);
|
||||
expect(supplier.account).toEqual('4100000001');
|
||||
expect(supplier.payDay).toEqual(15);
|
||||
});
|
||||
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
const app = require('vn-loopback/server/server');
|
||||
const LoopBackContext = require('loopback-context');
|
||||
|
||||
describe('Supplier updateFiscalData', () => {
|
||||
const supplierId = 1;
|
||||
const administrativeId = 5;
|
||||
const employeeId = 1;
|
||||
const defaultData = {
|
||||
name: 'Plants SL',
|
||||
nif: '06089160W',
|
||||
account: '4100000001',
|
||||
sageTaxTypeFk: 4,
|
||||
sageWithholdingFk: 1,
|
||||
sageTransactionTypeFk: 1,
|
||||
postCode: '15214',
|
||||
city: 'PONTEVEDRA',
|
||||
provinceFk: 1,
|
||||
countryFk: 1,
|
||||
};
|
||||
|
||||
it('should return an error if the user is not administrative', async() => {
|
||||
const ctx = {req: {accessToken: {userId: employeeId}}};
|
||||
ctx.args = {};
|
||||
|
||||
let error;
|
||||
await app.models.Supplier.updateFiscalData(ctx, supplierId)
|
||||
.catch(e => {
|
||||
error = e;
|
||||
});
|
||||
|
||||
expect(error.message).toBeDefined();
|
||||
});
|
||||
|
||||
it('should check that the supplier fiscal data is untainted', async() => {
|
||||
const supplier = await app.models.Supplier.findById(supplierId);
|
||||
|
||||
expect(supplier.name).toEqual(defaultData.name);
|
||||
expect(supplier.nif).toEqual(defaultData.nif);
|
||||
expect(supplier.account).toEqual(defaultData.account);
|
||||
expect(supplier.sageTaxTypeFk).toEqual(defaultData.sageTaxTypeFk);
|
||||
expect(supplier.sageWithholdingFk).toEqual(defaultData.sageWithholdingFk);
|
||||
expect(supplier.sageTransactionTypeFk).toEqual(defaultData.sageTransactionTypeFk);
|
||||
expect(supplier.postCode).toEqual(defaultData.postCode);
|
||||
expect(supplier.city).toEqual(defaultData.city);
|
||||
expect(supplier.provinceFk).toEqual(defaultData.provinceFk);
|
||||
expect(supplier.countryFk).toEqual(defaultData.countryFk);
|
||||
});
|
||||
|
||||
it('should update the supplier fiscal data and return the count if changes made', async() => {
|
||||
const activeCtx = {
|
||||
accessToken: {userId: administrativeId},
|
||||
};
|
||||
const ctx = {req: activeCtx};
|
||||
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
|
||||
active: activeCtx
|
||||
});
|
||||
|
||||
ctx.args = {
|
||||
name: 'Weapon Dealer',
|
||||
nif: 'A68446004',
|
||||
account: '4000000005',
|
||||
sageTaxTypeFk: 5,
|
||||
sageWithholdingFk: 2,
|
||||
sageTransactionTypeFk: 2,
|
||||
postCode: '46460',
|
||||
city: 'VALENCIA',
|
||||
provinceFk: 2,
|
||||
countryFk: 1,
|
||||
};
|
||||
|
||||
const result = await app.models.Supplier.updateFiscalData(ctx, supplierId);
|
||||
|
||||
expect(result.name).toEqual('Weapon Dealer');
|
||||
expect(result.nif).toEqual('A68446004');
|
||||
expect(result.account).toEqual('4000000005');
|
||||
expect(result.sageTaxTypeFk).toEqual(5);
|
||||
expect(result.sageWithholdingFk).toEqual(2);
|
||||
expect(result.sageTransactionTypeFk).toEqual(2);
|
||||
expect(result.postCode).toEqual('46460');
|
||||
expect(result.city).toEqual('VALENCIA');
|
||||
expect(result.provinceFk).toEqual(2);
|
||||
expect(result.countryFk).toEqual(1);
|
||||
|
||||
// Restores
|
||||
ctx.args = defaultData;
|
||||
await app.models.Supplier.updateFiscalData(ctx, supplierId);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,78 @@
|
|||
module.exports = Self => {
|
||||
Self.remoteMethod('updateFiscalData', {
|
||||
description: 'Updates fiscal data of a supplier',
|
||||
accessType: 'WRITE',
|
||||
accepts: [{
|
||||
arg: 'ctx',
|
||||
type: 'Object',
|
||||
http: {source: 'context'}
|
||||
},
|
||||
{
|
||||
arg: 'id',
|
||||
type: 'Number',
|
||||
description: 'The supplier id',
|
||||
http: {source: 'path'}
|
||||
},
|
||||
{
|
||||
arg: 'name',
|
||||
type: 'string'
|
||||
},
|
||||
{
|
||||
arg: 'nif',
|
||||
type: 'string'
|
||||
},
|
||||
{
|
||||
arg: 'account',
|
||||
type: 'string'
|
||||
},
|
||||
{
|
||||
arg: 'sageTaxTypeFk',
|
||||
type: 'number'
|
||||
},
|
||||
{
|
||||
arg: 'sageWithholdingFk',
|
||||
type: 'number'
|
||||
},
|
||||
{
|
||||
arg: 'sageTransactionTypeFk',
|
||||
type: 'number'
|
||||
},
|
||||
{
|
||||
arg: 'postCode',
|
||||
type: 'string'
|
||||
},
|
||||
{
|
||||
arg: 'city',
|
||||
type: 'string'
|
||||
},
|
||||
{
|
||||
arg: 'provinceFk',
|
||||
type: 'number'
|
||||
},
|
||||
{
|
||||
arg: 'countryFk',
|
||||
type: 'number'
|
||||
}],
|
||||
returns: {
|
||||
arg: 'res',
|
||||
type: 'string',
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
path: `/:id/updateFiscalData`,
|
||||
verb: 'PATCH'
|
||||
}
|
||||
});
|
||||
|
||||
Self.updateFiscalData = async(ctx, supplierId) => {
|
||||
const models = Self.app.models;
|
||||
const args = ctx.args;
|
||||
const supplier = await models.Supplier.findById(supplierId);
|
||||
|
||||
// Remove unwanted properties
|
||||
delete args.ctx;
|
||||
delete args.id;
|
||||
|
||||
return supplier.updateAttributes(args);
|
||||
};
|
||||
};
|
|
@ -1,4 +1,85 @@
|
|||
const UserError = require('vn-loopback/util/user-error');
|
||||
const validateTin = require('vn-loopback/util/validateTin');
|
||||
|
||||
module.exports = Self => {
|
||||
require('../methods/supplier/filter')(Self);
|
||||
require('../methods/supplier/getSummary')(Self);
|
||||
require('../methods/supplier/updateFiscalData')(Self);
|
||||
|
||||
Self.validatesPresenceOf('name', {
|
||||
message: 'The social name cannot be empty'
|
||||
});
|
||||
|
||||
Self.validatesUniquenessOf('name', {
|
||||
message: 'The supplier name must be unique'
|
||||
});
|
||||
|
||||
Self.validatesPresenceOf('city', {
|
||||
message: 'City cannot be empty'
|
||||
});
|
||||
|
||||
Self.validatesPresenceOf('nif', {
|
||||
message: 'The nif cannot be empty'
|
||||
});
|
||||
|
||||
Self.validatesUniquenessOf('nif', {
|
||||
message: 'TIN must be unique'
|
||||
});
|
||||
|
||||
Self.validateAsync('nif', tinIsValid, {
|
||||
message: 'Invalid TIN'
|
||||
});
|
||||
|
||||
Self.validatesLengthOf('postCode', {
|
||||
allowNull: true,
|
||||
allowBlank: true,
|
||||
min: 3, max: 10
|
||||
});
|
||||
|
||||
Self.validateAsync('postCode', hasValidPostcode, {
|
||||
message: `The postcode doesn't exist. Please enter a correct one`
|
||||
});
|
||||
|
||||
async function hasValidPostcode(err, done) {
|
||||
if (!this.postcode)
|
||||
return done();
|
||||
|
||||
const models = Self.app.models;
|
||||
const postcode = await models.Postcode.findById(this.postcode);
|
||||
|
||||
if (!postcode) err();
|
||||
done();
|
||||
}
|
||||
|
||||
async function tinIsValid(err, done) {
|
||||
const filter = {
|
||||
fields: ['code'],
|
||||
where: {id: this.countryFk}
|
||||
};
|
||||
const country = await Self.app.models.Country.findOne(filter);
|
||||
const code = country ? country.code.toLowerCase() : null;
|
||||
|
||||
if (!this.nif || !validateTin(this.nif, code))
|
||||
err();
|
||||
done();
|
||||
}
|
||||
|
||||
function isAlpha(value) {
|
||||
const regexp = new RegExp(/^[ñça-zA-Z0-9\s]*$/i);
|
||||
|
||||
return regexp.test(value);
|
||||
}
|
||||
|
||||
Self.observe('before save', async function(ctx) {
|
||||
let changes = ctx.data || ctx.instance;
|
||||
let orgData = ctx.currentInstance;
|
||||
|
||||
const socialName = changes.name || orgData.name;
|
||||
const hasChanges = orgData && changes;
|
||||
const socialNameChanged = hasChanges
|
||||
&& orgData.socialName != socialName;
|
||||
|
||||
if ((socialNameChanged) && !isAlpha(socialName))
|
||||
throw new UserError('The socialName has an invalid format');
|
||||
});
|
||||
};
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
"type": "String"
|
||||
},
|
||||
"account": {
|
||||
"type": "Number"
|
||||
"type": "String"
|
||||
},
|
||||
"countryFk": {
|
||||
"type": "Number"
|
||||
|
@ -64,7 +64,7 @@
|
|||
"type": "Number"
|
||||
},
|
||||
"postCode": {
|
||||
"type": "Number"
|
||||
"type": "String"
|
||||
},
|
||||
"payMethodFk": {
|
||||
"type": "Number"
|
||||
|
@ -77,7 +77,25 @@
|
|||
},
|
||||
"nickname": {
|
||||
"type": "String"
|
||||
}
|
||||
},
|
||||
"sageTaxTypeFk": {
|
||||
"type": "number",
|
||||
"mysql": {
|
||||
"columnName": "taxTypeSageFk"
|
||||
}
|
||||
},
|
||||
"sageTransactionTypeFk": {
|
||||
"type": "number",
|
||||
"mysql": {
|
||||
"columnName": "transactionTypeSageFk"
|
||||
}
|
||||
},
|
||||
"sageWithholdingFk": {
|
||||
"type": "number",
|
||||
"mysql": {
|
||||
"columnName": "withholdingSageFk"
|
||||
}
|
||||
}
|
||||
},
|
||||
"relations": {
|
||||
"payMethod": {
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
<mg-ajax path="Suppliers/{{patch.params.id}}" options="vnPatch"></mg-ajax>
|
||||
<vn-watcher
|
||||
vn-id="watcher"
|
||||
data="$ctrl.supplier"
|
||||
form="form"
|
||||
save="patch">
|
||||
</vn-watcher>
|
||||
<form name="form" ng-submit="watcher.submit()" class="vn-w-md">
|
||||
<vn-card class="vn-pa-lg">
|
||||
<vn-horizontal>
|
||||
<vn-textfield
|
||||
vn-one
|
||||
label="Alias"
|
||||
ng-model="$ctrl.supplier.nickname"
|
||||
rule
|
||||
vn-focus>
|
||||
</vn-textfield>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-check
|
||||
label="Official"
|
||||
ng-model="$ctrl.supplier.isOfficial">
|
||||
</vn-check>
|
||||
<vn-check
|
||||
label="Active"
|
||||
ng-model="$ctrl.supplier.isActive">
|
||||
</vn-check>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-textarea
|
||||
vn-one
|
||||
label="Notes"
|
||||
ng-model="$ctrl.supplier.note"
|
||||
rule>
|
||||
</vn-textarea>
|
||||
</vn-horizontal>
|
||||
</vn-card>
|
||||
<vn-button-bar>
|
||||
<vn-submit label="Save"></vn-submit>
|
||||
<vn-button label="Undo changes" ng-if="watcher.dataChanged()" ng-click="watcher.loadOriginalData()"></vn-button>
|
||||
</vn-button-bar>
|
||||
</form>
|
|
@ -0,0 +1,10 @@
|
|||
import ngModule from '../module';
|
||||
import Section from 'salix/components/section';
|
||||
|
||||
ngModule.vnComponent('vnSupplierBasicData', {
|
||||
template: require('./index.html'),
|
||||
controller: Section,
|
||||
bindings: {
|
||||
supplier: '<'
|
||||
}
|
||||
});
|
|
@ -0,0 +1,3 @@
|
|||
Notes: Notas
|
||||
Active: Activo
|
||||
Official: Oficial
|
|
@ -0,0 +1,165 @@
|
|||
<mg-ajax path="Suppliers/{{patch.params.id}}/updateFiscalData" options="vnPatch"></mg-ajax>
|
||||
<vn-watcher
|
||||
vn-id="watcher"
|
||||
data="$ctrl.supplier"
|
||||
id-field="id"
|
||||
form="form"
|
||||
save="patch">
|
||||
</vn-watcher>
|
||||
<vn-crud-model
|
||||
auto-load="true"
|
||||
url="Provinces/location"
|
||||
data="provincesLocation"
|
||||
order="name">
|
||||
</vn-crud-model>
|
||||
<vn-crud-model
|
||||
auto-load="true"
|
||||
url="Countries"
|
||||
data="countries"
|
||||
order="country">
|
||||
</vn-crud-model>
|
||||
<vn-crud-model
|
||||
auto-load="true"
|
||||
url="SageTaxTypes"
|
||||
data="sageTaxTypes"
|
||||
order="vat">
|
||||
</vn-crud-model>
|
||||
<vn-crud-model
|
||||
auto-load="true"
|
||||
url="SageTransactionTypes"
|
||||
data="sageTransactionTypes"
|
||||
order="transaction">
|
||||
</vn-crud-model>
|
||||
<vn-crud-model
|
||||
auto-load="true"
|
||||
url="SageWithholdings"
|
||||
data="sageWithholdings"
|
||||
order="withholding">
|
||||
</vn-crud-model>
|
||||
<form name="form" vn-http-submit="watcher.submit()" class="vn-w-md">
|
||||
<vn-card class="vn-pa-lg">
|
||||
<vn-horizontal>
|
||||
<vn-textfield
|
||||
vn-two
|
||||
vn-focus
|
||||
label="Social name"
|
||||
ng-model="$ctrl.supplier.name"
|
||||
info="You can use letters and spaces"
|
||||
required="true"
|
||||
rule>
|
||||
</vn-textfield>
|
||||
<vn-textfield
|
||||
vn-one
|
||||
label="Tax number"
|
||||
ng-model="$ctrl.supplier.nif"
|
||||
required="true"
|
||||
rule>
|
||||
</vn-textfield>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-textfield
|
||||
vn-one
|
||||
label="Account"
|
||||
ng-model="$ctrl.supplier.account"
|
||||
rule>
|
||||
</vn-textfield>
|
||||
<vn-autocomplete vn-one
|
||||
ng-model="$ctrl.supplier.sageTaxTypeFk"
|
||||
data="sageTaxTypes"
|
||||
show-field="vat"
|
||||
value-field="id"
|
||||
label="Sage tax type"
|
||||
rule>
|
||||
</vn-autocomplete>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-autocomplete vn-one
|
||||
ng-model="$ctrl.supplier.sageWithholdingFk"
|
||||
data="sageWithholdings"
|
||||
show-field="withholding"
|
||||
value-field="id"
|
||||
label="Sage withholding"
|
||||
rule>
|
||||
</vn-autocomplete>
|
||||
<vn-autocomplete vn-one
|
||||
ng-model="$ctrl.supplier.sageTransactionTypeFk"
|
||||
data="sageTransactionTypes"
|
||||
show-field="transaction"
|
||||
value-field="id"
|
||||
label="Sage transaction type"
|
||||
rule>
|
||||
</vn-autocomplete>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-datalist vn-one
|
||||
label="Postcode"
|
||||
ng-model="$ctrl.supplier.postCode"
|
||||
selection="$ctrl.postcode"
|
||||
url="Postcodes/location"
|
||||
fields="['code','townFk']"
|
||||
order="code, townFk"
|
||||
value-field="code"
|
||||
show-field="code"
|
||||
rule>
|
||||
<tpl-item>
|
||||
{{code}} - {{town.name}} ({{town.province.name}},
|
||||
{{town.province.country.country}})
|
||||
</tpl-item>
|
||||
<append>
|
||||
<vn-icon-button
|
||||
icon="add_circle"
|
||||
vn-tooltip="New postcode"
|
||||
ng-click="postcode.open()"
|
||||
vn-acl="deliveryBoss"
|
||||
vn-acl-action="remove">
|
||||
</vn-icon-button>
|
||||
</append>
|
||||
</vn-datalist>
|
||||
<vn-datalist vn-id="town" vn-one
|
||||
label="City"
|
||||
ng-model="$ctrl.supplier.city"
|
||||
selection="$ctrl.town"
|
||||
url="Towns/location"
|
||||
fields="['id', 'name', 'provinceFk']"
|
||||
show-field="name"
|
||||
value-field="name"
|
||||
required="true"
|
||||
rule>
|
||||
<tpl-item>
|
||||
{{name}}, {{province.name}}
|
||||
({{province.country.country}})
|
||||
</tpl-item>
|
||||
</vn-datalist>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-autocomplete vn-id="province" vn-one
|
||||
label="Province"
|
||||
ng-model="$ctrl.supplier.provinceFk"
|
||||
selection="$ctrl.province"
|
||||
data="provincesLocation"
|
||||
fields="['id', 'name', 'countryFk']"
|
||||
show-field="name"
|
||||
value-field="id"
|
||||
rule>
|
||||
<tpl-item>{{name}} ({{country.country}})</tpl-item>
|
||||
</vn-autocomplete>
|
||||
<vn-autocomplete vn-id="country" vn-one
|
||||
ng-model="$ctrl.supplier.countryFk"
|
||||
data="countries"
|
||||
show-field="country"
|
||||
value-field="id"
|
||||
label="Country"
|
||||
rule>
|
||||
</vn-autocomplete>
|
||||
</vn-horizontal>
|
||||
</vn-card>
|
||||
<vn-button-bar>
|
||||
<vn-submit label="Save"></vn-submit>
|
||||
<vn-button label="Undo changes" ng-if="$ctrl.$.form.$dirty" ng-click="watcher.loadOriginalData()"></vn-button>
|
||||
</vn-button-bar>
|
||||
</form>
|
||||
<!-- New postcode dialog -->
|
||||
<vn-geo-postcode
|
||||
vn-id="postcode"
|
||||
on-response="$ctrl.onResponse($response)">
|
||||
</vn-geo-postcode>
|
|
@ -0,0 +1,84 @@
|
|||
import ngModule from '../module';
|
||||
import Section from 'salix/components/section';
|
||||
|
||||
export default class Controller extends Section {
|
||||
get province() {
|
||||
return this._province;
|
||||
}
|
||||
|
||||
// Province auto complete
|
||||
set province(selection) {
|
||||
this._province = selection;
|
||||
|
||||
if (!selection) return;
|
||||
|
||||
const country = selection.country;
|
||||
|
||||
if (!this.supplier.countryFk)
|
||||
this.supplier.countryFk = country.id;
|
||||
}
|
||||
|
||||
get town() {
|
||||
return this._town;
|
||||
}
|
||||
|
||||
// Town auto complete
|
||||
set town(selection) {
|
||||
this._town = selection;
|
||||
|
||||
if (!selection) return;
|
||||
|
||||
const province = selection.province;
|
||||
const country = province.country;
|
||||
const postcodes = selection.postcodes;
|
||||
|
||||
if (!this.supplier.provinceFk)
|
||||
this.supplier.provinceFk = province.id;
|
||||
|
||||
if (!this.supplier.countryFk)
|
||||
this.supplier.countryFk = country.id;
|
||||
|
||||
if (!this.supplier.postCode && postcodes.length === 1)
|
||||
this.supplier.postCode = postcodes[0].code;
|
||||
}
|
||||
|
||||
get postcode() {
|
||||
return this._postcode;
|
||||
}
|
||||
|
||||
// Postcode auto complete
|
||||
set postcode(selection) {
|
||||
const oldValue = this._postcode;
|
||||
this._postcode = selection;
|
||||
|
||||
if (!selection || !oldValue) return;
|
||||
|
||||
const town = selection.town;
|
||||
const province = town.province;
|
||||
const country = province.country;
|
||||
|
||||
if (!this.supplier.city)
|
||||
this.supplier.city = town.name;
|
||||
|
||||
if (!this.supplier.provinceFk)
|
||||
this.supplier.provinceFk = province.id;
|
||||
|
||||
if (!this.supplier.countryFk)
|
||||
this.supplier.countryFk = country.id;
|
||||
}
|
||||
|
||||
onResponse(response) {
|
||||
this.supplier.postCode = response.code;
|
||||
this.supplier.city = response.city;
|
||||
this.supplier.provinceFk = response.provinceFk;
|
||||
this.supplier.countryFk = response.countryFk;
|
||||
}
|
||||
}
|
||||
|
||||
ngModule.vnComponent('vnSupplierFiscalData', {
|
||||
template: require('./index.html'),
|
||||
controller: Controller,
|
||||
bindings: {
|
||||
supplier: '<'
|
||||
}
|
||||
});
|
|
@ -0,0 +1,109 @@
|
|||
import './index';
|
||||
import watcher from 'core/mocks/watcher';
|
||||
|
||||
describe('Supplier', () => {
|
||||
describe('Component vnSupplierFiscalData', () => {
|
||||
let $scope;
|
||||
let $element;
|
||||
let controller;
|
||||
|
||||
beforeEach(ngModule('supplier'));
|
||||
|
||||
beforeEach(inject(($componentController, $rootScope) => {
|
||||
$scope = $rootScope.$new();
|
||||
$scope.watcher = watcher;
|
||||
$scope.watcher.orgData = {id: 1};
|
||||
$element = angular.element('<vn-supplier-fiscal-data></supplier-fiscal-data>');
|
||||
controller = $componentController('vnSupplierFiscalData', {$element, $scope});
|
||||
controller.card = {reload: () => {}};
|
||||
controller.supplier = {
|
||||
id: 1,
|
||||
name: 'Batman'
|
||||
};
|
||||
|
||||
controller._province = {};
|
||||
controller._town = {};
|
||||
controller._postcode = {};
|
||||
}));
|
||||
|
||||
describe('province() setter', () => {
|
||||
it(`should set countryFk property`, () => {
|
||||
controller.supplier.countryFk = null;
|
||||
controller.province = {
|
||||
id: 1,
|
||||
name: 'New york',
|
||||
country: {
|
||||
id: 2,
|
||||
name: 'USA'
|
||||
}
|
||||
};
|
||||
|
||||
expect(controller.supplier.countryFk).toEqual(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('town() setter', () => {
|
||||
it(`should set provinceFk property`, () => {
|
||||
controller.town = {
|
||||
provinceFk: 1,
|
||||
code: 46001,
|
||||
province: {
|
||||
id: 1,
|
||||
name: 'New york',
|
||||
country: {
|
||||
id: 2,
|
||||
name: 'USA'
|
||||
}
|
||||
},
|
||||
postcodes: []
|
||||
};
|
||||
|
||||
expect(controller.supplier.provinceFk).toEqual(1);
|
||||
});
|
||||
|
||||
it(`should set provinceFk property and fill the postalCode if there's just one`, () => {
|
||||
controller.town = {
|
||||
provinceFk: 1,
|
||||
code: 46001,
|
||||
province: {
|
||||
id: 1,
|
||||
name: 'New york',
|
||||
country: {
|
||||
id: 2,
|
||||
name: 'USA'
|
||||
}
|
||||
},
|
||||
postcodes: [{code: '46001'}]
|
||||
};
|
||||
|
||||
expect(controller.supplier.provinceFk).toEqual(1);
|
||||
expect(controller.supplier.postCode).toEqual('46001');
|
||||
});
|
||||
});
|
||||
|
||||
describe('postcode() setter', () => {
|
||||
it(`should set the town, provinceFk and contryFk properties`, () => {
|
||||
controller.postcode = {
|
||||
townFk: 1,
|
||||
code: 46001,
|
||||
town: {
|
||||
id: 1,
|
||||
name: 'New York',
|
||||
province: {
|
||||
id: 1,
|
||||
name: 'New york',
|
||||
country: {
|
||||
id: 2,
|
||||
name: 'USA'
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
expect(controller.supplier.city).toEqual('New York');
|
||||
expect(controller.supplier.provinceFk).toEqual(1);
|
||||
expect(controller.supplier.countryFk).toEqual(2);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,3 @@
|
|||
Sage tax type: Tipo de impuesto Sage
|
||||
Sage transaction type: Tipo de transacción Sage
|
||||
Sage withholding: Retención Sage
|
|
@ -5,6 +5,8 @@ import './card';
|
|||
import './descriptor';
|
||||
import './index/';
|
||||
import './search-panel';
|
||||
import './log';
|
||||
import './summary';
|
||||
import './basic-data';
|
||||
import './fiscal-data';
|
||||
import './contact';
|
||||
import './log';
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
{"state": "supplier.index", "icon": "icon-supplier"}
|
||||
],
|
||||
"card": [
|
||||
{"state": "supplier.card.basicData", "icon": "settings"},
|
||||
{"state": "supplier.card.fiscalData", "icon": "account_balance"},
|
||||
{"state": "supplier.card.contact", "icon": "contact_phone"},
|
||||
{"state": "supplier.card.log", "icon": "history"}
|
||||
]
|
||||
|
@ -41,13 +43,30 @@
|
|||
"params": {
|
||||
"supplier": "$ctrl.supplier"
|
||||
}
|
||||
},
|
||||
{
|
||||
}, {
|
||||
"url": "/basic-data",
|
||||
"state": "supplier.card.basicData",
|
||||
"component": "vn-supplier-basic-data",
|
||||
"description": "Basic data",
|
||||
"acl": ["administrative"],
|
||||
"params": {
|
||||
"supplier": "$ctrl.supplier"
|
||||
}
|
||||
}, {
|
||||
"url": "/fiscal-data",
|
||||
"state": "supplier.card.fiscalData",
|
||||
"component": "vn-supplier-fiscal-data",
|
||||
"description": "Fiscal data",
|
||||
"params": {
|
||||
"supplier": "$ctrl.supplier"
|
||||
},
|
||||
"acl": ["administrative"]
|
||||
}, {
|
||||
"url" : "/log",
|
||||
"state": "supplier.card.log",
|
||||
"component": "vn-supplier-log",
|
||||
"description": "Log"
|
||||
},
|
||||
},
|
||||
{
|
||||
"url": "/contact",
|
||||
"state": "supplier.card.contact",
|
||||
|
|
|
@ -12,6 +12,12 @@
|
|||
"type": "Number",
|
||||
"forceId": false
|
||||
},
|
||||
"saleFk": {
|
||||
"type": "Number"
|
||||
},
|
||||
"stateFk": {
|
||||
"type": "Number"
|
||||
},
|
||||
"isChecked": {
|
||||
"type": "boolean"
|
||||
},
|
||||
|
|
|
@ -5,22 +5,30 @@
|
|||
"model": "TicketLog",
|
||||
"relation": "ticket",
|
||||
"showField": "stateFk"
|
||||
},
|
||||
"options": {
|
||||
"mysql": {
|
||||
"table": "ticketTracking"
|
||||
}
|
||||
},
|
||||
},
|
||||
"options": {
|
||||
"mysql": {
|
||||
"table": "ticketTracking"
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"id": {
|
||||
"id": true,
|
||||
"type": "Number",
|
||||
"forceId": false
|
||||
},
|
||||
"created": {
|
||||
"type": "Date",
|
||||
"required": false
|
||||
}
|
||||
"id": {
|
||||
"id": true,
|
||||
"type": "Number",
|
||||
"forceId": false
|
||||
},
|
||||
"created": {
|
||||
"type": "Date"
|
||||
},
|
||||
"ticketFk": {
|
||||
"type": "Number"
|
||||
},
|
||||
"stateFk": {
|
||||
"type": "Number"
|
||||
},
|
||||
"workerFk": {
|
||||
"type": "Number"
|
||||
}
|
||||
},
|
||||
"relations": {
|
||||
"ticket": {
|
||||
|
|
Loading…
Reference in New Issue