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

This commit is contained in:
Bernat Exposito 2020-11-10 08:46:37 +01:00
commit e4e4f37609
52 changed files with 1208 additions and 82 deletions

View File

@ -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);
});
});

View File

@ -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();
});
});

View File

@ -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]);
};
};

View File

@ -56,6 +56,9 @@
"Sip": {
"dataSource": "vn"
},
"SageWithholding": {
"dataSource": "vn"
},
"UserConfigView": {
"dataSource": "vn"
},

View File

@ -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"
}
]
}

View File

@ -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');

View File

@ -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 ;

View File

@ -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');

View File

@ -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 */;

View File

@ -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

View File

@ -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) {

View File

@ -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"]',
}
};

View File

@ -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);

View File

@ -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');
});

View File

@ -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');

View File

@ -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});
});
});

View File

@ -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');
});
});

View File

@ -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');
});
});

View File

@ -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();

View File

@ -21,6 +21,7 @@ let buildDir = 'dist';
let backSources = [
'!node_modules',
'!loopback/locale/*.json',
'loopback',
'modules/*/back/**',
'modules/*/back/*',

View File

@ -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"
}

View File

@ -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"
}

View File

@ -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);

View File

@ -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,

View File

@ -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

View File

@ -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;
}

View File

@ -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>

View File

@ -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;
}

View File

@ -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

View File

@ -7,8 +7,18 @@
}
},
"properties": {
"id": {
"type": "Number",
"id": true
},
"code": {
"type": "String"
},
"itemFk": {
"type": "Number"
},
"warehouseFk": {
"type": "Number"
}
},
"relations": {

View File

@ -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"

View File

@ -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);
});

View File

@ -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);
});
});

View File

@ -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);
};
};

View File

@ -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');
});
};

View File

@ -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": {

View File

@ -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>

View File

@ -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: '<'
}
});

View File

@ -0,0 +1,3 @@
Notes: Notas
Active: Activo
Official: Oficial

View File

@ -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>

View File

@ -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: '<'
}
});

View File

@ -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);
});
});
});
});

View File

@ -0,0 +1,3 @@
Sage tax type: Tipo de impuesto Sage
Sage transaction type: Tipo de transacción Sage
Sage withholding: Retención Sage

View File

@ -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';

View File

@ -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",

View File

@ -12,6 +12,12 @@
"type": "Number",
"forceId": false
},
"saleFk": {
"type": "Number"
},
"stateFk": {
"type": "Number"
},
"isChecked": {
"type": "boolean"
},

View File

@ -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": {