Field ACL part one #689 (vn-model) beforeRemote() wrapper

This commit is contained in:
Joan Sanchez 2018-10-23 11:25:51 +02:00
parent 9f7be6364c
commit f2d5e6afee
35 changed files with 272 additions and 566 deletions

View File

@ -164,6 +164,7 @@
"state": "client.card.credit.create", "state": "client.card.credit.create",
"component": "vn-client-credit-create", "component": "vn-client-credit-create",
"description": "New credit", "description": "New credit",
"acl": ["teamBoss"],
"params": { "params": {
"client": "$ctrl.client" "client": "$ctrl.client"
} }
@ -191,6 +192,7 @@
"state": "client.card.greuge.create", "state": "client.card.greuge.create",
"component": "vn-client-greuge-create", "component": "vn-client-greuge-create",
"description": "New greuge", "description": "New greuge",
"acl": ["salesAssistant"],
"params": { "params": {
"client": "$ctrl.client" "client": "$ctrl.client"
} }

View File

@ -1,4 +1,4 @@
<mg-ajax path="/client/api/Clients/{{patch.params.id}}/updateBasicData" options="vnPatch"></mg-ajax> <mg-ajax path="/client/api/Clients/{{patch.params.id}}" options="vnPatch"></mg-ajax>
<vn-watcher <vn-watcher
vn-id="watcher" vn-id="watcher"
data="$ctrl.client" data="$ctrl.client"
@ -34,8 +34,7 @@
show-field="firstName" show-field="firstName"
value-field="id" value-field="id"
label="Salesperson" label="Salesperson"
vn-acl="salesAssistant, employee" vn-acl="salesAssistant">
acl-conditional-to-employee="{{!$ctrl.client.isTaxDataChecked}}">
<tpl-item>{{firstName}} {{name}}</tpl-item> <tpl-item>{{firstName}} {{name}}</tpl-item>
</vn-autocomplete> </vn-autocomplete>
<vn-autocomplete vn-one <vn-autocomplete vn-one

View File

@ -1,9 +1,9 @@
<mg-ajax path="/client/api/Clients/{{post.params.id}}/updateBillingData" options="vnPost"></mg-ajax> <mg-ajax path="/client/api/Clients/{{patch.params.id}}" options="vnPatch"></mg-ajax>
<vn-watcher <vn-watcher
vn-id="watcher" vn-id="watcher"
data="$ctrl.client" data="$ctrl.client"
form="form" form="form"
save="post"> save="patch">
</vn-watcher> </vn-watcher>
<form name="form" ng-submit="$ctrl.onSubmit()"> <form name="form" ng-submit="$ctrl.onSubmit()">
<vn-card pad-large> <vn-card pad-large>
@ -36,9 +36,9 @@
label="Swift / BIC" label="Swift / BIC"
url="/client/api/BankEntities" url="/client/api/BankEntities"
field="$ctrl.client.bankEntityFk" field="$ctrl.client.bankEntityFk"
select-fields="['name']" fields="['name']"
initial-data="$ctrl.client.bankEntityFk" initial-data="$ctrl.client.bankEntityFk"
where="{or: [{bic: {regexp: 'search'}}, {name: {regexp: 'search'}}]}" search-function="{or: [{bic: {regexp: $search}}, {name: {regexp: $search}}]}"
value-field="id" value-field="id"
show-field="bic" show-field="bic"
vn-acl="salesAssistant"> vn-acl="salesAssistant">

View File

@ -37,7 +37,11 @@
</vn-pagination> </vn-pagination>
</vn-card> </vn-card>
</vn-vertical> </vn-vertical>
<a ui-sref="client.card.credit.create" vn-tooltip="New credit"
vn-bind="+" fixed-bottom-right> <vn-float-button icon="add" fixed-bottom-right
<vn-float-button icon="add"></vn-float-button> ui-sref="client.card.credit.create"
</a> vn-acl="teamBoss"
vn-acl-action="remove"
vn-tooltip="New credit"
vn-bind="+">
</vn-float-button>

View File

@ -1,4 +1,4 @@
<mg-ajax path="/client/api/Clients/{{patch.params.id}}" options="vnPatch"></mg-ajax> <mg-ajax path="/client/api/Clients/{{patch.params.id}}/updateFiscalData" options="vnPatch"></mg-ajax>
<vn-watcher <vn-watcher
vn-id="watcher" vn-id="watcher"
data="$ctrl.client" data="$ctrl.client"
@ -13,41 +13,31 @@
vn-two vn-two
vn-focus vn-focus
label="Social name" label="Social name"
field="$ctrl.client.socialName" field="$ctrl.client.socialName">
vn-acl="administrative, salesAssistant, salesPerson"
acl-conditional-to-salesPerson="{{!$ctrl.client.isTaxDataChecked}}">
</vn-textfield> </vn-textfield>
<vn-textfield <vn-textfield
vn-one vn-one
label="Tax number" label="Tax number"
field="$ctrl.client.fi" field="$ctrl.client.fi">
vn-acl="administrative, salesAssistant, salesPerson"
acl-conditional-to-salesPerson="{{!$ctrl.client.isTaxDataChecked}}">
</vn-textfield> </vn-textfield>
</vn-horizontal> </vn-horizontal>
<vn-horizontal> <vn-horizontal>
<vn-textfield <vn-textfield
vn-two vn-two
label="Street" label="Street"
field="$ctrl.client.street" field="$ctrl.client.street">
vn-acl="administrative, salesAssistant, salesPerson"
acl-conditional-to-salesPerson="{{!$ctrl.client.isTaxDataChecked}}">
</vn-textfield> </vn-textfield>
</vn-horizontal> </vn-horizontal>
<vn-horizontal> <vn-horizontal>
<vn-textfield <vn-textfield
vn-one vn-one
label="Postcode" label="Postcode"
field="$ctrl.client.postcode" field="$ctrl.client.postcode">
vn-acl="administrative, salesAssistant, salesPerson"
acl-conditional-to-salesPerson="{{!$ctrl.client.isTaxDataChecked}}">
</vn-textfield> </vn-textfield>
<vn-textfield <vn-textfield
vn-one vn-one
label="City" label="City"
field="$ctrl.client.city" field="$ctrl.client.city">
vn-acl="administrative, salesAssistant, salesPerson"
acl-conditional-to-salesPerson="{{!$ctrl.client.isTaxDataChecked}}">
</vn-textfield> </vn-textfield>
</vn-horizontal> </vn-horizontal>
<vn-horizontal> <vn-horizontal>
@ -58,9 +48,7 @@
url="/client/api/Countries" url="/client/api/Countries"
show-field="country" show-field="country"
value-field="id" value-field="id"
label="Country" label="Country">
vn-acl="administrative, salesAssistant, salesPerson"
acl-conditional-to-salesPerson="{{!$ctrl.client.isTaxDataChecked}}">
</vn-autocomplete> </vn-autocomplete>
<vn-autocomplete <vn-autocomplete
vn-one vn-one
@ -69,80 +57,61 @@
url="/client/api/Provinces" url="/client/api/Provinces"
show-field="name" show-field="name"
value-field="id" value-field="id"
label="Province" label="Province">
vn-acl="administrative, salesAssistant, salesPerson"
acl-conditional-to-salesPerson="{{!$ctrl.client.isTaxDataChecked}}">
</vn-autocomplete> </vn-autocomplete>
</vn-horizontal> </vn-horizontal>
<vn-horizontal pad-small-v> <vn-horizontal pad-small-v>
<vn-check <vn-check
vn-one vn-one
label="Active" label="Active"
field="$ctrl.client.isActive" field="$ctrl.client.isActive">
vn-acl="administrative, salesAssistant, salesPerson"
acl-conditional-to-salesPerson="{{!$ctrl.client.isTaxDataChecked}}">
</vn-check> </vn-check>
<vn-check <vn-check
vn-one vn-one
label="Frozen" label="Frozen"
field="$ctrl.client.isFreezed" field="$ctrl.client.isFreezed">
vn-acl="administrative, salesAssistant, salesPerson"
acl-conditional-to-salesPerson="{{!$ctrl.client.isTaxDataChecked}}">
</vn-check> </vn-check>
</vn-horizontal> </vn-horizontal>
<vn-horizontal pad-small-v> <vn-horizontal pad-small-v>
<vn-check <vn-check
vn-one vn-one
label="Has to invoice" label="Has to invoice"
field="$ctrl.client.hasToInvoice" field="$ctrl.client.hasToInvoice">
vn-acl="administrative, salesAssistant, salesPerson"
acl-conditional-to-salesPerson="{{!$ctrl.client.isTaxDataChecked}}">
</vn-check> </vn-check>
<vn-check <vn-check
vn-one vn-one
label="Vies" label="Vies"
field="$ctrl.client.isVies" field="$ctrl.client.isVies">
vn-acl="administrative, salesAssistant, salesPerson"
acl-conditional-to-salesPerson="{{!$ctrl.client.isTaxDataChecked}}">
</vn-check> </vn-check>
</vn-horizontal> </vn-horizontal>
<vn-horizontal pad-small-v> <vn-horizontal pad-small-v>
<vn-check <vn-check
vn-one vn-one
label="Invoice by mail" label="Invoice by mail"
field="$ctrl.client.isToBeMailed" field="$ctrl.client.isToBeMailed">
vn-acl="administrative, salesAssistant, salesPerson"
acl-conditional-to-salesPerson="{{!$ctrl.client.isTaxDataChecked}}">
</vn-check> </vn-check>
<vn-check <vn-check
vn-one vn-one
label="Invoice by address" label="Invoice by address"
field="$ctrl.client.hasToInvoiceByAddress" field="$ctrl.client.hasToInvoiceByAddress">
vn-acl="administrative, salesAssistant, salesPerson"
acl-conditional-to-salesPerson="{{!$ctrl.client.isTaxDataChecked}}">
</vn-check> </vn-check>
</vn-horizontal> </vn-horizontal>
<vn-horizontal pad-small-v> <vn-horizontal pad-small-v>
<vn-check <vn-check
vn-one vn-one
label="Is equalizated" label="Is equalizated"
field="$ctrl.client.isEqualizated" field="$ctrl.client.isEqualizated">
vn-acl="administrative, salesAssistant, salesPerson"
acl-conditional-to-salesPerson="{{!$ctrl.client.isTaxDataChecked}}">
</vn-check> </vn-check>
<vn-check <vn-check
vn-one vn-one
label="Verified data" label="Verified data"
field="$ctrl.client.isTaxDataChecked" field="$ctrl.client.isTaxDataChecked"
vn-acl="administrative, salesAssistant, salesAssistant"> vn-acl="administrative">
</vn-check> </vn-check>
</vn-horizontal> </vn-horizontal>
</vn-card> </vn-card>
<vn-button-bar> <vn-button-bar>
<vn-submit <vn-submit label="Save">
label="Save"
vn-acl="administrative, salesAssistant, salesPerson"
acl-conditional-to-salesPerson="{{!$ctrl.client.isTaxDataChecked}}">
</vn-submit> </vn-submit>
</vn-button-bar> </vn-button-bar>
</form> </form>

View File

@ -51,8 +51,11 @@
</vn-pagination> </vn-pagination>
</vn-card> </vn-card>
</vn-vertical> </vn-vertical>
<a ui-sref="client.card.greuge.create" vn-tooltip="New greuge"
vn-bind="+" fixed-bottom-right> <vn-float-button icon="add" fixed-bottom-right
<vn-float-button icon="add"></vn-float-button> ui-sref="client.card.greuge.create"
</a> vn-tooltip="New greuge"
vn-acl="salesAssistant"
vn-acl-action="remove"
vn-bind="+">
</vn-float-button>

View File

@ -68,7 +68,7 @@
</vn-autocomplete> </vn-autocomplete>
<vn-check <vn-check
vn-one vn-one
label="Is active" label="Active"
field="$ctrl.item.isActive"> field="$ctrl.item.isActive">
</vn-check> </vn-check>
</vn-horizontal> </vn-horizontal>

View File

