Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix into 5472-user_passExpired
This commit is contained in:
commit
48be8b9fe0
|
@ -52,6 +52,7 @@ pipeline {
|
|||
}}}
|
||||
environment {
|
||||
NODE_ENV = ""
|
||||
TZ = 'Europe/Madrid'
|
||||
}
|
||||
parallel {
|
||||
stage('Frontend') {
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
name: account
|
||||
columns:
|
||||
id: id
|
||||
name: name
|
||||
roleFk: role
|
||||
nickname: nickname
|
||||
lang: lang
|
||||
password: password
|
||||
bcryptPassword: bcrypt password
|
||||
active: active
|
||||
email: email
|
||||
emailVerified: email verified
|
||||
created: created
|
||||
updated: updated
|
||||
image: image
|
||||
hasGrant: has grant
|
||||
userFk: user
|
|
@ -0,0 +1,17 @@
|
|||
name: cuenta
|
||||
columns:
|
||||
id: id
|
||||
name: nombre
|
||||
roleFk: rol
|
||||
nickname: apodo
|
||||
lang: idioma
|
||||
password: contraseña
|
||||
bcryptPassword: contraseña bcrypt
|
||||
active: activo
|
||||
email: email
|
||||
emailVerified: email verificado
|
||||
created: creado
|
||||
updated: actualizado
|
||||
image: imagen
|
||||
hasGrant: tiene permiso
|
||||
userFk: usuario
|
|
@ -218,6 +218,23 @@ let actions = {
|
|||
return handle.jsonValue();
|
||||
},
|
||||
|
||||
getValue: async function(selector) {
|
||||
return await this.waitToGetProperty(selector, 'value');
|
||||
},
|
||||
|
||||
getValues: async function(selectorMap) {
|
||||
const values = {};
|
||||
for (const key in selectorMap)
|
||||
values[key] = await this.waitToGetProperty(selectorMap[key], 'value');
|
||||
return values;
|
||||
},
|
||||
|
||||
innerText: async function(selector) {
|
||||
const element = await this.$(selector);
|
||||
const handle = await element.getProperty('innerText');
|
||||
return handle.jsonValue();
|
||||
},
|
||||
|
||||
waitPropertyLength: async function(selector, property, minLength) {
|
||||
await this.waitForFunction((selector, property, minLength) => {
|
||||
const element = document.querySelector(selector);
|
||||
|
|
|
@ -283,12 +283,6 @@ export default {
|
|||
cancelEditAddressButton: 'vn-client-address-edit > form > vn-button-bar > vn-button > button',
|
||||
watcher: 'vn-client-address-edit vn-watcher'
|
||||
},
|
||||
clientWebAccess: {
|
||||
enableWebAccessCheckbox: 'vn-check[label="Enable web access"]',
|
||||
userName: 'vn-client-web-access vn-textfield[ng-model="$ctrl.account.name"]',
|
||||
email: 'vn-client-web-access vn-textfield[ng-model="$ctrl.account.email"]',
|
||||
saveButton: 'button[type=submit]'
|
||||
},
|
||||
clientNotes: {
|
||||
addNoteFloatButton: 'vn-float-button',
|
||||
note: 'vn-textarea[ng-model="$ctrl.note.text"]',
|
||||
|
@ -312,15 +306,6 @@ export default {
|
|||
clientMandate: {
|
||||
firstMandateText: 'vn-client-mandate vn-card vn-table vn-tbody > vn-tr'
|
||||
},
|
||||
clientLog: {
|
||||
lastModificationPreviousValue: 'vn-client-log vn-tr table tr td.before',
|
||||
lastModificationCurrentValue: 'vn-client-log vn-tr table tr td.after',
|
||||
namePreviousValue: 'vn-client-log vn-tr table tr:nth-child(1) td.before',
|
||||
nameCurrentValue: 'vn-client-log vn-tr table tr:nth-child(1) td.after',
|
||||
activePreviousValue: 'vn-client-log vn-tr:nth-child(2) table tr:nth-child(2) td.before',
|
||||
activeCurrentValue: 'vn-client-log vn-tr:nth-child(2) table tr:nth-child(2) td.after'
|
||||
|
||||
},
|
||||
clientBalance: {
|
||||
company: 'vn-client-balance-index vn-autocomplete[ng-model="$ctrl.companyId"]',
|
||||
newPaymentButton: `vn-float-button`,
|
||||
|
@ -1361,18 +1346,6 @@ export default {
|
|||
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"]',
|
||||
},
|
||||
supplierBillingData: {
|
||||
payMethod: 'vn-supplier-billing-data vn-autocomplete[ng-model="$ctrl.supplier.payMethodFk"]',
|
||||
payDem: 'vn-supplier-billing-data vn-autocomplete[ng-model="$ctrl.supplier.payDemFk"]',
|
||||
|
|
|
@ -1,88 +1,56 @@
|
|||
/* eslint max-len: ["error", { "code": 150 }]*/
|
||||
import selectors from '../../helpers/selectors';
|
||||
import getBrowser from '../../helpers/puppeteer';
|
||||
|
||||
describe('Client Edit web access path', () => {
|
||||
const $ = {
|
||||
enableWebAccess: 'vn-client-web-access vn-check[label="Enable web access"]',
|
||||
userName: 'vn-client-web-access vn-textfield[ng-model="$ctrl.account.name"]',
|
||||
email: 'vn-client-web-access vn-textfield[ng-model="$ctrl.account.email"]',
|
||||
saveButton: 'vn-client-web-access button[type=submit]',
|
||||
nameValue: 'vn-client-log .change:nth-child(1) .basic-json:nth-child(1) vn-json-value',
|
||||
activeValue: 'vn-client-log .change:nth-child(2) .basic-json:nth-child(2) vn-json-value'
|
||||
};
|
||||
|
||||
describe('Client web access path', () => {
|
||||
let browser;
|
||||
let page;
|
||||
|
||||
beforeAll(async() => {
|
||||
browser = await getBrowser();
|
||||
page = browser.page;
|
||||
await page.loginAndModule('salesPerson', 'client');
|
||||
await page.accessToSearchResult('max');
|
||||
await page.accessToSection('client.card.webAccess');
|
||||
});
|
||||
|
||||
afterAll(async() => {
|
||||
await browser.close();
|
||||
});
|
||||
|
||||
it('should uncheck the Enable web access checkbox', async() => {
|
||||
await page.waitToClick(selectors.clientWebAccess.enableWebAccessCheckbox);
|
||||
await page.waitToClick(selectors.clientWebAccess.saveButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
it('should modify and save web access attributes', async() => {
|
||||
await page.accessToSection('client.card.webAccess');
|
||||
await page.click($.enableWebAccess);
|
||||
await page.click($.saveButton);
|
||||
const enableMessage = await page.waitForSnackbar();
|
||||
await page.overwrite($.userName, 'Legion');
|
||||
await page.overwrite($.email, 'legion@marvel.com');
|
||||
await page.click($.saveButton);
|
||||
const modifyMessage = await page.waitForSnackbar();
|
||||
|
||||
expect(message.text).toContain('Data saved!');
|
||||
});
|
||||
|
||||
it(`should update the name`, async() => {
|
||||
await page.clearInput(selectors.clientWebAccess.userName);
|
||||
await page.write(selectors.clientWebAccess.userName, 'Legion');
|
||||
await page.waitToClick(selectors.clientWebAccess.saveButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.text).toContain('Data saved!');
|
||||
});
|
||||
|
||||
it(`should update the email`, async() => {
|
||||
await page.clearInput(selectors.clientWebAccess.email);
|
||||
await page.write(selectors.clientWebAccess.email, 'legion@marvel.com');
|
||||
await page.waitToClick(selectors.clientWebAccess.saveButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.text).toContain('Data saved!');
|
||||
});
|
||||
|
||||
it('should reload the section and confirm web access is now unchecked', async() => {
|
||||
await page.reloadSection('client.card.webAccess');
|
||||
const result = await page.checkboxState(selectors.clientWebAccess.enableWebAccessCheckbox);
|
||||
const hasAccess = await page.checkboxState($.enableWebAccess);
|
||||
const userName = await page.getValue($.userName);
|
||||
const email = await page.getValue($.email);
|
||||
|
||||
expect(result).toBe('unchecked');
|
||||
});
|
||||
|
||||
it('should confirm web access name have been updated', async() => {
|
||||
const result = await page.waitToGetProperty(selectors.clientWebAccess.userName, 'value');
|
||||
|
||||
expect(result).toEqual('Legion');
|
||||
});
|
||||
|
||||
it('should confirm web access email have been updated', async() => {
|
||||
const result = await page.waitToGetProperty(selectors.clientWebAccess.email, 'value');
|
||||
|
||||
expect(result).toEqual('legion@marvel.com');
|
||||
});
|
||||
|
||||
it(`should navigate to the log section`, async() => {
|
||||
await page.accessToSection('client.card.log');
|
||||
});
|
||||
const logName = await page.innerText($.nameValue);
|
||||
const logActive = await page.innerText($.activeValue);
|
||||
|
||||
it(`should confirm the last log shows the updated client name and no modifications on active checkbox`, async() => {
|
||||
let namePreviousValue = await page
|
||||
.waitToGetProperty(selectors.clientLog.namePreviousValue, 'innerText');
|
||||
let nameCurrentValue = await page
|
||||
.waitToGetProperty(selectors.clientLog.nameCurrentValue, 'innerText');
|
||||
expect(enableMessage.type).toBe('success');
|
||||
expect(modifyMessage.type).toBe('success');
|
||||
|
||||
expect(namePreviousValue).toEqual('MaxEisenhardt');
|
||||
expect(nameCurrentValue).toEqual('Legion');
|
||||
});
|
||||
expect(hasAccess).toBe('unchecked');
|
||||
expect(userName).toEqual('Legion');
|
||||
expect(email).toEqual('legion@marvel.com');
|
||||
|
||||
it(`should confirm the penultimate log shows the updated active and no modifications on client name`, async() => {
|
||||
let activePreviousValue = await page
|
||||
.waitToGetProperty(selectors.clientLog.activePreviousValue, 'innerText');
|
||||
let activeCurrentValue = await page
|
||||
.waitToGetProperty(selectors.clientLog.activeCurrentValue, 'innerText');
|
||||
|
||||
expect(activePreviousValue).toEqual('✓');
|
||||
expect(activeCurrentValue).toEqual('✗');
|
||||
expect(logName).toEqual('Legion');
|
||||
expect(logActive).toEqual('✗');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,7 +1,21 @@
|
|||
import selectors from '../../helpers/selectors.js';
|
||||
import getBrowser from '../../helpers/puppeteer';
|
||||
|
||||
describe('Supplier fiscal data path', () => {
|
||||
const $ = {
|
||||
saveButton: 'vn-supplier-fiscal-data button[type="submit"]',
|
||||
};
|
||||
const $inputs = {
|
||||
province: 'vn-supplier-fiscal-data [name="province"]',
|
||||
country: 'vn-supplier-fiscal-data [name="country"]',
|
||||
postcode: 'vn-supplier-fiscal-data [name="postcode"]',
|
||||
city: 'vn-supplier-fiscal-data [name="city"]',
|
||||
socialName: 'vn-supplier-fiscal-data [name="socialName"]',
|
||||
taxNumber: 'vn-supplier-fiscal-data [name="taxNumber"]',
|
||||
account: 'vn-supplier-fiscal-data [name="account"]',
|
||||
sageWithholding: 'vn-supplier-fiscal-data [ng-model="$ctrl.supplier.sageWithholdingFk"]',
|
||||
sageTaxType: 'vn-supplier-fiscal-data [ng-model="$ctrl.supplier.sageTaxTypeFk"]'
|
||||
};
|
||||
|
||||
fdescribe('Supplier fiscal data path', () => {
|
||||
let browser;
|
||||
let page;
|
||||
|
||||
|
@ -10,102 +24,44 @@ describe('Supplier fiscal data path', () => {
|
|||
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.waitForTimeout(1000); // must repeat this action twice or fails. also #2699 may be a cool solution to this.
|
||||
await page.clearInput(selectors.supplierFiscalData.city);
|
||||
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, 'Wrong tax number');
|
||||
await page.clearInput(selectors.supplierFiscalData.account);
|
||||
await page.write(selectors.supplierFiscalData.account, '0123456789');
|
||||
await page.autocompleteSearch(selectors.supplierFiscalData.sageWihholding, 'retencion estimacion objetiva');
|
||||
await page.autocompleteSearch(selectors.supplierFiscalData.sageTaxType, 'operaciones no sujetas');
|
||||
it('should attempt to edit the fiscal data and check data is saved', async() => {
|
||||
await page.accessToSection('supplier.card.fiscalData');
|
||||
await page.clearInput($inputs.province);
|
||||
await page.clearInput($inputs.country);
|
||||
await page.clearInput($inputs.postcode);
|
||||
await page.overwrite($inputs.city, 'Valencia');
|
||||
await page.overwrite($inputs.socialName, 'Farmer King SL');
|
||||
await page.overwrite($inputs.taxNumber, 'Wrong tax number');
|
||||
await page.overwrite($inputs.account, '0123456789');
|
||||
await page.autocompleteSearch($inputs.sageWithholding, 'retencion estimacion objetiva');
|
||||
await page.autocompleteSearch($inputs.sageTaxType, 'operaciones no sujetas');
|
||||
await page.click($.saveButton);
|
||||
const errorMessage = await page.waitForSnackbar();
|
||||
await page.overwrite($inputs.taxNumber, '12345678Z');
|
||||
await page.click($.saveButton);
|
||||
const successMessage = await page.waitForSnackbar();
|
||||
|
||||
await page.waitToClick(selectors.supplierFiscalData.saveButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.text).toContain('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).toContain('Data saved!');
|
||||
});
|
||||
|
||||
it('should reload the section', async() => {
|
||||
await page.reloadSection('supplier.card.fiscalData');
|
||||
});
|
||||
const values = await page.getValues($inputs);
|
||||
|
||||
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('0123456789');
|
||||
});
|
||||
|
||||
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');
|
||||
expect(errorMessage.text).toContain('Invalid Tax number');
|
||||
expect(successMessage.type).toBe('success');
|
||||
expect(values).toEqual({
|
||||
province: 'Province one (España)',
|
||||
country: 'España',
|
||||
postcode: '46000',
|
||||
city: 'Valencia',
|
||||
socialName: 'Farmer King SL',
|
||||
taxNumber: '12345678Z',
|
||||
account: '0123456789',
|
||||
sageWithholding: 'RETENCION ESTIMACION OBJETIVA',
|
||||
sageTaxType: 'Operaciones no sujetas'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -166,7 +166,7 @@ export default class Field extends FormInput {
|
|||
if (event.defaultPrevented) return;
|
||||
event.preventDefault();
|
||||
this.field = null;
|
||||
this.input.dispatchEvent(new Event('change'));
|
||||
this.element.dispatchEvent(new Event('change'));
|
||||
}
|
||||
|
||||
buildInput(type) {
|
||||
|
|
|
@ -32,6 +32,7 @@ import './float-button';
|
|||
import './icon-menu';
|
||||
import './icon-button';
|
||||
import './input-number';
|
||||
import './json-value';
|
||||
import './label-value';
|
||||
import './range';
|
||||
import './input-time';
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
import ngModule from '../../module';
|
||||
import Component from 'core/lib/component';
|
||||
import './style.scss';
|
||||
|
||||
const maxStrLen = 50;
|
||||
|
||||
/**
|
||||
* Displays pretty JSON value.
|
||||
*
|
||||
* @property {*} value The value
|
||||
*/
|
||||
export default class Controller extends Component {
|
||||
get value() {
|
||||
return this._value;
|
||||
}
|
||||
|
||||
set value(value) {
|
||||
const wasEmpty = this._value === undefined;
|
||||
this._value = value;
|
||||
|
||||
let text;
|
||||
let cssClass;
|
||||
const type = typeof value;
|
||||
|
||||
if (value == null) {
|
||||
text = '∅';
|
||||
cssClass = 'null';
|
||||
} else {
|
||||
cssClass = type;
|
||||
switch (type) {
|
||||
case 'boolean':
|
||||
text = value ? '✓' : '✗';
|
||||
cssClass = value ? 'true' : 'false';
|
||||
break;
|
||||
case 'string':
|
||||
text = value.length <= maxStrLen
|
||||
? value
|
||||
: value.substring(0, maxStrLen) + '...';
|
||||
break;
|
||||
case 'object':
|
||||
if (value instanceof Date) {
|
||||
const hasZeroTime =
|
||||
value.getHours() === 0 &&
|
||||
value.getMinutes() === 0 &&
|
||||
value.getSeconds() === 0;
|
||||
const format = hasZeroTime ? 'dd/MM/yyyy' : 'dd/MM/yyyy HH:mm:ss';
|
||||
text = this.$filter('date')(value, format);
|
||||
} else
|
||||
text = value;
|
||||
break;
|
||||
default:
|
||||
text = value;
|
||||
}
|
||||
}
|
||||
|
||||
const el = this.element;
|
||||
el.textContent = text;
|
||||
el.title = type == 'string' && value.length > maxStrLen ? value : '';
|
||||
|
||||
cssClass = `json-${cssClass}`;
|
||||
if (wasEmpty)
|
||||
el.classList.add(cssClass);
|
||||
else
|
||||
el.classList.replace(this.className, cssClass);
|
||||
}
|
||||
}
|
||||
|
||||
ngModule.vnComponent('vnJsonValue', {
|
||||
controller: Controller,
|
||||
bindings: {
|
||||
value: '<?'
|
||||
}
|
||||
});
|
|
@ -0,0 +1,79 @@
|
|||
import './index';
|
||||
|
||||
describe('Salix Component vnLog', () => {
|
||||
let controller;
|
||||
let $scope;
|
||||
let $element;
|
||||
let el;
|
||||
|
||||
beforeEach(ngModule('vnCore'));
|
||||
|
||||
beforeEach(inject(($componentController, $rootScope) => {
|
||||
$scope = $rootScope.$new();
|
||||
$element = angular.element('<json-value></json-value>');
|
||||
controller = $componentController('vnJsonValue', {$element, $scope});
|
||||
el = controller.element;
|
||||
}));
|
||||
|
||||
describe('set value()', () => {
|
||||
it('should display null symbol when value is null equivalent', () => {
|
||||
controller.value = null;
|
||||
|
||||
expect(el.textContent).toEqual('∅');
|
||||
expect(el.className).toContain('json-null');
|
||||
});
|
||||
|
||||
it('should display ballot when value is false', () => {
|
||||
controller.value = false;
|
||||
|
||||
expect(el.textContent).toEqual('✗');
|
||||
expect(el.className).toContain('json-false');
|
||||
});
|
||||
|
||||
it('should display check when value is true', () => {
|
||||
controller.value = true;
|
||||
|
||||
expect(el.textContent).toEqual('✓');
|
||||
expect(el.className).toContain('json-true');
|
||||
});
|
||||
|
||||
it('should display string when value is an string', () => {
|
||||
controller.value = 'Foo';
|
||||
|
||||
expect(el.textContent).toEqual('Foo');
|
||||
expect(el.className).toContain('json-string');
|
||||
});
|
||||
|
||||
it('should display only date when value is date with time set to zero', () => {
|
||||
const date = Date.vnNew();
|
||||
date.setHours(0, 0, 0, 0);
|
||||
controller.value = date;
|
||||
|
||||
expect(el.textContent).toEqual('01/01/2001');
|
||||
expect(el.className).toContain('json-object');
|
||||
});
|
||||
|
||||
it('should display full date without time when value is date with time', () => {
|
||||
const date = Date.vnNew();
|
||||
date.setHours(15, 45);
|
||||
controller.value = date;
|
||||
|
||||
expect(el.textContent).toEqual('01/01/2001 15:45:00');
|
||||
expect(el.className).toContain('json-object');
|
||||
});
|
||||
|
||||
it('should display object when value is an object', () => {
|
||||
controller.value = {foo: 'bar'};
|
||||
|
||||
expect(el.textContent).toEqual('[object Object]');
|
||||
expect(el.className).toContain('json-object');
|
||||
});
|
||||
|
||||
it('should display number when value is a number', () => {
|
||||
controller.value = 2050;
|
||||
|
||||
expect(el.textContent).toEqual('2050');
|
||||
expect(el.className).toContain('json-number');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,23 @@
|
|||
vn-json-value {
|
||||
display: inline;
|
||||
|
||||
&.json-string {
|
||||
color: #d172cc;
|
||||
}
|
||||
&.json-object {
|
||||
color: #d1a572;
|
||||
}
|
||||
&.json-number {
|
||||
color: #85d0ff;
|
||||
}
|
||||
&.json-true {
|
||||
color: #7dc489;
|
||||
}
|
||||
&.json-false {
|
||||
color: #c74949;
|
||||
}
|
||||
&.json-null {
|
||||
color: #cd7c7c;
|
||||
font-style: italic;
|
||||
}
|
||||
}
|
|
@ -41,10 +41,15 @@ vn-table {
|
|||
display: table-row;
|
||||
height: 48px;
|
||||
}
|
||||
vn-thead, .vn-thead,
|
||||
vn-tbody, .vn-tbody,
|
||||
vn-tfoot, .vn-tfoot,
|
||||
thead, tbody, tfoot {
|
||||
& > thead,
|
||||
& > tbody,
|
||||
& > tfoot,
|
||||
& > vn-thead,
|
||||
& > vn-tbody,
|
||||
& > vn-tfoot,
|
||||
& > .vn-thead,
|
||||
& > .vn-tbody,
|
||||
& > .vn-tfoot {
|
||||
& > * {
|
||||
display: table-row;
|
||||
|
||||
|
@ -111,14 +116,14 @@ vn-table {
|
|||
color: inherit;
|
||||
}
|
||||
}
|
||||
a.vn-tbody {
|
||||
& > a.vn-tbody {
|
||||
&.clickable {
|
||||
@extend %clickable;
|
||||
}
|
||||
}
|
||||
vn-tbody > *,
|
||||
.vn-tbody > *,
|
||||
tbody > * {
|
||||
& > vn-tbody > *,
|
||||
& > .vn-tbody > *,
|
||||
& > tbody > * {
|
||||
border-bottom: $border-thin;
|
||||
|
||||
&:last-child {
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
$font-size: 11pt;
|
||||
$menu-width: 256px;
|
||||
$right-menu-width: 318px;
|
||||
$topbar-height: 56px;
|
||||
$mobile-width: 800px;
|
||||
$float-spacing: 20px;
|
||||
|
|
|
@ -88,13 +88,13 @@ vn-layout {
|
|||
}
|
||||
&.right-menu {
|
||||
& > vn-topbar > .end {
|
||||
width: 80px + $right-menu-width;
|
||||
width: 80px + $menu-width;
|
||||
}
|
||||
& > .main-view {
|
||||
padding-right: $right-menu-width;
|
||||
padding-right: $menu-width;
|
||||
}
|
||||
[fixed-bottom-right] {
|
||||
right: $right-menu-width;
|
||||
right: $menu-width;
|
||||
}
|
||||
}
|
||||
& > .main-view {
|
||||
|
|
|
@ -3,70 +3,211 @@
|
|||
url="{{$ctrl.url}}"
|
||||
filter="$ctrl.filter"
|
||||
link="{originFk: $ctrl.originId}"
|
||||
where="{changedModel: $ctrl.changedModel,
|
||||
changedModelId: $ctrl.changedModelId}"
|
||||
where="{changedModel: $ctrl.changedModel, changedModelId: $ctrl.changedModelId}"
|
||||
data="$ctrl.logs"
|
||||
limit="20"
|
||||
order="creationDate DESC, id DESC"
|
||||
limit="20">
|
||||
</vn-crud-model>
|
||||
<vn-crud-model
|
||||
url="{{$ctrl.url}}/{{$ctrl.originId}}/models"
|
||||
data="models"
|
||||
order="changedModel"
|
||||
auto-load="true">
|
||||
</vn-crud-model>
|
||||
<vn-data-viewer model="model" class="vn-w-xl">
|
||||
<vn-card>
|
||||
<vn-table model="model">
|
||||
<vn-thead>
|
||||
<vn-tr>
|
||||
<vn-th field="creationDate">Date</vn-th>
|
||||
<vn-th field="userFk" shrink>User</vn-th>
|
||||
<vn-th field="changedModel" ng-if="$ctrl.showModelName" shrink>Model</vn-th>
|
||||
<vn-th field="action" shrink>Action</vn-th>
|
||||
<vn-th field="changedModelValue" ng-if="$ctrl.showModelName">Name</vn-th>
|
||||
<vn-th expand>Changes</vn-th>
|
||||
</vn-tr>
|
||||
</vn-thead>
|
||||
<vn-tbody>
|
||||
<vn-tr ng-repeat="log in $ctrl.logs">
|
||||
<vn-td shrink-datetime>
|
||||
{{::log.creationDate | date:'dd/MM/yyyy HH:mm'}}
|
||||
</vn-td>
|
||||
<vn-td>
|
||||
<span ng-class="{'link': log.user.worker.id, 'value': !log.user.worker.id}"
|
||||
ng-click="$ctrl.showWorkerDescriptor($event, log.user.worker.id)"
|
||||
translate>{{::log.user.name || 'System' | translate}}
|
||||
<vn-data-viewer model="model" class="vn-w-md vn-px-sm">
|
||||
<div class="change vn-mb-sm" ng-repeat="log in $ctrl.logs">
|
||||
<div class="user-wrapper">
|
||||
<div
|
||||
class="user vn-mt-xs"
|
||||
title="{{::log.user.nickname || 'System'}}">
|
||||
<img
|
||||
ng-if="::log.user.worker"
|
||||
src="/api/Images/user/160x160/{{::log.user.worker.id}}/download?access_token={{::$ctrl.vnToken.token}}"
|
||||
ng-click="$ctrl.showWorkerDescriptor($event, log.user.worker.id)">
|
||||
</img>
|
||||
<div
|
||||
ng-if="::!log.user.worker"
|
||||
ng-class="::{system: !log.user}"
|
||||
ng-style="::$ctrl.userBgColor(log.user)"
|
||||
class="user-icon">
|
||||
{{::log.user ? log.user.name.charAt(0).toUpperCase() : 'S'}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="arrow bg-panel"></div>
|
||||
<div class="line"></div>
|
||||
</div>
|
||||
<vn-card class="detail vn-pa-sm">
|
||||
<div class="header vn-mb-sm">
|
||||
<div
|
||||
class="date text-secondary text-caption"
|
||||
title="{{::log.creationDate | date:'dd/MM/yyyy HH:mm'}}">
|
||||
{{::$ctrl.relativeDate(log.creationDate)}}
|
||||
</div>
|
||||
<span class="chip {{::$ctrl.actionsClass[log.action]}}" translate>
|
||||
{{::$ctrl.actionsText[log.action]}}
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="model vn-mb-sm"
|
||||
title="{{::log.changedModelValue}}"
|
||||
ng-if="::log.changedModel || log.changedModelValue">
|
||||
<span class="model-name"
|
||||
ng-if="::$ctrl.showModelName"
|
||||
title="{{::log.changedModel}}">
|
||||
{{::log.changedModelI18n}}
|
||||
</span>
|
||||
<span class="model-id"
|
||||
ng-if="::log.changedModelId">
|
||||
#{{::log.changedModelId}}
|
||||
</span>
|
||||
<span class="model-value">
|
||||
{{::log.changedModelValue}}
|
||||
</span>
|
||||
</div>
|
||||
<div class="changes {{::log.props.length ? 'props' : 'no-props'}}" vn-id="changes">
|
||||
<vn-icon icon="visibility"
|
||||
class="expand-button"
|
||||
ng-click="$ctrl.toggleAttributes(log, changes, true)">
|
||||
</vn-icon>
|
||||
<vn-icon icon="visibility_off"
|
||||
class="shrink-button"
|
||||
ng-click="$ctrl.toggleAttributes(log, changes, false)">
|
||||
</vn-icon>
|
||||
<div class="changes-wrapper">
|
||||
<span ng-if="::log.props.length"
|
||||
class="attributes">
|
||||
<span ng-if="!log.expand" ng-repeat="prop in ::log.props"
|
||||
class="basic-json">
|
||||
<span class="json-field"
|
||||
title="{{::prop.name}}">
|
||||
{{::prop.nameI18n}}:
|
||||
</span>
|
||||
<vn-json-value value="::$ctrl.mainVal(prop, log.action)"></vn-json-value><span ng-if="::!$last">,</span>
|
||||
</span>
|
||||
</vn-td>
|
||||
<vn-td ng-if="$ctrl.showModelName">
|
||||
{{::log.changedModel}}
|
||||
</vn-td>
|
||||
<vn-td shrink translate>
|
||||
{{::$ctrl.actionsText[log.action]}}
|
||||
</vn-td>
|
||||
<vn-td ng-if="$ctrl.showModelName">
|
||||
{{::log.changedModelValue}}
|
||||
</vn-td>
|
||||
<vn-td expand>
|
||||
<table class="attributes">
|
||||
<thead>
|
||||
<tr>
|
||||
<th translate class="field">Field</th>
|
||||
<th translate>Before</th>
|
||||
<th translate>After</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="prop in ::log.props">
|
||||
<td class="field">{{prop.name}}</td>
|
||||
<td class="before">{{prop.old}}</td>
|
||||
<td class="after">{{prop.new}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div ng-if="log.description != null">
|
||||
{{::log.description}}
|
||||
<div ng-if="log.expand"
|
||||
class="expanded-json">
|
||||
<div ng-repeat="prop in ::log.props">
|
||||
<span class="json-field"
|
||||
title="{{::prop.name}}">
|
||||
{{::prop.nameI18n}}:
|
||||
</span>
|
||||
<vn-json-value value="::$ctrl.mainVal(prop, log.action)"></vn-json-value>
|
||||
<span ng-if="::log.action == 'update'">
|
||||
← <vn-json-value value="::prop.old"></vn-json-value>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</vn-td>
|
||||
</vn-tr>
|
||||
</vn-tbody>
|
||||
</vn-table>
|
||||
</vn-card>
|
||||
</span>
|
||||
<span ng-if="::!log.props.length"
|
||||
class="description">
|
||||
{{::log.description}}
|
||||
</span>
|
||||
<span ng-if="::!log.description && !log.props.length"
|
||||
class="no-changes"
|
||||
translate>
|
||||
No changes
|
||||
</span>
|
||||
</div>
|
||||
</vn-card>
|
||||
</div>
|
||||
</div>
|
||||
</vn-data-viewer>
|
||||
<vn-side-menu side="right">
|
||||
<form vn-vertical
|
||||
ng-model-options="{updateOn: 'change blur'}"
|
||||
class="vn-pa-md filter">
|
||||
<h6
|
||||
class="text-secondary vn-mb-md"
|
||||
style="font-weight: normal;"
|
||||
translate>
|
||||
Filter
|
||||
</h6>
|
||||
<vn-textfield
|
||||
label="Name"
|
||||
ng-model="filter.changedModelValue">
|
||||
</vn-textfield>
|
||||
<vn-vertical>
|
||||
<vn-radio
|
||||
label="All"
|
||||
val="all"
|
||||
ng-model="filter.who">
|
||||
</vn-radio>
|
||||
<vn-radio
|
||||
label="User"
|
||||
val="user"
|
||||
ng-model="filter.who">
|
||||
</vn-radio>
|
||||
<vn-radio
|
||||
label="System"
|
||||
val="system"
|
||||
ng-model="filter.who">
|
||||
</vn-radio>
|
||||
</div>
|
||||
<vn-autocomplete
|
||||
ng-show="filter.who != 'system'"
|
||||
label="User"
|
||||
ng-model="filter.userFk"
|
||||
value-field="id"
|
||||
show-field="nickname"
|
||||
fields="['id', 'name', 'nickname']"
|
||||
search-function="$ctrl.searchUser($search)"
|
||||
url="{{$ctrl.url}}/{{$ctrl.originId}}/editors"
|
||||
order="nickname">
|
||||
<tpl-item>
|
||||
<div>{{nickname}}</div>
|
||||
<div class="text-secondary text-caption">{{name}}</div>
|
||||
</tpl-item>
|
||||
</vn-autocomplete>
|
||||
<vn-autocomplete
|
||||
label="Model"
|
||||
ng-model="filter.changedModel"
|
||||
value-field="changedModel"
|
||||
show-field="changedModel"
|
||||
data="models">
|
||||
</vn-autocomplete>
|
||||
<vn-textfield
|
||||
label="Id"
|
||||
ng-model="filter.changedModelId">
|
||||
</vn-textfield>
|
||||
<vn-vertical>
|
||||
<vn-check
|
||||
label="Creates"
|
||||
ng-model="filter.actions.insert">
|
||||
</vn-check>
|
||||
<vn-check
|
||||
label="Updates"
|
||||
ng-model="filter.actions.update">
|
||||
</vn-check>
|
||||
<vn-check
|
||||
label="Deletes"
|
||||
ng-model="filter.actions.delete">
|
||||
</vn-check>
|
||||
<vn-check
|
||||
label="Views"
|
||||
ng-model="filter.actions.select">
|
||||
</vn-check>
|
||||
</div>
|
||||
<vn-date-picker
|
||||
label="Date"
|
||||
ng-model="filter.from">
|
||||
</vn-date-picker>
|
||||
<vn-date-picker
|
||||
label="To"
|
||||
ng-model="filter.to">
|
||||
</vn-date-picker>
|
||||
<vn-button-bar vn-vertical class="vn-mt-sm">
|
||||
<vn-button
|
||||
label="Filter"
|
||||
ng-click="$ctrl.applyFilter(filter)">
|
||||
</vn-button>
|
||||
<vn-button
|
||||
label="Reset"
|
||||
class="flat"
|
||||
ng-click="$ctrl.resetFilter()"
|
||||
ng-if="model.userFilter">
|
||||
</vn-button>
|
||||
</vn-button-bar>
|
||||
</form>
|
||||
</vn-side-menu>
|
||||
<vn-worker-descriptor-popover vn-id="workerDescriptor">
|
||||
</vn-worker-descriptor-popover>
|
||||
|
|
|
@ -13,11 +13,17 @@ export default class Controller extends Section {
|
|||
delete: 'Deletes',
|
||||
select: 'Views'
|
||||
};
|
||||
this.actionsClass = {
|
||||
insert: 'success',
|
||||
update: 'warning',
|
||||
delete: 'alert',
|
||||
select: 'notice'
|
||||
};
|
||||
this.filter = {
|
||||
include: [{
|
||||
relation: 'user',
|
||||
scope: {
|
||||
fields: ['name'],
|
||||
fields: ['nickname'],
|
||||
include: {
|
||||
relation: 'worker',
|
||||
scope: {
|
||||
|
@ -27,6 +33,20 @@ export default class Controller extends Section {
|
|||
},
|
||||
}],
|
||||
};
|
||||
|
||||
this.dateFilter = this.$filter('date');
|
||||
this.lang = this.$translate.use();
|
||||
this.today = Date.vnNew();
|
||||
this.today.setHours(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
$postLink() {
|
||||
this.resetFilter();
|
||||
this.$.$watch(
|
||||
() => this.$.filter,
|
||||
() => this.applyFilter(),
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
get logs() {
|
||||
|
@ -42,6 +62,7 @@ export default class Controller extends Section {
|
|||
const oldValues = log.oldInstance || empty;
|
||||
const newValues = log.newInstance || empty;
|
||||
const locale = validations[log.changedModel]?.locale || empty;
|
||||
log.changedModelI18n = locale.name || log.changedModel;
|
||||
|
||||
let props = Object.keys(oldValues).concat(Object.keys(newValues));
|
||||
props = [...new Set(props)];
|
||||
|
@ -49,9 +70,10 @@ export default class Controller extends Section {
|
|||
log.props = [];
|
||||
for (const prop of props) {
|
||||
log.props.push({
|
||||
name: locale[prop] || prop,
|
||||
old: this.formatValue(oldValues[prop]),
|
||||
new: this.formatValue(newValues[prop])
|
||||
name: prop,
|
||||
nameI18n: locale.columns?.[prop] || prop,
|
||||
old: this.castJsonValue(oldValues[prop]),
|
||||
new: this.castJsonValue(newValues[prop])
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -61,37 +83,124 @@ export default class Controller extends Section {
|
|||
return !(this.changedModel && this.changedModelId);
|
||||
}
|
||||
|
||||
formatValue(value) {
|
||||
let type = typeof value;
|
||||
castJsonValue(value) {
|
||||
return typeof value === 'string' && validDate.test(value)
|
||||
? new Date(value)
|
||||
: value;
|
||||
}
|
||||
|
||||
if (type === 'string' && validDate.test(value)) {
|
||||
value = new Date(value);
|
||||
type = typeof value;
|
||||
}
|
||||
mainVal(prop, action) {
|
||||
return action == 'delete' ? prop.old : prop.new;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case 'boolean':
|
||||
return value ? '✓' : '✗';
|
||||
case 'object':
|
||||
if (value instanceof Date) {
|
||||
const hasZeroTime =
|
||||
value.getHours() === 0 &&
|
||||
value.getMinutes() === 0 &&
|
||||
value.getSeconds() === 0;
|
||||
const format = hasZeroTime ? 'dd/MM/yyyy' : 'dd/MM/yyyy HH:mm:ss';
|
||||
return this.$filter('date')(value, format);
|
||||
}
|
||||
else
|
||||
return value;
|
||||
default:
|
||||
return value;
|
||||
}
|
||||
toggleAttributes(log, changesEl, force) {
|
||||
log.expand = force;
|
||||
changesEl.classList.toggle('expanded', force);
|
||||
}
|
||||
|
||||
relativeDate(dateVal) {
|
||||
if (dateVal == null) return '';
|
||||
const date = new Date(dateVal);
|
||||
const dateZeroTime = new Date(dateVal);
|
||||
dateZeroTime.setHours(0, 0, 0, 0);
|
||||
const diff = Math.trunc((this.today.getTime() - dateZeroTime.getTime()) / (1000 * 3600 * 24));
|
||||
|
||||
let format;
|
||||
if (diff == 0)
|
||||
format = `'${this.$t('today')}'`;
|
||||
else if (diff == 1)
|
||||
format = `'${this.$t('yesterday')}'`;
|
||||
else if (diff > 1 && diff < 7)
|
||||
format = `'${date.toLocaleDateString(this.lang, {weekday: 'short'})}'`;
|
||||
else if (this.today.getFullYear() == date.getFullYear())
|
||||
format = `d '${date.toLocaleDateString(this.lang, {month: 'short'})}'`;
|
||||
else
|
||||
format = `dd/MM/yyyy`;
|
||||
|
||||
return this.dateFilter(date, `${format} HH:mm`);
|
||||
}
|
||||
|
||||
showWorkerDescriptor(event, workerId) {
|
||||
if (!workerId) return;
|
||||
this.$.workerDescriptor.show(event.target, workerId);
|
||||
}
|
||||
|
||||
resetFilter() {
|
||||
this.$.filter = {who: 'all'};
|
||||
}
|
||||
|
||||
applyFilter() {
|
||||
const filter = this.$.filter;
|
||||
|
||||
function getParam(prop, value) {
|
||||
if (value == null || value == '') return null;
|
||||
switch (prop) {
|
||||
case 'changedModelValue':
|
||||
return {[prop]: {like: `%${value}%`}};
|
||||
case 'who':
|
||||
switch (value) {
|
||||
case 'all':
|
||||
return null;
|
||||
case 'user':
|
||||
return {userFk: {neq: null}};
|
||||
case 'system':
|
||||
return {userFk: null};
|
||||
}
|
||||
case 'actions':
|
||||
const inq = [];
|
||||
for (const action in value) {
|
||||
if (value[action])
|
||||
inq.push(action);
|
||||
}
|
||||
return inq.length ? {action: {inq}} : null;
|
||||
case 'from':
|
||||
if (filter.to) {
|
||||
return {creationDate: {gte: value}};
|
||||
} else {
|
||||
const to = new Date(value);
|
||||
to.setHours(23, 59, 59, 999);
|
||||
return {creationDate: {between: [value, to]}};
|
||||
}
|
||||
case 'to':
|
||||
const to = new Date(value);
|
||||
to.setHours(23, 59, 59, 999);
|
||||
return {creationDate: {lte: to}};
|
||||
default:
|
||||
return {[prop]: value};
|
||||
}
|
||||
}
|
||||
|
||||
const and = [];
|
||||
for (const prop in filter) {
|
||||
const param = getParam(prop, filter[prop]);
|
||||
if (param) and.push(param);
|
||||
}
|
||||
|
||||
const lbFilter = and.length ? {where: {and}} : null;
|
||||
return this.$.model.applyFilter(lbFilter);
|
||||
}
|
||||
|
||||
searchUser(search) {
|
||||
if (/^[0-9]+$/.test(search)) {
|
||||
return {id: search};
|
||||
} else {
|
||||
return {or: [
|
||||
{name: search},
|
||||
{nickname: {like: `%${search}%`}}
|
||||
]}
|
||||
}
|
||||
}
|
||||
|
||||
userBgColor(user) {
|
||||
if (!user) return;
|
||||
const name = user.name || '';
|
||||
let hash = 0;
|
||||
for (let i = 0; i < name.length; i++)
|
||||
hash += name.charCodeAt(i);
|
||||
return {
|
||||
backgroundColor: '#'+ colors[hash % colors.length]
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
ngModule.vnComponent('vnLog', {
|
||||
|
@ -105,3 +214,29 @@ ngModule.vnComponent('vnLog', {
|
|||
url: '@'
|
||||
}
|
||||
});
|
||||
|
||||
const colors = [
|
||||
'e2553d', // Coral
|
||||
'FFA07A', // Salmon
|
||||
'FFDAB9', // Peach
|
||||
'a17077', // Pink
|
||||
'bf0e99', // Pink light
|
||||
'52a500', // Green chartreuse
|
||||
'00aeae', // Cian
|
||||
'b754cf', // Purple middle
|
||||
'8a69cd', // Blue lavender
|
||||
'1fa8a1', // Green ocean
|
||||
'DC143C', // Red crimson
|
||||
'5681cf', // Blue steel
|
||||
'FF1493', // Ping intense
|
||||
'00d700', // Green lime
|
||||
'1E90FF', // Blue sky
|
||||
'8B008B', // Purple dark
|
||||
'cc7000', // Orange bright
|
||||
'00b5b8', // Turquoise
|
||||
'8B0000', // Red dark
|
||||
'008080', // Green bluish
|
||||
'2F4F4F', // Gray board
|
||||
'7e7e7e', // Gray
|
||||
'5d5d5d', // Gray dark
|
||||
];
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
import './index';
|
||||
|
||||
describe('Salix Component vnLog', () => {
|
||||
let controller;
|
||||
let $scope;
|
||||
let $element;
|
||||
|
||||
beforeEach(ngModule('salix'));
|
||||
|
||||
beforeEach(inject(($componentController, $rootScope) => {
|
||||
$scope = $rootScope.$new();
|
||||
$element = angular.element('<vn-log></vn-log>');
|
||||
controller = $componentController('vnLog', {$element, $scope});
|
||||
}));
|
||||
|
||||
describe('relativeDate()', () => {
|
||||
let date;
|
||||
|
||||
beforeEach(() => {
|
||||
date = Date.vnNew();
|
||||
});
|
||||
|
||||
it('should return empty string when date is null', () => {
|
||||
const ret = controller.relativeDate(null);
|
||||
|
||||
expect(ret).toEqual('');
|
||||
});
|
||||
|
||||
it('should return empty string when date is undefined', () => {
|
||||
const ret = controller.relativeDate(undefined);
|
||||
|
||||
expect(ret).toEqual('');
|
||||
});
|
||||
|
||||
it('should return today and time when date is today', () => {
|
||||
const ret = controller.relativeDate(date);
|
||||
|
||||
expect(ret).toEqual('today 12:00');
|
||||
});
|
||||
|
||||
it('should return yesterday and time when date is yesterday', () => {
|
||||
date.setDate(date.getDate() - 1);
|
||||
const ret = controller.relativeDate(date);
|
||||
|
||||
expect(ret).toEqual('yesterday 12:00');
|
||||
});
|
||||
|
||||
it('should return abreviated weekday name and time when date is on past week', () => {
|
||||
date.setDate(date.getDate() - 3);
|
||||
const ret = controller.relativeDate(date);
|
||||
|
||||
expect(ret).toEqual('Fri 12:00');
|
||||
});
|
||||
|
||||
it('should return abreviated month name, day number and time when date is on this year', () => {
|
||||
date.setDate(date.getDate() + 20);
|
||||
const ret = controller.relativeDate(date);
|
||||
|
||||
expect(ret).toEqual('21 Jan 12:00');
|
||||
});
|
||||
|
||||
it('should return abreviated month name, day number, year and time when date is on different year', () => {
|
||||
date.setDate(date.getDate() - 20);
|
||||
const ret = controller.relativeDate(date);
|
||||
|
||||
expect(ret).toEqual('12/12/2000 12:00');
|
||||
});
|
||||
|
||||
it('should convert to date and return string when date is not a Date class instance', () => {
|
||||
const ret = controller.relativeDate(date.toJSON());
|
||||
|
||||
expect(ret).toEqual('today 12:00');
|
||||
});
|
||||
});
|
||||
|
||||
describe('castJsonValue()', () => {
|
||||
it('should return date when string has valid JSON date format', () => {
|
||||
const now = Date.vnNew();
|
||||
|
||||
const ret = controller.castJsonValue(now.toJSON());
|
||||
|
||||
expect(ret).toBeInstanceOf(Date);
|
||||
});
|
||||
|
||||
it('should return same value when is string with invalid JSON date format', () => {
|
||||
const ret = controller.castJsonValue('Foo');
|
||||
|
||||
expect(ret).toEqual('Foo');
|
||||
});
|
||||
|
||||
it('should return same value when is not an string', () => {
|
||||
const ret = controller.castJsonValue(1001);
|
||||
|
||||
expect(ret).toEqual(1001);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -13,3 +13,6 @@ Views: Visualiza
|
|||
System: Sistema
|
||||
note: nota
|
||||
Changes: Cambios
|
||||
No changes: No hay cambios
|
||||
today: hoy
|
||||
yesterday: ayer
|
||||
|
|
|
@ -1,66 +1,173 @@
|
|||
@import "variables";
|
||||
|
||||
vn-log {
|
||||
vn-td {
|
||||
vertical-align: initial !important;
|
||||
}
|
||||
.changes {
|
||||
display: none;
|
||||
}
|
||||
.label {
|
||||
color: $color-font-secondary;
|
||||
}
|
||||
.value {
|
||||
color: $color-font;
|
||||
}
|
||||
.change {
|
||||
display: flex;
|
||||
|
||||
@media screen and (max-width: 1570px) {
|
||||
vn-table .expendable {
|
||||
& > .user-wrapper {
|
||||
position: relative;
|
||||
padding-right: 10px;
|
||||
|
||||
& > .user {
|
||||
border-radius: 50%;
|
||||
height: 36px;
|
||||
width: 36px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
|
||||
& > * {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
img {
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
}
|
||||
.user-icon {
|
||||
font-size: 22px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
&.system {
|
||||
background-color: $color-main;
|
||||
}
|
||||
}
|
||||
}
|
||||
& > .arrow {
|
||||
height: 8px;
|
||||
width: 8px;
|
||||
position: absolute;
|
||||
transform: rotateY(0deg) rotate(45deg);
|
||||
top: 18px;
|
||||
right: -4px;
|
||||
z-index: 1;
|
||||
}
|
||||
& > .line {
|
||||
position: absolute;
|
||||
background-color: $color-main;
|
||||
width: 2px;
|
||||
left: 17px;
|
||||
z-index: -1;
|
||||
top: 44px;
|
||||
bottom: -8px;
|
||||
}
|
||||
}
|
||||
&:last-child > .user-wrapper > .line {
|
||||
display: none;
|
||||
}
|
||||
.changes {
|
||||
padding-top: 10px;
|
||||
display: block;
|
||||
.detail {
|
||||
position: relative;
|
||||
flex-grow: 1;
|
||||
width: 100%;
|
||||
border-radius: 2px;
|
||||
overflow: hidden;
|
||||
|
||||
& > .header {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
|
||||
& > .chip {
|
||||
padding: 2px 4px;
|
||||
border-radius: 4px;
|
||||
display: inline-block;
|
||||
color: $color-font-bg;
|
||||
|
||||
&.notice {
|
||||
background-color: $color-notice-medium;
|
||||
}
|
||||
&.success {
|
||||
background-color: $color-success-medium;
|
||||
}
|
||||
&.warning {
|
||||
background-color: $color-main-medium;
|
||||
}
|
||||
&.alert {
|
||||
background-color: lighten($color-alert, 5%);
|
||||
}
|
||||
}
|
||||
.date {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
& > .model {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
|
||||
& > .model-name {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
& > .model-value {
|
||||
font-style: italic;
|
||||
color: #c7bd2b;
|
||||
}
|
||||
& > .model-id {
|
||||
color: $color-font-secondary;
|
||||
font-size: .9em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.attributes {
|
||||
width: 100%;
|
||||
.changes {
|
||||
overflow: hidden;
|
||||
background-color: rgba(255, 255, 255, .05);
|
||||
border-radius: 4px;
|
||||
color: $color-font-secondary;
|
||||
transition: max-height 150ms ease-in-out;
|
||||
max-height: 28px;
|
||||
position: relative;
|
||||
|
||||
tr {
|
||||
height: 10px;
|
||||
& > .expand-button,
|
||||
& > .shrink-button {
|
||||
display: none;
|
||||
}
|
||||
&.props {
|
||||
padding-right: 24px;
|
||||
|
||||
& > td {
|
||||
padding: 2px;
|
||||
& > .expand-button,
|
||||
& > .shrink-button {
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
right: 8px;
|
||||
font-size: inherit;
|
||||
float: right;
|
||||
cursor: pointer;
|
||||
}
|
||||
& > td.field,
|
||||
& > th.field {
|
||||
width: 20%;
|
||||
color: gray;
|
||||
& > .expand-button {
|
||||
display: block;
|
||||
}
|
||||
& > td.before,
|
||||
& > th.before,
|
||||
& > td.after,
|
||||
& > th.after {
|
||||
width: 40%;
|
||||
white-space: pre-line;
|
||||
&.expanded {
|
||||
max-height: 500px;
|
||||
padding-right: 0;
|
||||
|
||||
& > .changes-wrapper {
|
||||
text-overflow: initial;
|
||||
white-space: initial;
|
||||
}
|
||||
& > .shrink-button {
|
||||
display: block;
|
||||
}
|
||||
& > .expand-button {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
& > .changes-wrapper {
|
||||
padding: 4px 6px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
|
||||
& > .no-changes {
|
||||
font-style: italic;
|
||||
}
|
||||
.json-field {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.ellipsis {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
max-width: 400px;
|
||||
text-overflow: ellipsis;
|
||||
display: inline-block;
|
||||
}
|
||||
.no-ellipsize,
|
||||
[no-ellipsize] {
|
||||
text-overflow: '';
|
||||
white-space: normal;
|
||||
overflow: auto;
|
||||
}
|
||||
.alignSpan {
|
||||
overflow: hidden;
|
||||
display: inline-block;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
const mergeFilters = require('vn-loopback/util/filter').mergeFilters;
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethod('editors', {
|
||||
description: 'Get the list of entity editors',
|
||||
accepts: [
|
||||
{
|
||||
arg: 'id',
|
||||
type: 'integer',
|
||||
description: 'The model id',
|
||||
required: true
|
||||
}, {
|
||||
arg: 'filter',
|
||||
type: 'Object',
|
||||
description: 'The user filter object'
|
||||
}
|
||||
],
|
||||
returns: {
|
||||
type: [Self],
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
path: `/:id/editors`,
|
||||
verb: 'GET'
|
||||
}
|
||||
});
|
||||
|
||||
Self.editors = async(id, filter) => {
|
||||
const res = await Self.find({
|
||||
fields: ['userFk'],
|
||||
where: {originFk: id}
|
||||
});
|
||||
const userIds = new Set(res.map(x => x.userFk));
|
||||
|
||||
filter = mergeFilters(filter, {
|
||||
where: {id: {inq: [...userIds]}}
|
||||
});
|
||||
return await Self.app.models.VnUser.find(filter);
|
||||
};
|
||||
};
|
|
@ -0,0 +1,44 @@
|
|||
const mergeFilters = require('vn-loopback/util/filter').mergeFilters;
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethod('models', {
|
||||
description: 'Get the list of entity models',
|
||||
accepts: [
|
||||
{
|
||||
arg: 'id',
|
||||
type: 'integer',
|
||||
description: 'The model id',
|
||||
required: true
|
||||
}, {
|
||||
arg: 'filter',
|
||||
type: 'Object',
|
||||
description: 'The filter object'
|
||||
}
|
||||
],
|
||||
returns: {
|
||||
type: [Self],
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
path: `/:id/models`,
|
||||
verb: 'GET'
|
||||
}
|
||||
});
|
||||
|
||||
Self.models = async(id, filter) => {
|
||||
filter = mergeFilters(filter, {
|
||||
fields: ['changedModel'],
|
||||
where: {
|
||||
originFk: id,
|
||||
changedModel: {neq: null}
|
||||
}
|
||||
});
|
||||
const res = await Self.find(filter);
|
||||
|
||||
const set = new Set();
|
||||
return res.filter(x => set.has(x.changedModel)
|
||||
? false
|
||||
: set.add(x.changedModel)
|
||||
);
|
||||
};
|
||||
};
|
|
@ -0,0 +1,10 @@
|
|||
|
||||
module.exports = function(Self) {
|
||||
Object.assign(Self, {
|
||||
setup() {
|
||||
Self.super_.setup.call(this);
|
||||
require('../methods/log/editors')(this);
|
||||
require('../methods/log/models')(this);
|
||||
}
|
||||
});
|
||||
};
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"name": "Log",
|
||||
"base": "VnModel"
|
||||
}
|
|
@ -28,12 +28,14 @@ module.exports = function(Self) {
|
|||
});
|
||||
|
||||
// Register field ACL validation
|
||||
/* this.beforeRemote('prototype.patchAttributes', ctx => this.checkUpdateAcls(ctx));
|
||||
/*
|
||||
this.beforeRemote('prototype.patchAttributes', ctx => this.checkUpdateAcls(ctx));
|
||||
this.beforeRemote('updateAll', ctx => this.checkUpdateAcls(ctx));
|
||||
this.beforeRemote('patchOrCreate', ctx => this.checkInsertAcls(ctx));
|
||||
this.beforeRemote('create', ctx => this.checkInsertAcls(ctx));
|
||||
this.beforeRemote('replaceById', ctx => this.checkInsertAcls(ctx));
|
||||
this.beforeRemote('replaceOrCreate', ctx => this.checkInsertAcls(ctx)); */
|
||||
this.beforeRemote('replaceOrCreate', ctx => this.checkInsertAcls(ctx));
|
||||
*/
|
||||
|
||||
this.remoteMethod('crud', {
|
||||
description: `Create, update or/and delete instances from model with a single request`,
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
name: mail
|
||||
columns:
|
||||
id: id
|
||||
receiver: receiver
|
||||
replyTo: reply to
|
||||
subject: subject
|
||||
body: body
|
|
@ -0,0 +1,7 @@
|
|||
name: mail
|
||||
columns:
|
||||
id: id
|
||||
receiver: receptor
|
||||
replyTo: responder a
|
||||
subject: asunto
|
||||
body: cuerpo
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "RoleLog",
|
||||
"base": "VnModel",
|
||||
"base": "Log",
|
||||
"options": {
|
||||
"mysql": {
|
||||
"table": "account.roleLog"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "UserLog",
|
||||
"base": "VnModel",
|
||||
"base": "Log",
|
||||
"options": {
|
||||
"mysql": {
|
||||
"table": "account.userLog"
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
name: claim beginning
|
||||
columns:
|
||||
id: id
|
||||
quantity: quantity
|
||||
claimFk: claim
|
||||
saleFk: sale
|
|
@ -0,0 +1,6 @@
|
|||
name: comienzo reclamación
|
||||
columns:
|
||||
id: id
|
||||
quantity: cantidad
|
||||
claimFk: reclamación
|
||||
saleFk: línea
|
|
@ -0,0 +1,9 @@
|
|||
name: claim development
|
||||
columns:
|
||||
id: id
|
||||
claimFk: claim
|
||||
claimResponsibleFk: responsible
|
||||
claimReasonFk: reason
|
||||
claimResultFk: result
|
||||
claimRedeliveryFk: redelivery
|
||||
workerFk: worker
|
|
@ -0,0 +1,9 @@
|
|||
name: desarrollo reclamación
|
||||
columns:
|
||||
id: id
|
||||
claimFk: reclamación
|
||||
claimResponsibleFk: responsable
|
||||
claimReasonFk: motivo
|
||||
claimResultFk: resultado
|
||||
claimRedeliveryFk: reenvío
|
||||
workerFk: trabajador
|
|
@ -0,0 +1,4 @@
|
|||
name: claim dms
|
||||
columns:
|
||||
dmsFk: dms
|
||||
claimFk: claim
|
|
@ -0,0 +1,4 @@
|
|||
name: documento reclamación
|
||||
columns:
|
||||
dmsFk: dms
|
||||
claimFk: reclamación
|
|
@ -0,0 +1,7 @@
|
|||
name: claim end
|
||||
columns:
|
||||
id: id
|
||||
claimFk: claim
|
||||
saleFk: sale
|
||||
workerFk: worker
|
||||
claimDestinationFk: destination
|
|
@ -0,0 +1,7 @@
|
|||
name: final reclamación
|
||||
columns:
|
||||
id: id
|
||||
claimFk: reclamación
|
||||
saleFk: línea
|
||||
workerFk: trabajador
|
||||
claimDestinationFk: destino
|
|
@ -0,0 +1,7 @@
|
|||
name: claim observation
|
||||
columns:
|
||||
id: id
|
||||
claimFk: claim
|
||||
text: text
|
||||
created: created
|
||||
workerFk: worker
|
|
@ -0,0 +1,7 @@
|
|||
name: observación reclamación
|
||||
columns:
|
||||
id: id
|
||||
claimFk: reclamación
|
||||
text: texto
|
||||
created: creado
|
||||
workerFk: tabajador
|
|
@ -0,0 +1,16 @@
|
|||
name: claim
|
||||
columns:
|
||||
id: id
|
||||
observation: observation
|
||||
ticketCreated: ticket created
|
||||
isChargedToMana: charged to mana
|
||||
created: created
|
||||
responsibility: responsibility
|
||||
hasToPickUp: has to pickUp
|
||||
ticketFk: ticket
|
||||
claimStateFk: claim state
|
||||
workerFk: worker
|
||||
packages: packages
|
||||
rma: rma
|
||||
clientFk: client
|
||||
claimFk: claim
|
|
@ -0,0 +1,16 @@
|
|||
name: reclamación
|
||||
columns:
|
||||
id: id
|
||||
observation: observación
|
||||
ticketCreated: ticket creado
|
||||
isChargedToMana: cargado al maná
|
||||
created: creado
|
||||
responsibility: responsabilidad
|
||||
hasToPickUp: es recogida
|
||||
ticketFk: ticket
|
||||
claimStateFk: estado reclamación
|
||||
workerFk: trabajador
|
||||
packages: paquetes
|
||||
rma: rma
|
||||
clientFk: cliente
|
||||
claimFk: reclamación
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "ClaimLog",
|
||||
"base": "VnModel",
|
||||
"base": "Log",
|
||||
"options": {
|
||||
"mysql": {
|
||||
"table": "claimLog"
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
name: address
|
||||
columns:
|
||||
id: id
|
||||
nickname: nickname
|
||||
street: street
|
||||
city: city
|
||||
postalCode: postal code
|
||||
phone: phone
|
||||
mobile: mobile
|
||||
isActive: active
|
||||
longitude: longitude
|
||||
latitude: latitude
|
||||
isEqualizated: equalizated
|
||||
isLogifloraAllowed: logiflora allowed
|
||||
provinceFk: province
|
||||
clientFk: client
|
||||
agencyModeFk: agency
|
||||
addressFk: address
|
||||
incotermsFk: incoterms
|
||||
customsAgentFk: customs agent
|
|
@ -0,0 +1,20 @@
|
|||
name: dirección
|
||||
columns:
|
||||
id: id
|
||||
nickname: apodo
|
||||
street: calle
|
||||
city: ciudad
|
||||
postalCode: código postal
|
||||
phone: teléfono
|
||||
mobile: móvil
|
||||
isActive: activo
|
||||
longitude: longitud
|
||||
latitude: latitud
|
||||
isEqualizated: igualado
|
||||
isLogifloraAllowed: logiflora permitido
|
||||
provinceFk: provincia
|
||||
clientFk: cliente
|
||||
agencyModeFk: agencia
|
||||
addressFk: dirección
|
||||
incotermsFk: incoterms
|
||||
customsAgentFk: agente adunanas
|
|
@ -0,0 +1,6 @@
|
|||
name: client contact
|
||||
columns:
|
||||
id: id
|
||||
name: name
|
||||
phone: phone
|
||||
clientFk: client
|
|
@ -0,0 +1,6 @@
|
|||
name: contacto cliente
|
||||
columns:
|
||||
id: id
|
||||
name: nombre
|
||||
phone: teléfono
|
||||
clientFk: cliente
|
|
@ -0,0 +1,4 @@
|
|||
name: client dms
|
||||
columns:
|
||||
dmsFk: dms
|
||||
clientFk: client
|
|
@ -0,0 +1,4 @@
|
|||
name: documento cliente
|
||||
columns:
|
||||
dmsFk: dms
|
||||
clientFk: client
|
|
@ -0,0 +1,7 @@
|
|||
name: client observation
|
||||
columns:
|
||||
id: id
|
||||
clientFk: client
|
||||
text: text
|
||||
created: created
|
||||
workerFk: worker
|
|
@ -0,0 +1,7 @@
|
|||
name: observación cliente
|
||||
columns:
|
||||
id: id
|
||||
clientFk: cliente
|
||||
text: texto
|
||||
created: creado
|
||||
workerFk: trabajador
|
|
@ -0,0 +1,8 @@
|
|||
name: client sample
|
||||
columns:
|
||||
id: id
|
||||
created: created
|
||||
clientFk: client
|
||||
typeFk: type
|
||||
userFk: user
|
||||
companyFk: company
|
|
@ -0,0 +1,8 @@
|
|||
name: muestra cliente
|
||||
columns:
|
||||
id: id
|
||||
created: creado
|
||||
clientFk: cliente
|
||||
typeFk: tipo
|
||||
userFk: usuario
|
||||
companyFk: compañia
|
|
@ -0,0 +1,50 @@
|
|||
name: client
|
||||
columns:
|
||||
id: id
|
||||
name: name
|
||||
fi: fi
|
||||
socialName: socialName
|
||||
contact: contact
|
||||
street: street
|
||||
city: city
|
||||
postcode: postcode
|
||||
email: email
|
||||
phone: phone
|
||||
mobile: mobile
|
||||
isActive: active
|
||||
credit: credit
|
||||
creditInsurance: credit insurance
|
||||
iban: iban
|
||||
dueDay: due day
|
||||
isEqualizated: equalizated
|
||||
isFreezed: freezed
|
||||
hasToInvoiceByAddress: invoice by address
|
||||
hasToInvoice: has to invoice
|
||||
isToBeMailed: be mailed
|
||||
hasSepaVnl: sepa nnl
|
||||
hasLcr: lcr
|
||||
hasCoreVnl: core vnl
|
||||
hasCoreVnh: core vnh
|
||||
hasIncoterms: incoterms
|
||||
isTaxDataChecked: tax data checked
|
||||
eypbc: eypbc
|
||||
quality: quality
|
||||
isVies: vies
|
||||
isRelevant: relevant
|
||||
accountingAccount: accounting account
|
||||
created: created
|
||||
sageTaxTypeFk: sage tax type
|
||||
sageTransactionTypeFk: sage transaction type
|
||||
businessTypeFk: business type
|
||||
salesPersonFk: sales person
|
||||
hasElectronicInvoice: electronic invoice
|
||||
payMethodFk: pay method
|
||||
provinceFk: province
|
||||
countryFk: country
|
||||
contactChannelFk: contact channel
|
||||
clientTypeFk: client type
|
||||
clientFk: client
|
||||
defaultAddressFk: default address
|
||||
bankEntityFk: bank entity
|
||||
transferorFk: transferor
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
name: cliente
|
||||
columns:
|
||||
id: id
|
||||
name: nombre
|
||||
fi: fi
|
||||
socialName: nombre social
|
||||
contact: contacto
|
||||
street: calle
|
||||
city: ciudad
|
||||
postcode: código postal
|
||||
email: email
|
||||
phone: teléfono
|
||||
mobile: móvil
|
||||
isActive: activo
|
||||
credit: crédito
|
||||
creditInsurance: seguro crédito
|
||||
iban: iban
|
||||
dueDay: día vencimiento
|
||||
isEqualizated: igualado
|
||||
isFreezed: congelado
|
||||
hasToInvoiceByAddress: factura por dirección
|
||||
hasToInvoice: tiene que facturar
|
||||
isToBeMailed: envío por email
|
||||
hasSepaVnl: sepa nnl
|
||||
hasLcr: lcr
|
||||
hasCoreVnl: centro vnl
|
||||
hasCoreVnh: cenrto vnh
|
||||
hasIncoterms: incoterms
|
||||
isTaxDataChecked: datos fiscales comprobados
|
||||
eypbc: eypbc
|
||||
quality: calidad
|
||||
isVies: vies
|
||||
isRelevant: importante
|
||||
accountingAccount: cuenta contable
|
||||
created: creado
|
||||
sageTaxTypeFk: tipo impuesto sage
|
||||
sageTransactionTypeFk: tipo transacción sage
|
||||
businessTypeFk: tipo negocio
|
||||
salesPersonFk: comercial
|
||||
hasElectronicInvoice: factura electrónica
|
||||
payMethodFk: método pago
|
||||
provinceFk: provincia
|
||||
countryFk: país
|
||||
contactChannelFk: canal de contacto
|
||||
clientTypeFk: tipo de cliente
|
||||
clientFk: cliente
|
||||
defaultAddressFk: dirección predeterminada
|
||||
bankEntityFk: entidad bancaria
|
||||
transferorFk: cedente
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
name: greuge
|
||||
columns:
|
||||
id: id
|
||||
description: description
|
||||
amount: amount
|
||||
shipped: shipped
|
||||
created: created
|
||||
greugeTypeFk: greuge type
|
||||
clientFk: client
|
||||
ticketFk: ticket
|
||||
userFk: user
|
|
@ -0,0 +1,11 @@
|
|||
name: greuge
|
||||
columns:
|
||||
id: id
|
||||
description: descripción
|
||||
amount: cantidad
|
||||
shipped: enviado
|
||||
created: creado
|
||||
greugeTypeFk: tipo de greuge
|
||||
clientFk: cliente
|
||||
ticketFk: ticket
|
||||
userFk: usuario
|
|
@ -0,0 +1,8 @@
|
|||
name: recovery
|
||||
columns:
|
||||
id: id
|
||||
started: started
|
||||
finished: finished
|
||||
amount: amount
|
||||
period: period
|
||||
clientFk: client
|
|
@ -0,0 +1,8 @@
|
|||
name: recuperación
|
||||
columns:
|
||||
id: id
|
||||
started: comenzado
|
||||
finished: terminado
|
||||
amount: cantidad
|
||||
period: período
|
||||
clientFk: cliente
|
|
@ -0,0 +1,15 @@
|
|||
name: tpv transaction
|
||||
columns:
|
||||
id: id
|
||||
merchantFk: merchant
|
||||
clientFk: client
|
||||
receiptFk: receipt
|
||||
amount: amount
|
||||
response: response
|
||||
errorCode: error code
|
||||
status: status
|
||||
created: created
|
||||
merchantParameters: merchant parameters
|
||||
signature: signature
|
||||
signatureVersion: signature version
|
||||
responseError: response error
|
|
@ -0,0 +1,15 @@
|
|||
name: transacción tpv
|
||||
columns:
|
||||
id: id
|
||||
merchantFk: comerciante
|
||||
clientFk: cliente
|
||||
receiptFk: recibo
|
||||
amount: cantidad
|
||||
response: respuesta
|
||||
errorCode: código error
|
||||
status: estado
|
||||
created: creado
|
||||
merchantParameters: parámetros comerciante
|
||||
signature: firma
|
||||
signatureVersion: versión firma
|
||||
responseError: error de respuesta
|
|
@ -76,7 +76,7 @@ module.exports = function(Self) {
|
|||
|
||||
const date = Date.vnNew();
|
||||
date.setHours(0, 0, 0, 0);
|
||||
const query = `SELECT vn.clientGetDebt(?, ?) AS debt`;
|
||||
const query = `SELECT vn.client_getDebt(?, ?) AS debt`;
|
||||
const data = await Self.rawSql(query, [id, date], myOptions);
|
||||
|
||||
client.debt = data[0].debt;
|
||||
|
|
|
@ -27,7 +27,7 @@ module.exports = Self => {
|
|||
|
||||
const date = Date.vnNew();
|
||||
date.setHours(0, 0, 0, 0);
|
||||
const query = `SELECT vn.clientGetDebt(?, ?) AS debt`;
|
||||
const query = `SELECT vn.client_getDebt(?, ?) AS debt`;
|
||||
const [debt] = await Self.rawSql(query, [clientFk, date], myOptions);
|
||||
|
||||
return debt;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "ClientLog",
|
||||
"base": "VnModel",
|
||||
"base": "Log",
|
||||
"options": {
|
||||
"mysql": {
|
||||
"table": "clientLog"
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
name: buy
|
||||
columns:
|
||||
id: id
|
||||
quantity: quantity
|
||||
buyingValue: buying value
|
||||
freightValue: freight value
|
||||
packing: packing
|
||||
grouping: grouping
|
||||
stickers: stickers
|
||||
groupingMode: grouping mode
|
||||
comissionValue: comission value
|
||||
packageValue: package value
|
||||
price2: price2
|
||||
price3: price3
|
||||
weight: weight
|
||||
entryFk: entry
|
||||
itemFk: item
|
||||
packageFk: package
|
|
@ -0,0 +1,18 @@
|
|||
name: compra
|
||||
columns:
|
||||
id: id
|
||||
quantity: cantidad
|
||||
buyingValue: valor compra
|
||||
freightValue: valor flete
|
||||
packing: embalaje
|
||||
grouping: agrupación
|
||||
stickers: pegatinas
|
||||
groupingMode: modo agrupación
|
||||
comissionValue: valor comisión
|
||||
packageValue: valor paquete
|
||||
price2: precio2
|
||||
price3: precio3
|
||||
weight: peso
|
||||
entryFk: entrada
|
||||
itemFk: artículo
|
||||
packageFk: paquete
|
|
@ -0,0 +1,6 @@
|
|||
name: entry observation
|
||||
columns:
|
||||
id: id
|
||||
description: description
|
||||
entryFk: entry
|
||||
observationTypeFk: observation type
|
|
@ -0,0 +1,6 @@
|
|||
name: observación entrada
|
||||
columns:
|
||||
id: id
|
||||
description: descripción
|
||||
entryFk: entrada
|
||||
observationTypeFk: tipo observación
|
|
@ -0,0 +1,23 @@
|
|||
name: entry
|
||||
columns:
|
||||
id: id
|
||||
dated: dated
|
||||
reference: reference
|
||||
invoiceNumber: invoice number
|
||||
isBooked: booked
|
||||
isExcludedFromAvailable: excluded from available
|
||||
notes: notes
|
||||
isConfirmed: confirmed
|
||||
isVirtual: virtual
|
||||
isRaid: raid
|
||||
commission: commission
|
||||
isOrdered: price3
|
||||
created: created
|
||||
observation: observation
|
||||
isBlocked: blocked
|
||||
loadPriority: load priority
|
||||
supplierFk: supplier
|
||||
travelFk: travel
|
||||
companyFk: company
|
||||
observationEditorFk: observation editor
|
||||
currencyFk: currency
|
|
@ -0,0 +1,23 @@
|
|||
name: entrada
|
||||
columns:
|
||||
id: id
|
||||
dated: fecha
|
||||
reference: referencia
|
||||
invoiceNumber: número factura
|
||||
isBooked: reservado
|
||||
isExcludedFromAvailable: excluido del disponible
|
||||
notes: notas
|
||||
isConfirmed: confirmado
|
||||
isVirtual: virtual
|
||||
isRaid: incursión
|
||||
commission: comisión
|
||||
isOrdered: precio3
|
||||
created: creado
|
||||
observation: observación
|
||||
isBlocked: bloqueado
|
||||
loadPriority: prioridad de carga
|
||||
supplierFk: proveedor
|
||||
travelFk: envío
|
||||
companyFk: empresa
|
||||
observationEditorFk: editor observación
|
||||
currencyFk: moneda
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "EntryLog",
|
||||
"base": "VnModel",
|
||||
"base": "Log",
|
||||
"options": {
|
||||
"mysql": {
|
||||
"table": "entryLog"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "InvoiceInLog",
|
||||
"base": "VnModel",
|
||||
"base": "Log",
|
||||
"options": {
|
||||
"mysql": {
|
||||
"table": "invoiceInLog"
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
name: item barcode
|
||||
columns:
|
||||
id: id
|
||||
code: code
|
||||
itemFk: item
|
|
@ -0,0 +1,5 @@
|
|||
name: código barras artículo
|
||||
columns:
|
||||
id: id
|
||||
code: código
|
||||
itemFk: artículo
|
|
@ -0,0 +1,5 @@
|
|||
name: item botanical
|
||||
columns:
|
||||
itemFk: item
|
||||
genusFk: genus
|
||||
specieFk: specie
|
|
@ -0,0 +1,5 @@
|
|||
name: artículo botánico
|
||||
columns:
|
||||
itemFk: artículo
|
||||
genusFk: género
|
||||
specieFk: especie
|
|
@ -0,0 +1,7 @@
|
|||
name: item tag
|
||||
columns:
|
||||
id: id
|
||||
value: value
|
||||
itemFk: item
|
||||
tagFk: tag
|
||||
priority: priority
|
|
@ -0,0 +1,7 @@
|
|||
name: etiqueta artículo
|
||||
columns:
|
||||
id: id
|
||||
value: valor
|
||||
itemFk: artículo
|
||||
tagFk: etiqueta
|
||||
priority: prioridad
|
|
@ -0,0 +1,7 @@
|
|||
name: item tax country
|
||||
columns:
|
||||
id: id
|
||||
effectived: effectived
|
||||
itemFk: item
|
||||
countryFk: country
|
||||
taxClassFk: tax class
|
|
@ -0,0 +1,7 @@
|
|||
name: impuesto país del artículo
|
||||
columns:
|
||||
id: id
|
||||
effectived: efectivo
|
||||
itemFk: artículo
|
||||
countryFk: país
|
||||
taxClassFk: clase impuestos
|
|
@ -0,0 +1,47 @@
|
|||
name: item
|
||||
columns:
|
||||
id: id
|
||||
name: name
|
||||
quantity: quantity
|
||||
size: size
|
||||
category: category
|
||||
typeFk: type
|
||||
stems: stems
|
||||
description: description
|
||||
isActive: active
|
||||
relevancy: relevancy
|
||||
weightByPiece: weight by piece
|
||||
stemMultiplier: stem multiplier
|
||||
image: image
|
||||
longName: long name
|
||||
subName: sub name
|
||||
tag5: tag5
|
||||
value5: value5
|
||||
tag6: tag6
|
||||
value6: value6
|
||||
tag7: tag7
|
||||
value7: value7
|
||||
tag8: tag8
|
||||
value8: value8
|
||||
tag9: tag9
|
||||
value9: value9
|
||||
tag10: tag10
|
||||
value10: value10
|
||||
itemPackingTypeFk: item packing type
|
||||
hasKgPrice: has kg price
|
||||
family: family
|
||||
expenseFk: expense
|
||||
minPrice: min price
|
||||
packingOut: packing out
|
||||
hasMinPrice: has min price
|
||||
isFragile: fragile
|
||||
isFloramondo: is floramondo
|
||||
packingShelve: packing shelve
|
||||
isLaid: laid
|
||||
inkFk: ink
|
||||
originFk: origin
|
||||
producerFk: producer
|
||||
intrastatFk: intrastat
|
||||
genericFk: generic
|
||||
itemFk: item
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
name: artículo
|
||||
columns:
|
||||
id: id
|
||||
name: nombre
|
||||
quantity: cantidad
|
||||
size: tamaño
|
||||
category: categoría
|
||||
typeFk: tipo
|
||||
stems: tallos
|
||||
description: descripción
|
||||
isActive: activo
|
||||
relevancy: relevancia
|
||||
weightByPiece: peso por pieza
|
||||
stemMultiplier: multiplicador de tallo
|
||||
image: imagen
|
||||
longName: nombre largo
|
||||
subName: subnombre
|
||||
tag5: etiqueta5
|
||||
value5: valor5
|
||||
tag6: etiqueta6
|
||||
value6: valor6
|
||||
tag7: etiqueta7
|
||||
value7: valor7
|
||||
tag8: etiqueta8
|
||||
value8: valor8
|
||||
tag9: etiqueta9
|
||||
value9: valor9
|
||||
tag10: etiqueta10
|
||||
value10: valor10
|
||||
itemPackingTypeFk: embalaje del artículo
|
||||
hasKgPrice: tiene precio kg
|
||||
family: familia
|
||||
expenseFk: gasto
|
||||
minPrice: precio mínimo
|
||||
packingOut: empaquetar
|
||||
hasMinPrice: tiene precio mínimo
|
||||
isFragile: frágil
|
||||
isFloramondo: es floramondo
|
||||
packingShelve: estantería embalaje
|
||||
isLaid: puesto
|
||||
inkFk: tinta
|
||||
originFk: origen
|
||||
producerFk: productor
|
||||
intrastatFk: intrastat
|
||||
genericFk: genérico
|
||||
itemFk: artículo
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "ItemLog",
|
||||
"base": "VnModel",
|
||||
"base": "Log",
|
||||
"options": {
|
||||
"mysql": {
|
||||
"table": "itemLog"
|
||||
|
@ -12,7 +12,7 @@
|
|||
"type": "number",
|
||||
"forceId": false
|
||||
},
|
||||
"originFk": {
|
||||
"originFk": {
|
||||
"type": "number",
|
||||
"required": true
|
||||
},
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
vn-fixed-price-search-panel vn-side-menu {
|
||||
.menu {
|
||||
min-width: $right-menu-width;
|
||||
min-width: $menu-width;
|
||||
}
|
||||
& > div {
|
||||
.input {
|
||||
|
|
|
@ -238,7 +238,7 @@ module.exports = Self => {
|
|||
ENGINE = MEMORY
|
||||
SELECT DISTINCT clientFk FROM tmp.filter`);
|
||||
|
||||
stmt = new ParameterizedSQL('CALL clientGetDebt(?)', [args.to]);
|
||||
stmt = new ParameterizedSQL('CALL client_getDebt(?)', [args.to]);
|
||||
stmts.push(stmt);
|
||||
stmts.push('DROP TEMPORARY TABLE tmp.clientGetDebt');
|
||||
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
name: route
|
||||
columns:
|
||||
id: id
|
||||
created: created
|
||||
time: time
|
||||
kmStart: km start
|
||||
kmEnd: km end
|
||||
started: started
|
||||
finished: finished
|
||||
gestdoc: gestdoc
|
||||
cost: cost
|
||||
m3: m3
|
||||
description: description
|
||||
isOk: ok
|
||||
workerFk: worker
|
||||
vehicleFk: vehicle
|
||||
agencyModeFk: agency
|
||||
routeFk: route
|
||||
zoneFk: zone
|
|
@ -0,0 +1,19 @@
|
|||
name: ruta
|
||||
columns:
|
||||
id: id
|
||||
created: creado
|
||||
time: tiempo
|
||||
kmStart: km inicio
|
||||
kmEnd: km fin
|
||||
started: comenzado
|
||||
finished: terminado
|
||||
gestdoc: gestdoc
|
||||
cost: costo
|
||||
m3: m3
|
||||
description: descripción
|
||||
isOk: ok
|
||||
workerFk: trabajador
|
||||
vehicleFk: vehículo
|
||||
agencyModeFk: agencia
|
||||
routeFk: ruta
|
||||
zoneFk: zona
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "RouteLog",
|
||||
"base": "VnModel",
|
||||
"base": "Log",
|
||||
"options": {
|
||||
"mysql": {
|
||||
"table": "routeLog"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "ShelvingLog",
|
||||
"base": "VnModel",
|
||||
"base": "Log",
|
||||
"options": {
|
||||
"mysql": {
|
||||
"table": "shelvingLog"
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
name: supplier account
|
||||
columns:
|
||||
id: id
|
||||
iban: iban
|
||||
beneficiary: beneficiary
|
||||
supplierFk: supplier
|
||||
bankEntityFk: bank entity
|
|
@ -0,0 +1,7 @@
|
|||
name: cuenta proveedor
|
||||
columns:
|
||||
id: id
|
||||
iban: iban
|
||||
beneficiary: beneficiario
|
||||
supplierFk: proveedor
|
||||
bankEntityFk: entidad bancaria
|
|
@ -0,0 +1,9 @@
|
|||
name: supplier contact
|
||||
columns:
|
||||
id: id
|
||||
supplierFk: supplier
|
||||
phone: phone
|
||||
mobile: mobile
|
||||
email: email
|
||||
observation: observation
|
||||
name: name
|
|
@ -0,0 +1,9 @@
|
|||
name: contacto proveedor
|
||||
columns:
|
||||
id: id
|
||||
supplierFk: proveedor
|
||||
phone: teléfono
|
||||
mobile: móvil
|
||||
email: email
|
||||
observation: observación
|
||||
name: nombre
|
|
@ -0,0 +1,36 @@
|
|||
name: supplier
|
||||
columns:
|
||||
id: id
|
||||
name: name
|
||||
account: account
|
||||
countryFk: country
|
||||
nif: nif
|
||||
phone: phone
|
||||
retAccount: ret account
|
||||
commission: commission
|
||||
postcodeFk: postcode
|
||||
isActive: active
|
||||
isOfficial: official
|
||||
isSerious: serious
|
||||
isTrucker: trucker
|
||||
note: note
|
||||
street: street
|
||||
city: city
|
||||
provinceFk: province
|
||||
postCode: postcode
|
||||
payMethodFk: pay method
|
||||
payDemFk: pay dem
|
||||
payDay: pay day
|
||||
nickname: nickname
|
||||
workerFk: worker
|
||||
sageTaxTypeFk: sage tax type
|
||||
taxTypeSageFk: sage tax type
|
||||
sageTransactionTypeFk: sage transaction type
|
||||
transactionTypeSageFk: sage transaction type
|
||||
sageWithholdingFk: sage with holding
|
||||
withholdingSageFk: sage with holding
|
||||
isPayMethodChecked: pay method checked
|
||||
supplierActivityFk: supplier activity
|
||||
healthRegister: health register
|
||||
isVies: vies
|
||||
supplierFk: supplier
|
|
@ -0,0 +1,36 @@
|
|||
name: proveedor
|
||||
columns:
|
||||
id: id
|
||||
name: nombre
|
||||
account: cuenta
|
||||
countryFk: país
|
||||
nif: nif
|
||||
phone: teléfono
|
||||
retAccount: cuenta ret
|
||||
commission: comisión
|
||||
postcodeFk: código postal
|
||||
isActive: activo
|
||||
isOfficial: oficial
|
||||
isSerious: serio
|
||||
isTrucker: camionero
|
||||
note: nota
|
||||
street: calle
|
||||
city: ciudad
|
||||
provinceFk: provincia
|
||||
postCode: código postal
|
||||
payMethodFk: método pago
|
||||
payDemFk: pagar dem
|
||||
payDay: día pago
|
||||
nickname: apodo
|
||||
workerFk: trabajador
|
||||
sageTaxTypeFk: tipo de impuesto sage
|
||||
taxTypeSageFk: tipo de impuesto sage
|
||||
sageTransactionTypeFk: tipo de transacción sage
|
||||
transactionTypeSageFk: tipo de transacción sage
|
||||
sageWithholdingFk: sage con tenencia
|
||||
withholdingSageFk: sage con tenencia
|
||||
isPayMethodChecked: método pago verificado
|
||||
supplierActivityFk: actividad del proveedor
|
||||
healthRegister: registro sanitario
|
||||
isVies: vies
|
||||
supplierFk: proveedor
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "SupplierLog",
|
||||
"base": "VnModel",
|
||||
"base": "Log",
|
||||
"options": {
|
||||
"mysql": {
|
||||
"table": "supplierLog"
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
vn-two
|
||||
vn-focus
|
||||
label="Social name"
|
||||
name="socialName"
|
||||
ng-model="$ctrl.supplier.name"
|
||||
info="Only letters, numbers and spaces can be used"
|
||||
required="true"
|
||||
|
@ -50,6 +51,7 @@
|
|||
<vn-textfield
|
||||
vn-one
|
||||
label="Tax number"
|
||||
name="taxNumber"
|
||||
ng-model="$ctrl.supplier.nif"
|
||||
required="true"
|
||||
rule>
|
||||
|
@ -59,35 +61,42 @@
|
|||
<vn-textfield
|
||||
vn-one
|
||||
label="Account"
|
||||
name="account"
|
||||
ng-model="$ctrl.supplier.account"
|
||||
insertable="true"
|
||||
max-length="10"
|
||||
rule>
|
||||
</vn-textfield>
|
||||
<vn-autocomplete vn-one
|
||||
<vn-autocomplete
|
||||
vn-one
|
||||
label="Sage tax type"
|
||||
name="sageTaxType"
|
||||
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
|
||||
<vn-autocomplete
|
||||
vn-one
|
||||
label="Sage withholding"
|
||||
name="sageWithholding"
|
||||
ng-model="$ctrl.supplier.sageWithholdingFk"
|
||||
data="sageWithholdings"
|
||||
show-field="withholding"
|
||||
value-field="id"
|
||||
label="Sage withholding"
|
||||
rule>
|
||||
</vn-autocomplete>
|
||||
<vn-autocomplete vn-one
|
||||
<vn-autocomplete
|
||||
vn-one
|
||||
label="Sage transaction type"
|
||||
name="sageTransactionType"
|
||||
ng-model="$ctrl.supplier.sageTransactionTypeFk"
|
||||
url="SageTransactionTypes"
|
||||
show-field="transaction"
|
||||
value-field="id"
|
||||
label="Sage transaction type"
|
||||
search-function="{or: [{id: $search}, {transaction: {like: '%'+ $search +'%'}}]}"
|
||||
order="transaction"
|
||||
rule>
|
||||
|
@ -95,12 +104,14 @@
|
|||
</vn-autocomplete>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-autocomplete vn-one
|
||||
<vn-autocomplete
|
||||
vn-one
|
||||
label="Supplier activity"
|
||||
name="supplierActivity"
|
||||
ng-model="$ctrl.supplier.supplierActivityFk"
|
||||
data="supplierActivities"
|
||||
show-field="name"
|
||||
value-field="code"
|
||||
label="Supplier activity"
|
||||
rule>
|
||||
</vn-autocomplete>
|
||||
<vn-textfield
|
||||
|
@ -118,8 +129,10 @@
|
|||
rule
|
||||
vn-focus>
|
||||
</vn-textfield>
|
||||
<vn-datalist vn-one
|
||||
<vn-datalist
|
||||
vn-one
|
||||
label="Postcode"
|
||||
name="postcode"
|
||||
ng-model="$ctrl.supplier.postCode"
|
||||
selection="$ctrl.postcode"
|
||||
url="Postcodes/location"
|
||||
|
@ -144,8 +157,11 @@
|
|||
</vn-datalist>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-datalist vn-id="town" vn-one
|
||||
<vn-datalist
|
||||
vn-one
|
||||
vn-id="town"
|
||||
label="City"
|
||||
name="city"
|
||||
ng-model="$ctrl.supplier.city"
|
||||
selection="$ctrl.town"
|
||||
url="Towns/location"
|
||||
|
@ -159,8 +175,11 @@
|
|||
({{province.country.country}})
|
||||
</tpl-item>
|
||||
</vn-datalist>
|
||||
<vn-autocomplete vn-id="province" vn-one
|
||||
<vn-autocomplete
|
||||
vn-one
|
||||
vn-id="province"
|
||||
label="Province"
|
||||
name="province"
|
||||
ng-model="$ctrl.supplier.provinceFk"
|
||||
selection="$ctrl.province"
|
||||
data="provincesLocation"
|
||||
|
@ -172,12 +191,15 @@
|
|||
</vn-autocomplete>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-autocomplete vn-id="country" vn-two
|
||||
<vn-autocomplete
|
||||
vn-two
|
||||
vn-id="country"
|
||||
label="Country"
|
||||
name="country"
|
||||
ng-model="$ctrl.supplier.countryFk"
|
||||
data="countries"
|
||||
show-field="country"
|
||||
value-field="id"
|
||||
label="Country"
|
||||
rule>
|
||||
</vn-autocomplete>
|
||||
<vn-two class="vn-pl-xs">
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
name: expedition
|
||||
columns:
|
||||
id: id
|
||||
freightItemFk: freight item
|
||||
created: created
|
||||
counter: counter
|
||||
ticketFk: ticket
|
||||
agencyModeFk: agency
|
||||
workerFk: worker
|
||||
packagingFk: packaging
|
|
@ -0,0 +1,10 @@
|
|||
name: expedición
|
||||
columns:
|
||||
id: id
|
||||
freightItemFk: artículo de carga
|
||||
created: creado
|
||||
counter: contador
|
||||
ticketFk: ticket
|
||||
agencyModeFk: agencia
|
||||
workerFk: trabajador
|
||||
packagingFk: embalaje
|
|
@ -1,11 +1,13 @@
|
|||
concept: concept
|
||||
quantity: quantity
|
||||
price: price
|
||||
discount: discount
|
||||
reserved: reserved
|
||||
isPicked: is picked
|
||||
created: created
|
||||
originalQuantity: original quantity
|
||||
itemFk: item
|
||||
ticketFk: ticket
|
||||
saleFk: sale
|
||||
name: sale
|
||||
columns:
|
||||
concept: concept
|
||||
quantity: quantity
|
||||
price: price
|
||||
discount: discount
|
||||
reserved: reserved
|
||||
isPicked: is picked
|
||||
created: created
|
||||
originalQuantity: original quantity
|
||||
itemFk: item
|
||||
ticketFk: ticket
|
||||
saleFk: sale
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
concept: concepto
|
||||
quantity: cantidad
|
||||
price: precio
|
||||
discount: descuento
|
||||
reserved: reservado
|
||||
isPicked: esta seleccionado
|
||||
created: creado
|
||||
originalQuantity: cantidad original
|
||||
itemFk: artículo
|
||||
ticketFk: ticket
|
||||
saleFk: línea
|
||||
name: línea
|
||||
columns:
|
||||
concept: concepto
|
||||
quantity: cantidad
|
||||
price: precio
|
||||
discount: descuento
|
||||
reserved: reservado
|
||||
isPicked: esta seleccionado
|
||||
created: creado
|
||||
originalQuantity: cantidad original
|
||||
itemFk: artículo
|
||||
ticketFk: ticket
|
||||
saleFk: línea
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue