Create postcode form #1585
gitea/salix/dev This commit looks good Details

This commit is contained in:
Joan Sanchez 2019-07-08 14:07:09 +02:00
parent b98b534eec
commit 6f3b023644
25 changed files with 431 additions and 106 deletions

9
back/models/postcode.js Normal file
View File

@ -0,0 +1,9 @@
let UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
Self.rewriteDbError(function(err) {
if (err.code === 'ER_DUP_ENTRY')
return new UserError(`This postcode already exists`);
return err;
});
};

View File

@ -41,16 +41,17 @@
"scopes": {
"location": {
"include": [{
"relation": "postcodes"
},
{
"relation": "province",
"scope": {
"include": {
"relation": "country"
}
}
},
{
"relation": "postcodes"
}]
}],
"fields": ["id", "name", "provinceFk"]
}
}
}

View File

@ -1,4 +1,5 @@
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
VALUES
('Zone', 'editPrices', 'WRITE', 'ALLOW', 'ROLE', 'deliveryBoss'),
('Postcode', '*', 'WRITE', 'ALLOW', 'ROLE', 'employee'),
('Ticket', 'addSale', 'WRITE', 'ALLOW', 'ROLE', 'employee');

View File

@ -116,7 +116,7 @@ export default {
firstObservationDescriptionInput: 'vn-client-address-edit [name=observations] :nth-child(1) [model="observation.description"] input',
secondObservationTypeAutocomplete: 'vn-client-address-edit [name=observations] :nth-child(2) [field="observation.observationTypeFk"]',
secondObservationDescriptionInput: 'vn-client-address-edit [name=observations] :nth-child(2) [model="observation.description"] input',
addObservationButton: 'vn-client-address-edit vn-icon-button[icon="add_circle"]',
addObservationButton: 'vn-client-address-edit div[name="observations"] vn-icon-button[icon="add_circle"]',
saveButton: `${components.vnSubmit}`,
cancelCreateAddressButton: 'button[ui-sref="client.card.address.index"]',
cancelEditAddressButton: 'vn-client-address-edit > form > vn-button-bar > vn-button > button'

View File

@ -16,7 +16,7 @@
</div>
<label class="mdl-textfield__label">
<span translate>{{::$ctrl.label}}</span>
<span translate ng-show="::$ctrl.required">(*)</span>
<span translate ng-show="::$ctrl.required">*</span>
</label>
</div>
</div>

View File

@ -152,8 +152,9 @@ module.exports = function(Self) {
}
try {
await realMethod.call(this, data, options);
if (cb) cb();
const result = await realMethod.call(this, data, options);
if (cb) cb(null, result);
} catch (err) {
let myErr = replaceErr(err, replaceErrFunc);
if (cb)

View File

@ -93,5 +93,6 @@
"Extension format is invalid": "El formato de la extensión es inválido",
"Invalid parameters to create a new ticket": "Parámetros inválidos para crear un nuevo ticket",
"This item is not available": "Este artículo no está disponible",
"This postcode already exists": "Este código postal ya existe",
"Concept cannot be blank": "Concept cannot be blank"
}

View File

@ -24,9 +24,9 @@
<vn-textfield vn-one label="Street address" field="$ctrl.address.street"></vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete vn-id="postcode" vn-one
<vn-autocomplete vn-one
field="$ctrl.address.postalCode"
on-change="$ctrl.setLocation()"
selection="$ctrl.postcodeSelection"
search-function="{code: $search}"
url="/api/Postcodes/location"
fields="['code', 'townFk']"
@ -38,6 +38,11 @@
({{town.province.country.country}})
</tpl-item>
</vn-autocomplete>
<vn-icon-button vn-auto margin-medium-v
icon="add_circle"
vn-tooltip="New postcode"
ng-click="postcode.open()">
</vn-icon-button>
<vn-autocomplete vn-one
label="City"
url="/api/Towns"
@ -76,3 +81,8 @@
</button>
</vn-button-bar>
</form>
<!-- New postcode dialog -->
<vn-client-postcode vn-id="postcode"
on-response="$ctrl.onResponse(response)">
</vn-client-postcode>

View File

@ -14,18 +14,28 @@ export default class Controller {
this.address = this.data.address;
}
setLocation() {
const location = this.$.postcode.selection;
if (!location || !location.town) return;
const town = location.town;
get postcodeSelection() {
return this._postcodeSelection;
}
set postcodeSelection(selection) {
this._postcodeSelection = selection;
if (!selection) return;
const town = selection.town;
const province = town.province;
const country = province.country;
this.address.city = location.town.name;
this.address.city = town.name;
this.address.provinceFk = province.id;
this.address.countryFk = country.id;
}
onResponse(response) {
this.address.postalCode = response.code;
}
onSubmit() {
this.$.watcher.submit().then(res => {
if (res.data && this.data.isDefaultAddress)

View File

@ -49,5 +49,30 @@ describe('Client', () => {
expect(controller.$state.go).toHaveBeenCalledWith('client.card.address.index');
});
});
describe('postcodeSelection() setter', () => {
it(`should set the town, province and contry properties`, () => {
controller.postcodeSelection = {
townFk: 1,
code: 46001,
town: {
id: 1,
name: 'New York',
province: {
id: 1,
name: 'New york',
country: {
id: 2,
name: 'USA'
}
}
}
};
expect(controller.address.city).toEqual('New York');
expect(controller.address.provinceFk).toEqual(1);
expect(controller.address.countryFk).toEqual(2);
});
});
});
});

View File

@ -40,7 +40,7 @@
<vn-textfield vn-one label="Street" field="$ctrl.address.street"></vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete vn-id="postcode" vn-one
<vn-autocomplete vn-one
field="$ctrl.address.postalCode"
on-change="$ctrl.setLocation()"
search-function="{code: $search}"
@ -54,6 +54,11 @@
({{town.province.country.country}})
</tpl-item>
</vn-autocomplete>
<vn-icon-button vn-auto margin-medium-v
icon="add_circle"
vn-tooltip="New postcode"
ng-click="postcode.open()">
</vn-icon-button>
<vn-autocomplete vn-one
label="City"
url="/api/Towns"
@ -108,17 +113,23 @@
</vn-icon-button>
</vn-none>
</vn-horizontal>
<vn-icon-button
vn-bind="+"
vn-tooltip="Add note"
icon="add_circle"
ng-if="types.length > observations.length"
ng-click="model.insert()">
</vn-icon-button>
</div>
<vn-icon-button
vn-bind="+"
vn-tooltip="Add note"
icon="add_circle"
ng-if="types.length > observations.length"
ng-click="model.insert()">
</vn-icon-button>
</vn-card>
<vn-button-bar>
<vn-submit label="Save"></vn-submit>
<vn-button ng-click="$ctrl.cancel()" label="Cancel"></vn-button>
</vn-button-bar>
</form>
<!-- New postcode dialog -->
<vn-client-postcode vn-id="postcode"
on-response="$ctrl.onResponse(response)">
</vn-client-postcode>

View File

@ -20,18 +20,28 @@ export default class Controller {
this.$state.go('client.card.address.index');
}
setLocation() {
const location = this.$.postcode.selection;
if (!location || !location.town) return;
const town = location.town;
get postcodeSelection() {
return this._postcodeSelection;
}
set postcodeSelection(selection) {
this._postcodeSelection = selection;
if (!selection) return;
const town = selection.town;
const province = town.province;
const country = province.country;
this.address.city = location.town.name;
this.address.city = town.name;
this.address.provinceFk = province.id;
this.address.countryFk = country.id;
}
onResponse(response) {
this.address.postalCode = response.code;
}
onSubmit() {
this.$.watcher.check();
this.$.watcher.realSubmit()

View File

@ -55,5 +55,31 @@ describe('Client', () => {
expect(controller.$state.go).toHaveBeenCalledWith('client.card.address.index');
});
});
describe('postcodeSelection() setter', () => {
it(`should set the town, province and contry properties`, () => {
controller.address = {};
controller.postcodeSelection = {
townFk: 1,
code: 46001,
town: {
id: 1,
name: 'New York',
province: {
id: 1,
name: 'New york',
country: {
id: 2,
name: 'USA'
}
}
}
};
expect(controller.address.city).toEqual('New York');
expect(controller.address.provinceFk).toEqual(1);
expect(controller.address.countryFk).toEqual(2);
});
});
});
});

View File

@ -4,72 +4,70 @@
limit="10"
link="{clientFk: $ctrl.$stateParams.id}"
data="$ctrl.addresses"
auto-load="false">
auto-load="true">
</vn-crud-model>
<div compact>
<vn-table model="model">
<vn-card pad-large>
<vn-horizontal
ng-repeat="address in $ctrl.addresses"
class="pad-medium-top"
style="align-items: center;">
<vn-one
border-radius
class="pad-small border-solid"
ng-class="{
'item-hightlight': $ctrl.isDefaultAddress(address),
'item-disabled': !address.isActive && !$ctrl.isDefaultAddress(address)
}">
<vn-horizontal style="align-items: center;">
<vn-none pad-medium-h>
<vn-icon-button
icon="star"
ng-if="$ctrl.isDefaultAddress(address)">
</vn-icon-button>
<vn-icon-button
ng-if="!address.isActive"
icon="star_border"
vn-tooltip="Active first to set as default">
</vn-icon-button>
<vn-icon-button
ng-if="address.isActive && !$ctrl.isDefaultAddress(address)"
icon="star_border"
vn-tooltip="Set as default"
ng-click="$ctrl.setDefault(address)">
</vn-icon-button>
</vn-none>
<vn-one border-solid-right>
<vn-horizontal>
<vn-one>
<div><b>{{::address.nickname}}</b></div>
<div name="street">{{::address.street}}</div>
<div>{{::address.city}}, {{::address.province}}</div>
<div>{{::address.phone}}, {{::address.mobile}}</div>
</vn-one>
<vn-one>
<vn-check
vn-one label="Is equalizated"
field="address.isEqualizated"
disabled="true">
</vn-check>
</vn-one>
</vn-horizontal>
</vn-one>
<vn-vertical vn-one pad-medium-h>
<vn-one ng-repeat="observation in address.observations track by $index" ng-class="{'pad-small-top': $index}">
<b margin-medium-right>{{::observation.observationType.description}}:</b>
<span>{{::observation.description}}</span>
<vn-card pad-large>
<vn-horizontal
ng-repeat="address in $ctrl.addresses"
class="pad-medium-top"
style="align-items: center;">
<vn-one
border-radius
class="pad-small border-solid"
ng-class="{
'item-hightlight': $ctrl.isDefaultAddress(address),
'item-disabled': !address.isActive && !$ctrl.isDefaultAddress(address)
}">
<vn-horizontal style="align-items: center;">
<vn-none pad-medium-h>
<vn-icon-button
icon="star"
ng-if="$ctrl.isDefaultAddress(address)">
</vn-icon-button>
<vn-icon-button
ng-if="!address.isActive"
icon="star_border"
vn-tooltip="Active first to set as default">
</vn-icon-button>
<vn-icon-button
ng-if="address.isActive && !$ctrl.isDefaultAddress(address)"
icon="star_border"
vn-tooltip="Set as default"
ng-click="$ctrl.setDefault(address)">
</vn-icon-button>
</vn-none>
<vn-one border-solid-right>
<vn-horizontal>
<vn-one>
<div><b>{{::address.nickname}}</b></div>
<div name="street">{{::address.street}}</div>
<div>{{::address.city}}, {{::address.province}}</div>
<div>{{::address.phone}}, {{::address.mobile}}</div>
</vn-one>
</vn-vertical>
<a pad-medium-h vn-tooltip="Edit address"
vn-auto ui-sref="client.card.address.edit({addressId: {{::address.id}}})">
<vn-icon-button icon="edit"></vn-icon-button>
</a>
</vn-horizontal>
</vn-one>
</vn-horizontal>
</vn-table>
</vn-card>
<vn-one>
<vn-check
vn-one label="Is equalizated"
field="address.isEqualizated"
disabled="true">
</vn-check>
</vn-one>
</vn-horizontal>
</vn-one>
<vn-vertical vn-one pad-medium-h>
<vn-one ng-repeat="observation in address.observations track by $index" ng-class="{'pad-small-top': $index}">
<b margin-medium-right>{{::observation.observationType.description}}:</b>
<span>{{::observation.description}}</span>
</vn-one>
</vn-vertical>
<a pad-medium-h vn-tooltip="Edit address"
vn-auto ui-sref="client.card.address.edit({addressId: {{::address.id}}})">
<vn-icon-button icon="edit"></vn-icon-button>
</a>
</vn-horizontal>
</vn-one>
</vn-horizontal>
</vn-card>
<vn-float-button
vn-bind="+"
fixed-bottom-right

View File

@ -6,7 +6,7 @@
save="post">
</vn-watcher>
<div class="content-block">
<form name="form" ng-submit="$ctrl.onSubmit()" compact>
<form name="form" vn-http-submit="$ctrl.onSubmit()" compact>
<vn-card pad-large>
<vn-horizontal>
<vn-textfield vn-two label="Comercial Name" field="$ctrl.client.name" vn-focus></vn-textfield>
@ -33,9 +33,9 @@
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete vn-id="postcode" vn-one
<vn-autocomplete vn-one
field="$ctrl.client.postcode"
on-change="$ctrl.setLocation()"
selection="$ctrl.postcodeSelection"
search-function="{code: $search}"
url="/api/Postcodes/location"
fields="['code', 'townFk']"
@ -47,6 +47,11 @@
({{town.province.country.country}})
</tpl-item>
</vn-autocomplete>
<vn-icon-button vn-auto margin-medium-v
icon="add_circle"
vn-tooltip="New postcode"
ng-click="postcode.open()">
</vn-icon-button>
<vn-autocomplete vn-one
label="City"
url="/api/Towns"
@ -99,4 +104,9 @@
<vn-button ui-sref="client.index" label="Cancel"></vn-button>
</vn-button-bar>
</form>
</div>
<!-- New postcode dialog -->
<vn-client-postcode vn-id="postcode"
on-response="$ctrl.onResponse(response)">
</vn-client-postcode>
</div>

View File

@ -1,34 +1,46 @@
import ngModule from '../module';
export default class Controller {
constructor($scope, $state, $http) {
constructor($scope, $state, $http, $translate, vnApp) {
this.$ = $scope;
this.$state = $state;
this.$http = $http;
this.$translate = $translate;
this.vnApp = vnApp;
this.client = {
active: true
};
}
setLocation() {
const location = this.$.postcode.selection;
if (!location || !location.town) return;
const town = location.town;
get postcodeSelection() {
return this._postcodeSelection;
}
set postcodeSelection(selection) {
this._postcodeSelection = selection;
if (!selection) return;
const town = selection.town;
const province = town.province;
const country = province.country;
this.client.city = location.town.name;
this.client.city = town.name;
this.client.provinceFk = province.id;
this.client.countryFk = country.id;
}
onResponse(response) {
this.client.postcode = response.code;
}
onSubmit() {
this.$.watcher.submit().then(
return this.$.watcher.submit().then(
json => this.$state.go('client.card.basicData', {id: json.data.id})
);
}
}
Controller.$inject = ['$scope', '$state', '$http'];
Controller.$inject = ['$scope', '$state', '$http', '$translate', 'vnApp'];
ngModule.component('vnClientCreate', {
template: require('./index.html'),

View File

@ -39,5 +39,30 @@ describe('Client', () => {
expect(controller.$state.go).toHaveBeenCalledWith('client.card.basicData', {id: '1234'});
});
});
describe('postcodeSelection() setter', () => {
it(`should set the town, province and contry properties`, () => {
controller.postcodeSelection = {
townFk: 1,
code: 46001,
town: {
id: 1,
name: 'New York',
province: {
id: 1,
name: 'New york',
country: {
id: 2,
name: 'USA'
}
}
}
};
expect(controller.client.city).toEqual('New York');
expect(controller.client.provinceFk).toEqual(1);
expect(controller.client.countryFk).toEqual(2);
});
});
});
});

View File

@ -35,5 +35,6 @@ import './sample/create';
import './web-payment';
import './log';
import './sms';
import './postcode';
import './dms/index';
import './dms/create';

View File

@ -0,0 +1,47 @@
<vn-dialog class="edit"
vn-id="postcodeDialog"
on-open="$ctrl.onOpen()"
on-response="$ctrl.onResponse(response)">
<tpl-body>
<h5 pad-small-v translate>New postcode</h5>
<p translate>Please, ensure you put the correct data!</p>
<vn-horizontal>
<vn-textfield vn-one vn-focus vn-id="postcode"
label="Postcode"
model="$ctrl.data.code"
required="true">
</vn-textfield>
<vn-autocomplete vn-one
label="City"
url="/api/Towns/location"
show-field="name"
value-field="id"
field="$ctrl.data.townFk"
selection="$ctrl.townSelection"
required="true">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete vn-one
disabled="true"
field="$ctrl.data.provinceFk"
url="/api/Provinces"
show-field="name"
value-field="id"
label="Province">
</vn-autocomplete>
<vn-autocomplete vn-one
disabled="true"
field="$ctrl.data.countryFk"
url="/api/Countries"
show-field="country"
value-field="id"
label="Country">
</vn-autocomplete>
</vn-horizontal>
</tpl-body>
<tpl-buttons>
<input type="button" response="CANCEL" translate-attr="{value: 'Cancel'}"/>
<button response="ACCEPT" translate>Save</button>
</tpl-buttons>
</vn-dialog>

View File