@ -195,7 +195,7 @@ export default {
expenceSelect: `vn-autocomplete[field="$ctrl.item.expenceFk"] input`, expenceSelect: `vn-autocomplete[field="$ctrl.item.expenceFk"] input`,
expenceSelectOptionThree: `vn-autocomplete[field="$ctrl.item.expenceFk"] vn-drop-down ul > li:nth-child(3)`, expenceSelectOptionThree: `vn-autocomplete[field="$ctrl.item.expenceFk"] vn-drop-down ul > li:nth-child(3)`,
longNameInput: `vn-textfield[label="Full name"] input`, longNameInput: `vn-textfield[label="Full name"] input`,
isActiveCheckbox: `vn-check[label='Is active'] > label > input`, isActiveCheckbox: `vn-check[label='Active'] > label > input`,
submitBasicDataButton: `${components.vnSubmit}` submitBasicDataButton: `${components.vnSubmit}`
}, },
itemTags: { itemTags: {

View File

@ -75,7 +75,7 @@ describe('Client Edit fiscalData path', () => {
}); });
}); });
it(`should click on the fiscal data button to start editing`, () => { it(`should click on the fiscal data button`, () => {
return nightmare return nightmare
.waitToClick(selectors.clientFiscalData.fiscalDataButton) .waitToClick(selectors.clientFiscalData.fiscalDataButton)
.waitForURL('fiscal-data') .waitForURL('fiscal-data')
@ -85,161 +85,6 @@ describe('Client Edit fiscalData path', () => {
}); });
}); });
it('should not be able to edit the social name', done => {
return nightmare
.wait(selectors.clientFiscalData.socialNameInput)
.evaluate(selector => {
return document.querySelector(selector).disabled;
}, selectors.clientFiscalData.socialNameInput)
.then(value => {
expect(value).toBeTruthy();
done();
}).catch(done.fail);
});
it('should not be able to edit the fiscal id', done => {
return nightmare
.evaluate(selector => {
return document.querySelector(selector).disabled;
}, selectors.clientFiscalData.fiscalIdInput)
.then(value => {
expect(value).toBeTruthy();
done();
}).catch(done.fail);
});
it('should not be able to edit the address', done => {
return nightmare
.evaluate(selector => {
return document.querySelector(selector).disabled;
}, selectors.clientFiscalData.addressInput)
.then(value => {
expect(value).toBeTruthy();
done();
}).catch(done.fail);
});
it('should not be able to edit the postcode', done => {
return nightmare
.evaluate(selector => {
return document.querySelector(selector).disabled;
}, selectors.clientFiscalData.postcodeInput)
.then(value => {
expect(value).toBeTruthy();
done();
}).catch(done.fail);
});
it('should not be able to edit the city', done => {
return nightmare
.evaluate(selector => {
return document.querySelector(selector).disabled;
}, selectors.clientFiscalData.cityInput)
.then(value => {
expect(value).toBeTruthy();
done();
}).catch(done.fail);
});
it('should not be able to edit the country', done => {
return nightmare
.evaluate(selector => {
return document.querySelector(selector).disabled;
}, selectors.clientFiscalData.countryInput)
.then(value => {
expect(value).toBeTruthy();
done();
}).catch(done.fail);
});
it('should not be able to edit the province', done => {
return nightmare
.evaluate(selector => {
return document.querySelector(selector).disabled;
}, selectors.clientFiscalData.provinceInput)
.then(value => {
expect(value).toBeTruthy();
done();
}).catch(done.fail);
});
it('should not be able to edit the active checkbox', done => {
return nightmare
.evaluate(selector => {
return document.querySelector(selector).disabled;
}, selectors.clientFiscalData.activeCheckboxInput)
.then(value => {
expect(value).toBeTruthy();
done();
}).catch(done.fail);
});
it('should not be able to edit the frozen checkbox', done => {
return nightmare
.evaluate(selector => {
return document.querySelector(selector).disabled;
}, selectors.clientFiscalData.frozenCheckboxInput)
.then(value => {
expect(value).toBeTruthy();
done();
}).catch(done.fail);
});
it('should not be able to edit the has to invoice checkbox', done => {
return nightmare
.evaluate(selector => {
return document.querySelector(selector).disabled;
}, selectors.clientFiscalData.hasToInvoiceCheckboxInput)
.then(value => {
expect(value).toBeTruthy();
done();
}).catch(done.fail);
});
it('should not be able to edit the vies checkbox', done => {
return nightmare
.evaluate(selector => {
return document.querySelector(selector).disabled;
}, selectors.clientFiscalData.viesCheckboxInput)
.then(value => {
expect(value).toBeTruthy();
done();
}).catch(done.fail);
});
it('should not be able to edit the invoice by mail checkbox', done => {
return nightmare
.evaluate(selector => {
return document.querySelector(selector).disabled;
}, selectors.clientFiscalData.invoiceByMailCheckboxInput)
.then(value => {
expect(value).toBeTruthy();
done();
}).catch(done.fail);
});
it('should not be able to edit the invoice by address', done => {
return nightmare
.evaluate(selector => {
return document.querySelector(selector).disabled;
}, selectors.clientFiscalData.invoiceByAddressCheckboxInput)
.then(value => {
expect(value).toBeTruthy();
done();
}).catch(done.fail);
});
it('should not be able to edit the equalization tax checkbox', done => {
return nightmare
.evaluate(selector => {
return document.querySelector(selector).disabled;
}, selectors.clientFiscalData.equalizationTaxCheckboxLabel)
.then(value => {
expect(value).toBeTruthy();
done();
}).catch(done.fail);
});
it('should not be able to edit the verified data checkbox', done => { it('should not be able to edit the verified data checkbox', done => {
return nightmare return nightmare
.evaluate(selector => { .evaluate(selector => {
@ -250,17 +95,6 @@ describe('Client Edit fiscalData path', () => {
done(); done();
}).catch(done.fail); }).catch(done.fail);
}); });
it('should not be able to use the submit button', done => {
return nightmare
.evaluate(selector => {
return document.querySelector(selector).disabled;
}, selectors.clientFiscalData.saveButton)
.then(value => {
expect(value).toBeTruthy();
done();
}).catch(done.fail);
});
}); });
describe('as administrative', () => { describe('as administrative', () => {

View File

@ -7,7 +7,7 @@ describe('Client', () => {
beforeAll(() => { beforeAll(() => {
return nightmare return nightmare
.waitForLogin('employee'); .waitForLogin('salesAssistant');
}); });
it('should click on the Clients button of the top bar menu', done => { it('should click on the Clients button of the top bar menu', done => {

View File

@ -290,14 +290,15 @@ describe('Client lock verified data path', () => {
}).catch(done.fail); }).catch(done.fail);
}); });
it('should confirm the form have been disabled for salesPerson', done => { it('should not be able to save change throwing a verified data error', done => {
return nightmare return nightmare
.wait(selectors.clientFiscalData.socialNameInput) .wait(selectors.clientFiscalData.socialNameInput)
.evaluate(selector => { .clearInput(selectors.clientFiscalData.socialNameInput)
return document.querySelector(selector).disabled; .type(selectors.clientFiscalData.socialNameInput, 'salesPerson was here')
}, 'vn-textfield[field="$ctrl.client.socialName"] input') .click(selectors.clientFiscalData.saveButton)
.waitForSnackbar()
.then(result => { .then(result => {
expect(result).toEqual(true); expect(result).toEqual(jasmine.arrayContaining([`You can't make changes on a client with verified data`]));
done(); done();
}).catch(done.fail); }).catch(done.fail);
}); });
@ -360,40 +361,14 @@ describe('Client lock verified data path', () => {
}).catch(done.fail); }).catch(done.fail);
}); });
it('should confirm verified data button is enabled for salesAssistant', done => { it('should confirm verified data button is disabled for salesAssistant', done => {
return nightmare return nightmare
.wait(selectors.clientFiscalData.verifiedDataCheckboxInput) .wait(selectors.clientFiscalData.verifiedDataCheckboxInput)
.evaluate(selector => { .evaluate(selector => {
return document.querySelector(selector).disabled; return document.querySelector(selector).disabled;
}, selectors.clientFiscalData.verifiedDataCheckbox) }, selectors.clientFiscalData.verifiedDataCheckbox)
.then(result => { .then(result => {
expect(result).not.toBe(true); expect(result).toBeTruthy();
done();
}).catch(done.fail);
});
it('should uncheck the Verified data checkbox', done => {
return nightmare
.waitToClick(selectors.clientFiscalData.verifiedDataCheckboxInput)
.waitToClick(selectors.clientFiscalData.saveButton)
.waitForLastSnackbar()
.then(result => {
expect(result).toEqual('Data saved!');
done();
}).catch(done.fail);
});
it('should confirm Verified data checkbox is unchecked', done => {
return nightmare
.waitToClick(selectors.clientBasicData.basicDataButton)
.wait(selectors.clientBasicData.nameInput)
.waitToClick(selectors.clientFiscalData.fiscalDataButton)
.wait(selectors.clientFiscalData.verifiedDataCheckboxInput)
.evaluate(selector => {
return document.querySelector(selector).checked;
}, selectors.clientFiscalData.verifiedDataCheckboxInput)
.then(value => {
expect(value).toBeFalsy();
done(); done();
}).catch(done.fail); }).catch(done.fail);
}); });

View File

@ -0,0 +1,71 @@
USE `vn`;
CREATE TABLE `salix`.`fieldAcl` (
`id` INT NOT NULL AUTO_INCREMENT,
`model` VARCHAR(255) NULL,
`property` VARCHAR(255) NULL,
`actionType` SET('insert', 'update', '*') NULL DEFAULT '*',
`role` VARCHAR(45) NULL,
PRIMARY KEY (`id`));
INSERT INTO `salix`.`fieldAcl` (`model`, `property`, `actionType`, `role`) VALUES ('Client', 'name', 'update', 'employee');
INSERT INTO `salix`.`fieldAcl` (`model`, `property`, `actionType`, `role`) VALUES ('Client', 'contact', 'update', 'employee');
INSERT INTO `salix`.`fieldAcl` (`model`, `property`, `actionType`, `role`) VALUES ('Client', 'email', 'update', 'employee');
INSERT INTO `salix`.`fieldAcl` (`model`, `property`, `actionType`, `role`) VALUES ('Client', 'phone', 'update', 'employee');
INSERT INTO `salix`.`fieldAcl` (`model`, `property`, `actionType`, `role`) VALUES ('Client', 'mobile', 'update', 'employee');
INSERT INTO `salix`.`fieldAcl` (`model`, `property`, `actionType`, `role`) VALUES ('Client', 'contactChannelFk', 'update', 'employee');
INSERT INTO `salix`.`fieldAcl` (`model`, `property`, `actionType`, `role`) VALUES ('Client', 'socialName', 'update', 'salesPerson');
INSERT INTO `salix`.`fieldAcl` (`model`, `property`, `actionType`, `role`) VALUES ('Client', 'fi', 'update', 'salesPerson');
INSERT INTO `salix`.`fieldAcl` (`model`, `property`, `actionType`, `role`) VALUES ('Client', 'street', 'update', 'salesPerson');
INSERT INTO `salix`.`fieldAcl` (`model`, `property`, `actionType`, `role`) VALUES ('Client', 'postcode', 'update', 'salesPerson');
INSERT INTO `salix`.`fieldAcl` (`model`, `property`, `actionType`, `role`) VALUES ('Client', 'city', 'update', 'salesPerson');
INSERT INTO `salix`.`fieldAcl` (`model`, `property`, `actionType`, `role`) VALUES ('Client', 'countryFk', 'update', 'salesPerson');
INSERT INTO `salix`.`fieldAcl` (`model`, `property`, `actionType`, `role`) VALUES ('Client', 'provinceFk', 'update', 'salesPerson');
INSERT INTO `salix`.`fieldAcl` (`model`, `property`, `actionType`, `role`) VALUES ('Client', 'isActive', 'update', 'salesPerson');
INSERT INTO `salix`.`fieldAcl` (`model`, `property`, `actionType`, `role`) VALUES ('Client', 'salesPersonFk', 'update', 'salesAssistant');
INSERT INTO `salix`.`fieldAcl` (`model`, `property`, `actionType`, `role`) VALUES ('Client', 'hasToInvoice', 'update', 'salesPerson');
INSERT INTO `salix`.`fieldAcl` (`model`, `property`, `actionType`, `role`) VALUES ('Client', 'isToBeMailed', 'update', 'salesPerson');
INSERT INTO `salix`.`fieldAcl` (`model`, `property`, `actionType`, `role`) VALUES ('Client', 'isEqualizated', 'update', 'salesPerson');
INSERT INTO `salix`.`fieldAcl` (`model`, `property`, `actionType`, `role`) VALUES ('Client', 'isFreezed', 'update', 'salesPerson');
INSERT INTO `salix`.`fieldAcl` (`model`, `property`, `actionType`, `role`) VALUES ('Client', 'isVies', 'update', 'salesPerson');
INSERT INTO `salix`.`fieldAcl` (`model`, `property`, `actionType`, `role`) VALUES ('Client', 'hasToInvoiceByAddress', 'update', 'salesPerson');
INSERT INTO `salix`.`fieldAcl` (`model`, `property`, `actionType`, `role`) VALUES ('Client', 'isTaxDataChecked', 'update', 'salesAssistant');
INSERT INTO `salix`.`fieldAcl` (`model`, `property`, `actionType`, `role`) VALUES ('Client', 'payMethodFk', 'update', 'salesAssistant');
INSERT INTO `salix`.`fieldAcl` (`model`, `property`, `actionType`, `role`) VALUES ('Client', 'dueDay', 'update', 'salesAssistant');
INSERT INTO `salix`.`fieldAcl` (`model`, `property`, `actionType`, `role`) VALUES ('Client', 'iban', 'update', 'salesAssistant');
INSERT INTO `salix`.`fieldAcl` (`model`, `property`, `actionType`, `role`) VALUES ('Client', 'bankEntityFk', 'update', 'salesAssistant');
INSERT INTO `salix`.`fieldAcl` (`model`, `property`, `actionType`, `role`) VALUES ('Client', 'hasLcr', 'update', 'salesAssistant');
INSERT INTO `salix`.`fieldAcl` (`model`, `property`, `actionType`, `role`) VALUES ('Client', 'hasCoreVnl', 'update', 'salesAssistant');
INSERT INTO `salix`.`fieldAcl` (`model`, `property`, `actionType`, `role`) VALUES ('Client', 'hasSepaVnl', 'update', 'salesAssistant');
INSERT INTO `salix`.`fieldAcl` (`model`, `property`, `actionType`, `role`) VALUES ('Client', 'credit', 'update', 'teamBoss');
INSERT INTO `salix`.`fieldAcl` (`model`, `property`, `actionType`, `role`) VALUES ('BankEntity', '*', 'insert', 'salesAssistant');
INSERT INTO `salix`.`fieldAcl` (`model`, `property`, `actionType`, `role`) VALUES ('Address', 'isDefaultAddress', '*', 'employee');
INSERT INTO `salix`.`fieldAcl` (`model`, `property`, `actionType`, `role`) VALUES ('Address', 'nickname', '*', 'employee');
INSERT INTO `salix`.`fieldAcl` (`model`, `property`, `actionType`, `role`) VALUES ('Address', 'postalCode', '*', 'employee');
INSERT INTO `salix`.`fieldAcl` (`model`, `property`, `actionType`, `role`) VALUES ('Address', 'provinceFk', '*', 'employee');
INSERT INTO `salix`.`fieldAcl` (`model`, `property`, `actionType`, `role`) VALUES ('Address', 'agencyModeFk', '*', 'employee');
INSERT INTO `salix`.`fieldAcl` (`model`, `property`, `actionType`, `role`) VALUES ('Address', 'phone', '*', 'employee');
INSERT INTO `salix`.`fieldAcl` (`model`, `property`, `actionType`, `role`) VALUES ('Address', 'mobile', '*', 'employee');
INSERT INTO `salix`.`fieldAcl` (`model`, `property`, `actionType`, `role`) VALUES ('Address', 'street', '*', 'employee');
INSERT INTO `salix`.`fieldAcl` (`model`, `property`, `actionType`, `role`) VALUES ('Address', 'city', '*', 'employee');
INSERT INTO `salix`.`fieldAcl` (`model`, `property`, `actionType`, `role`) VALUES ('Address', 'isActive', '*', 'employee');
INSERT INTO `salix`.`fieldAcl` (`model`, `property`, `actionType`, `role`) VALUES ('Address', 'isEqualizated', '*', 'salesAssistant');
INSERT INTO `salix`.`fieldAcl` (`model`, `property`, `actionType`, `role`) VALUES ('Address', 'clientFk', 'insert', 'employee');
INSERT INTO `salix`.`fieldAcl` (`model`, `property`, `actionType`, `role`) VALUES ('ClientObservation', '*', 'insert', 'employee');
INSERT INTO `salix`.`fieldAcl` (`model`, `property`, `actionType`, `role`) VALUES ('Recovery', '*', 'insert', 'administrative');
INSERT INTO `salix`.`fieldAcl` (`model`, `property`, `actionType`, `role`) VALUES ('Recovery', 'finished', 'update', 'administrative');
INSERT INTO `salix`.`fieldAcl` (`model`, `property`, `actionType`, `role`) VALUES ('CreditClassification', 'finished', 'update', 'creditInsurance');
INSERT INTO `salix`.`fieldAcl` (`model`, `property`, `actionType`, `role`) VALUES ('Account', '*', 'update', 'employee');
INSERT INTO `salix`.`fieldAcl` (`model`, `property`, `actionType`, `role`) VALUES ('Greuge', '*', 'insert', 'salesAssistant');
INSERT INTO `salix`.`fieldAcl` (`model`, `property`, `actionType`, `role`) VALUES ('ClientSample', '*', 'insert', 'employee');
INSERT INTO `salix`.`fieldAcl` (`model`, `property`, `actionType`, `role`) VALUES ('Item', '*', '*', 'buyer');
INSERT INTO `salix`.`fieldAcl` (`model`, `property`, `actionType`, `role`) VALUES ('Item', '*', '*', 'marketingBoss');
INSERT INTO `salix`.`fieldAcl` (`model`, `property`, `actionType`, `role`) VALUES ('ItemBotanical', '*', '*', 'buyer');
INSERT INTO `salix`.`fieldAcl` (`model`, `property`, `actionType`, `role`) VALUES ('ClaimEnd', '*', '*', 'salesAssistant');
DELETE FROM `salix`.`ACL` WHERE `id`='28';
UPDATE `salix`.`ACL` SET `accessType`='*' WHERE `id`='60';
DELETE FROM `salix`.`ACL` WHERE `id`='59';
DELETE FROM `salix`.`ACL` WHERE `id`='57';
INSERT INTO `salix`.`ACL` (`id`, `model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES (116, 'BankEntity', '*', '*', 'ALLOW', 'role', 'employee');
INSERT INTO `salix`.`ACL` (`id`, `model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES (117, 'ClientSample', '*', '*', 'ALLOW', 'role', 'employee');

View File

@ -34,5 +34,8 @@
"The new quantity should be smaller than the old one": "The new quantity should be smaller than the old one", "The new quantity should be smaller than the old one": "The new quantity should be smaller than the old one",
"Package cannot be blank": "Package cannot be blank", "Package cannot be blank": "Package cannot be blank",
"The sales of this ticket can't be modified": "The sales of this ticket can't be modified", "The sales of this ticket can't be modified": "The sales of this ticket can't be modified",
"You don't have enough privileges to do that": "You don't have enough privileges to do that" "You don't have enough privileges to do that": "You don't have enough privileges to do that",
"You don't have enough privileges to change that field": "You don't have enough privileges to change that field",
"You don't have enough privileges": "You don't have enough privileges",
"You can't make changes on a client with verified data": "You can't make changes on a client with verified data"
} }