@ -0,0 +1,70 @@
import ngModule from '../module';
import Component from 'core/lib/component';
import './style.scss';
class Controller extends Component {
constructor($element, $scope, $http, $translate, vnApp) {
super($element, $scope);
this.$ = $scope;
this.$http = $http;
this.$translate = $translate;
this.vnApp = vnApp;
}
get townSelection() {
return this._townSelection;
}
set townSelection(selection) {
this._townSelection = selection;
if (!selection) return;
const province = selection.province;
const country = province.country;
this.data.provinceFk = province.id;
this.data.countryFk = country.id;
}
open() {
this.$.postcodeDialog.show();
}
onOpen() {
this.$.postcode.focus();
}
onResponse(response) {
if (response == 'ACCEPT') {
try {
if (!this.data.code)
throw new Error(`The postcode can't be empty`);
if (!this.data.townFk)
throw new Error(`The town can't be empty`);
this.$http.patch(`/api/postcodes`, this.data).then(response => {
if (response.data) {
this.vnApp.showMessage(this.$translate.instant('The postcode has been saved'));
this.emit('response', {response: response.data});
}
});
} catch (e) {
this.vnApp.showError(this.$translate.instant(e.message));
return false;
}
}
return true;
}
}
Controller.$inject = ['$element', '$scope', '$http', '$translate', 'vnApp'];
ngModule.component('vnClientPostcode', {
template: require('./index.html'),
controller: Controller,
bindings: {
data: '<',
}
});

View File

@ -0,0 +1,34 @@
import './index';
describe('Client', () => {
describe('Component vnClientPostcode', () => {
let controller;
let $httpBackend;
let $element;
beforeEach(ngModule('client'));
beforeEach(angular.mock.inject(($componentController, _$httpBackend_) => {
$httpBackend = _$httpBackend_;
$element = angular.element('<vn-dialog></vn-dialog>');
controller = $componentController('vnClientPostcode', {$element});
controller.client = {id: 101};
}));
describe('onResponse()', () => {
it('should perform a POST query and show a success snackbar', () => {
let params = {townFk: 1, provinceFk: 1, countryFk: 1, code: '46460'};
controller.data = {townFk: 1, provinceFk: 1, countryFk: 1, code: '46460'};
spyOn(controller.vnApp, 'showMessage');
$httpBackend.when('PATCH', `/api/postcodes`, params).respond(200, params);
$httpBackend.expect('PATCH', `/api/postcodes`, params).respond(params);
controller.onResponse('ACCEPT');
$httpBackend.flush();
expect(controller.vnApp.showMessage).toHaveBeenCalledWith('The postcode has been saved');
});
});
});
});

View File

@ -0,0 +1,5 @@
New postcode: Nuevo código postal
Please, ensure you put the correct data!: ¡Por favor, asegúrate de poner los datos correctos!
The postcode can't be empty: El código postal no puede quedar vacío
The town can't be empty: La población no puede quedar vacía
The postcode has been saved: El código postal ha sido guardado

View File

@ -0,0 +1,9 @@
@import "variables";
vn-client-postcode {
vn-dialog {
p {
color: $color-alert
}
}
}

View File

@ -65,14 +65,19 @@ module.exports = Self => {
fields: ['botanical', 'countryFk', 'taxClassFk']
}, options);
const promises = [];
for (tax of originalTaxes) {
tax.itemFk = newId;
await models.ItemTaxCountry.upsertWithWhere({
const newTax = models.ItemTaxCountry.upsertWithWhere({
itemFk: newId,
countryFk: tax.countryFk,
}, tax, options);
promises.push(newTax);
}
await Promise.all(promises);
}
/**
@ -110,10 +115,14 @@ module.exports = Self => {
fields: ['tagFk', 'value', 'priority']
}, options);
const promises = [];
for (tag of originalTags) {
tag.itemFk = newId;
const newItemTag = models.ItemTag.create(tag, options);
await models.ItemTag.create(tag, options);
promises.push(newItemTag);
}
await Promise.all(promises);
}
};

View File

@ -43,7 +43,7 @@ module.exports = Self => {
const ticket = await models.Ticket.findById(id);
const shouldRefresh = false;
const [[stock]] = await Self.rawSql(`CALL vn.getItemVisibleAvailable(?, ?, ?, ?)`, [
const [[stock]] = await Self.rawSql(`CALL vn.itemGetVisibleAvailable(?, ?, ?, ?)`, [
itemId,
ticket.shipped,
ticket.warehouseFk,