View File

@ -51,9 +51,12 @@
"You don't have enough privileges to change that field": "No tienes permisos para cambiar ese campo", "You don't have enough privileges to change that field": "No tienes permisos para cambiar ese campo",
"Warehouse cannot be blank": "El almacén no puede quedar en blanco", "Warehouse cannot be blank": "El almacén no puede quedar en blanco",
"Agency cannot be blank": "La agencia no puede quedar en blanco", "Agency cannot be blank": "La agencia no puede quedar en blanco",
"You don't have enough privileges to do that": "No tienes permisos para para hacer esto", "You can't make changes on a client with verified data": "No puedes hacer cambios en un cliente con datos comprobados",
"This address doesn't exist": "Este consignatario no existe", "This address doesn't exist": "Este consignatario no existe",
"The sales of this ticket can't be modified": "Los movimientos de este tiquet no pueden ser modificadas", "The sales of this ticket can't be modified": "Los movimientos de este tiquet no pueden ser modificadas",
"You can't create an order for a inactive client": "You can't create an order for a inactive client", "You can't create an order for a inactive client": "You can't create an order for a inactive client",
"You can't create an order for a client that doesn't has tax data verified": "You can't create an order for a client that doesn't has tax data verified" "You can't create an order for a client that doesn't has tax data verified": "You can't create an order for a client that doesn't has tax data verified",
"You haven't enought privileges": "You haven't enought privileges",
"You don't have enought privileges": "You don't have enought privileges",
"You don't have enough privileges": "You don't have enough privileges"
} }

View File

@ -1,52 +0,0 @@
const app = require(`${servicesDir}/client/server/server`);
describe('Client updateBasicData', () => {
afterAll(async() => {
let id = 101;
let ctx = {req: {accessToken: {userId: 1}}};
let validparams = {email: 'BruceWayne@verdnatura.es'};
await app.models.Client.updateBasicData(ctx, validparams, id);
});
it('should return an error if the params aint valid', async() => {
let error;
let ctx = {req: {accessToken: {userId: 1}}};
let id = 101;
let invalidparams = {invalid: 'param for update'};
await app.models.Client.updateBasicData(ctx, invalidparams, id)
.catch(e => {
error = e;
});
expect(error.toString()).toContain(`You don't have enough privileges to do that`);
});
it('should return an error if the client has isTaxDataChecked and employee try to change his salesPerson', async() => {
let error;
let ctx = {req: {accessToken: {userId: 1}}};
let id = 101;
let params = {salesPerson: 3};
await app.models.Client.updateBasicData(ctx, params, id)
.catch(e => {
error = e;
});
expect(error.toString()).toContain(`You don't have enough privileges to do that`);
});
it('should update the client fiscal data and return the count if changes made', async() => {
let id = 101;
let client = await app.models.Client.findById(id);
expect(client.email).toEqual('BruceWayne@verdnatura.es');
let validparams = {email: 'myNewEmail@myDomain.es'};
let ctx = {req: {accessToken: {userId: 1}}};
let result = await app.models.Client.updateBasicData(ctx, validparams, id);
expect(result.email).toEqual('myNewEmail@myDomain.es');
});
});

View File

@ -1,82 +0,0 @@
const app = require(`${servicesDir}/client/server/server`);
describe('Client updateBillingData', () => {
afterAll(async() => {
let ctxOfAdmin = {req: {accessToken: {userId: 5}}};
let validparams = {
phone: 1111111111,
payMethodFk: 5,
dueDay: 0,
iban: null,
hasLcr: 0,
hasCoreVnl: 1,
hasSepaVnl: 1
};
let idWithDataChecked = 101;
await app.models.Client.updateBillingData(ctxOfAdmin, validparams, idWithDataChecked);
});
it('should return an error if the user is not administrative and the isTaxDataChecked value is true', async() => {
let error;
let ctxOfNoAdmin = {req: {accessToken: {userId: 1}}};
let params = {iban: null};
let idWithDataChecked = 101;
await app.models.Client.updateBillingData(ctxOfNoAdmin, params, idWithDataChecked)
.catch(e => {
error = e;
});
expect(error.message).toEqual(`You don't have enough privileges to do that`);
});
it('should update the billing data and check if the changes were made', async() => {
let ctxOfAdmin = {req: {accessToken: {userId: 5}}};
let params = {
phone: 2222222222,
payMethodFk: 4,
bankEntityFk: 128,
dueDay: 30,
iban: 'ES91 2100 0418 4502 0005 1332',
hasLcr: 1,
hasCoreVnl: 0,
hasSepaVnl: 0
};
let idWithDataChecked = 101;
await app.models.Client.updateBillingData(ctxOfAdmin, params, idWithDataChecked);
let client = await app.models.Client.findById(idWithDataChecked);
expect(client.phone).not.toEqual(params.phone);
expect(client.payMethodFk).toEqual(params.payMethodFk);
expect(client.dueDay).toEqual(params.dueDay);
expect(client.iban).toEqual(params.iban);
expect(client.hasLcr).toBeTruthy();
expect(client.hasCoreVnl).toBeFalsy();
expect(client.hasSepaVnl).toBeFalsy();
});
it('should return an error if the given IBAN is an invalid one', async() => {
let ctxOfAdmin = {req: {accessToken: {userId: 5}}};
let validparams = {
payMethodFk: 5,
iban: null};
let idWithDataChecked = 101;
await app.models.Client.updateBillingData(ctxOfAdmin, validparams, idWithDataChecked);
validparams = {iban: 12345};
try {
await app.models.Client.updateBillingData(ctxOfAdmin, validparams, idWithDataChecked);
} catch (error) {
expect(error.toString()).toContain('The IBAN does not have the correct format');
}
let client = await app.models.Client.findById(idWithDataChecked);
expect(client.iban).toBeFalsy();
});
});

View File

@ -21,7 +21,7 @@ describe('Client updateFiscalData', () => {
error = e; error = e;
}); });
expect(error.toString()).toContain(`You don't have enough privileges to do that`); expect(error.toString()).toContain(`You can't make changes on a client with verified data`);
}); });
it('should return an error if the user is administrative and the isTaxDataChecked value is true BUT the params aint valid', async() => { it('should return an error if the user is administrative and the isTaxDataChecked value is true BUT the params aint valid', async() => {

View File

@ -1,54 +0,0 @@
let UserError = require('../../helpers').UserError;
module.exports = Self => {
Self.remoteMethodCtx('updateBasicData', {
description: 'Updates billing data of a client',
accessType: 'WRITE',
accepts: [{
arg: 'data',
type: 'Object',
required: true,
description: 'Params to update',
http: {source: 'body'}
}, {
arg: 'id',
type: 'string',
required: true,
description: 'Model id',
http: {source: 'path'}
}],
returns: {
arg: 'data',
type: 'Worker',
root: true
},
http: {
path: `/:id/updateBasicData`,
verb: 'PATCH'
}
});
Self.updateBasicData = async(ctx, params, id) => {
let userId = ctx.req.accessToken.userId;
let validUpdateParams = [
'contact',
'name',
'email',
'phone',
'mobile',
'salesPersonFk',
'contactChannelFk'
];
let isSalesAssistant = await Self.app.models.Account.hasRole(userId, 'salesAssistant');
let client = await Self.app.models.Client.findById(id);
for (const key in params) {
if (validUpdateParams.indexOf(key) === -1 || key == 'salesPersonFk' && client.isTaxDataChecked && !isSalesAssistant)
throw new UserError(`You don't have enough privileges to do that`);
}
return await client.updateAttributes(params);
};
};

View File

@ -1,64 +0,0 @@
let UserError = require('../../helpers').UserError;
module.exports = Self => {
Self.remoteMethodCtx('updateBillingData', {
description: 'Updates billing data of a client',
accessType: 'WRITE',
accepts: [{
arg: 'data',
type: 'Object',
required: true,
description: 'Params to update',
http: {source: 'body'}
}, {
arg: 'id',
type: 'string',
required: true,
description: 'Model id',
http: {source: 'path'}
}],
returns: {
arg: 'data',
type: 'Worker',
root: true
},
http: {
path: `/:id/updateBillingData`,
verb: 'POST'
}
});
Self.updateBillingData = async(ctx, params, id) => {
let userId = ctx.req.accessToken.userId;
let data = filterAttributes(params, [
'payMethodFk',
'bankEntityFk',
'dueDay',
'iban',
'hasLcr',
'hasCoreVnl',
'hasSepaVnl']);
if (!Object.keys(data).length) return;
let isSalesAssistant = await Self.app.models.Account.hasRole(userId, 'salesAssistant');
let client = await Self.app.models.Client.findOne({where: {id: id}});
if (!isSalesAssistant)
throw new UserError(`You don't have enough privileges to do that`);
return client.updateAttributes(data);
};
function filterAttributes(params, allowed) {
let newParams = {};
Object.keys(params).forEach(attribute => {
if (allowed.indexOf(attribute) > -1)
newParams[attribute] = params[attribute];
});
return newParams;
}
};

View File

@ -30,11 +30,11 @@ module.exports = Self => {
Self.updateFiscalData = async(ctx, params, id) => { Self.updateFiscalData = async(ctx, params, id) => {
let userId = ctx.req.accessToken.userId; let userId = ctx.req.accessToken.userId;
let isAdministrative = await Self.app.models.Account.hasRole(userId, 'administrative'); let isSalesAssistant = await Self.app.models.Account.hasRole(userId, 'salesAssistant');
let [taxData] = await Self.app.models.Client.find({where: {id: id}, fields: ['isTaxDataChecked']}); let [taxData] = await Self.app.models.Client.find({where: {id: id}, fields: ['isTaxDataChecked']});
if (!isAdministrative && taxData.isTaxDataChecked) if (!isSalesAssistant && taxData.isTaxDataChecked)
throw new UserError(`You don't have enough privileges to do that`); throw new UserError(`You can't make changes on a client with verified data`);
let validUpdateParams = [ let validUpdateParams = [
'socialName', 'socialName',

View File

@ -47,19 +47,36 @@ module.exports = Self => {
* Checks if user has a role. * Checks if user has a role.
* *
* @param {Integer} userId The user id * @param {Integer} userId The user id
* @param {String} role The role name * @param {String} name The role name
* @return {Boolean} %true if user has the role, %false otherwise * @return {Boolean} %true if user has the role, %false otherwise
*/ */
Self.hasRole = async function(userId, role) { Self.hasRole = async function(userId, name) {
let roles = await Self.getRoles(userId);
return roles.find(role => {
return role.toLowerCase() == name.toLowerCase();
});
};
/**
* Get all user roles.
*
* @param {Integer} userId The user id
* @return {Object} User role list
*/
Self.getRoles = async function(userId) {
let result = await Self.rawSql( let result = await Self.rawSql(
`SELECT COUNT(*) AS roleCount `SELECT r.name
FROM account.user u FROM account.user u
JOIN account.roleRole rr ON rr.role = u.role JOIN account.roleRole rr ON rr.role = u.role
JOIN account.role r ON r.id = rr.inheritsFrom JOIN account.role r ON r.id = rr.inheritsFrom
WHERE u.id = ? WHERE u.id = ?`, [userId]);
AND r.name = ?`,
[userId, role] let roles = [];
); for (role of result) {
return result[0].roleCount > 0; roles.push(role.name);
}
return roles;
}; };
}; };

View File

@ -17,8 +17,6 @@ module.exports = Self => {
require('../methods/client/getAverageInvoiced')(Self); require('../methods/client/getAverageInvoiced')(Self);
require('../methods/client/summary')(Self); require('../methods/client/summary')(Self);
require('../methods/client/updateFiscalData')(Self); require('../methods/client/updateFiscalData')(Self);
require('../methods/client/updateBillingData')(Self);
require('../methods/client/updateBasicData')(Self);
require('../methods/client/getTransactions')(Self); require('../methods/client/getTransactions')(Self);
require('../methods/client/confirmTransaction')(Self); require('../methods/client/confirmTransaction')(Self);
@ -102,8 +100,6 @@ module.exports = Self => {
}); });
} }
// Hooks
Self.observe('before save', async function(ctx) { Self.observe('before save', async function(ctx) {
let changes = ctx.data || ctx.instance; let changes = ctx.data || ctx.instance;
let orgData = ctx.currentInstance; let orgData = ctx.currentInstance;

View File

@ -0,0 +1,27 @@
{
"name": "FieldAcl",
"base": "VnModel",
"options": {
"mysql": {
"table": "fieldAcl"
}
},
"properties": {
"id": {
"id": true,
"type": "Number"
},
"model": {
"type": "String"
},
"property":{
"type": "String"
},
"actionType":{
"type": "String"
},
"role":{
"type": "String"
}
}
}

View File

@ -1,4 +1,4 @@
module.exports = Self => { module.exports = Self => {
require('../methods/userConfig/setUserConfig')(Self); require('../methods/user-config/setUserConfig')(Self);
require('../methods/userConfig/getUserConfig')(Self); require('../methods/user-config/getUserConfig')(Self);
}; };

View File

@ -1,34 +1,20 @@
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL; const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
const UserError = require('../helpers').UserError;
module.exports = function(Self) { module.exports = function(Self) {
Self.ParameterizedSQL = ParameterizedSQL; Self.ParameterizedSQL = ParameterizedSQL;
Self.setup = function() { Self.setup = function() {
Self.super_.setup.call(this); Self.super_.setup.call(this);
/*
let disableMethods = { // Register field ACL validation
create: true, this.beforeRemote('prototype.patchAttributes', ctx => this.checkUpdateAcls(ctx));
replaceOrCreate: true, this.beforeRemote('updateAll', ctx => this.checkUpdateAcls(ctx));
patchOrCreate: true, this.beforeRemote('patchOrCreate', ctx => this.checkInsertAcls(ctx));
upsert: true, this.beforeRemote('create', ctx => this.checkInsertAcls(ctx));
updateOrCreate: true, this.beforeRemote('replaceById', ctx => this.checkInsertAcls(ctx));
exists: true, this.beforeRemote('replaceOrCreate', ctx => this.checkInsertAcls(ctx));
find: true,
findOne: true,
findById: true,
deleteById: true,
replaceById: true,
updateAttributes: false,
createChangeStream: true,
updateAll: true,
upsertWithWhere: true,
count: true
};
for (let method in disableMethods) {
// this.disableRemoteMethod(method, disableMethods[method]);
}
*/
this.remoteMethod('crud', { this.remoteMethod('crud', {
description: 'Create, update or/and delete instances from model with a single request', description: 'Create, update or/and delete instances from model with a single request',
@ -298,6 +284,72 @@ module.exports = function(Self) {
]); ]);
}; };
Self.checkAcls = async function(ctx, actionType) {
let userId = ctx.req.accessToken.userId;
let models = this.app.models;
let userRoles = await models.Account.getRoles(userId);
let data = ctx.args.data;
let modelAcls;
function modifiedProperties(data) {
let properties = [];
for (property in data) {
properties.push(property);
}
return properties;
}
modelAcls = await models.FieldAcl.find({
where: {
and: [
{model: this.modelName},
{role: {inq: userRoles}},
{property: '*'},
{or: [{actionType: '*'}, {actionType: actionType}]}
]
}
});
let allowedAll = modelAcls.find(acl => {
return acl.property == '*';
});
if (allowedAll)
return;
modelAcls = await models.FieldAcl.find({
where: {
and: [
{model: this.modelName},
{role: {inq: userRoles}},
{property: {inq: modifiedProperties(data)}},
{or: [{actionType: '*'}, {actionType: actionType}]}
]
}
});
let propsHash = {};
for (let acl of modelAcls)
propsHash[acl.property] = true;
let allowedProperties = Object.keys(data).every(property => {
return propsHash[property];
});
if (!allowedProperties)
throw new UserError(`You don't have enough privileges`);
};
Self.checkUpdateAcls = function(ctx) {
return this.checkAcls(ctx, 'update');
};
Self.checkInsertAcls = function(ctx) {
return this.checkAcls(ctx, 'insert');
};
// Action bindings // Action bindings
require('../methods/vn-model/validateBinded')(Self); require('../methods/vn-model/validateBinded')(Self);
// Handle MySql errors // Handle MySql errors

View File

@ -0,0 +1,3 @@
module.exports = Self => {
require('../methods/worker-mana/getCurrentWorkerMana')(Self);
};

View File

@ -1,3 +0,0 @@
module.exports = Self => {
require('../methods/workerMana/getCurrentWorkerMana')(Self);
};

View File

@ -15,6 +15,9 @@
"ACL": { "ACL": {
"dataSource": "salix" "dataSource": "salix"
}, },
"FieldAcl": {
"dataSource": "salix"
},
"ObservationType": { "ObservationType": {
"dataSource": "vn" "dataSource": "vn"
}, },