Merge pull request '4925-item.tags' (!1241) from 4925-item.tags into dev
gitea/salix/pipeline/head This commit looks good Details

Reviewed-on: #1241
Reviewed-by: Juan Ferrer <juan@verdnatura.es>
Reviewed-by: Alex Moreno <alexm@verdnatura.es>
This commit is contained in:
Vicent Llopis 2023-01-27 08:16:00 +00:00
commit ce1bdaec17
8 changed files with 234 additions and 31 deletions

View File

@ -15,7 +15,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- -
### Fixed ### Fixed
- - (Artículos -> Etiquetas) Permite intercambiar la relevancia entre dos etiquetas.
## [2302.01] - 2023-01-26 ## [2302.01] - 2023-01-26

View File

@ -0,0 +1,3 @@
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
VALUES
('Tag', 'onSubmit', 'WRITE', 'ALLOW', 'ROLE', 'employee');

View File

@ -147,28 +147,17 @@ export default class CrudModel extends ModelProxy {
this.moreRows = null; this.moreRows = null;
} }
/** getChanges() {
* Saves current changes on the server. if (!this.isChanged) return null;
*
* @return {Promise} The save request promise
*/
save() {
if (!this.isChanged)
return this.$q.resolve();
let deletes = []; const deletes = [];
let updates = []; const updates = [];
let creates = []; const creates = [];
let orgDeletes = [];
let orgUpdates = [];
let orgCreates = [];
let pk = this.primaryKey; const pk = this.primaryKey;
for (let row of this.removed) { for (let row of this.removed)
deletes.push(row.$orgRow[pk]); deletes.push(row.$orgRow[pk]);
orgDeletes.push(row);
}
for (let row of this.data) { for (let row of this.data) {
if (row.$isNew) { if (row.$isNew) {
@ -178,7 +167,6 @@ export default class CrudModel extends ModelProxy {
data[prop] = row[prop]; data[prop] = row[prop];
} }
creates.push(row); creates.push(row);
orgCreates.push(row);
} else if (row.$oldData) { } else if (row.$oldData) {
let data = {}; let data = {};
for (let prop in row.$oldData) for (let prop in row.$oldData)
@ -187,28 +175,38 @@ export default class CrudModel extends ModelProxy {
data, data,
where: {[pk]: row.$orgRow[pk]} where: {[pk]: row.$orgRow[pk]}
}); });
orgUpdates.push(row);
} }
} }
let changes = {deletes, updates, creates}; const changes = {deletes, updates, creates};
for (let prop in changes) { for (let prop in changes) {
if (changes[prop].length === 0) if (changes[prop].length === 0)
changes[prop] = undefined; changes[prop] = undefined;
} }
if (!changes) return changes;
return this.$q.resolve(); }
/**
* Saves current changes on the server.
*
* @return {Promise} The save request promise
*/
save() {
const pk = this.primaryKey;
const changes = this.getChanges();
if (!changes) return this.$q.resolve();
const creates = changes.creates || [];
let url = this.saveUrl ? this.saveUrl : `${this._url}/crud`; let url = this.saveUrl ? this.saveUrl : `${this._url}/crud`;
return this.$http.post(url, changes) return this.$http.post(url, changes)
.then(res => { .then(res => {
const created = res.data; const created = res.data;
// Apply new data to created instances // Apply new data to created instances
for (let i = 0; i < orgCreates.length; i++) { for (let i = 0; i < creates.length; i++) {
const row = orgCreates[i]; const row = creates[i];
row[pk] = created[i][pk]; row[pk] = created[i][pk];
for (let prop in row) { for (let prop in row) {

View File

@ -0,0 +1,93 @@
module.exports = function(Self) {
Self.remoteMethodCtx('onSubmit', {
description: 'Save model changes',
accessType: 'WRITE',
accepts: [
{
arg: 'creates',
type: ['object'],
description: 'The itemTags records to create'
}, {
arg: 'deletes',
type: ['number'],
description: 'The itemTags ids to delete'
}, {
arg: 'updates',
type: ['object'],
description: 'The itemTags records to update'
}, {
arg: 'maxPriority',
type: 'number',
description: 'The maxPriority value'
}
],
returns: {
root: true,
type: 'object'
},
http: {
verb: 'PATCH',
path: '/onSubmit'
}
});
Self.onSubmit = async(ctx, options) => {
const models = Self.app.models;
const args = ctx.args;
let tx;
const myOptions = {};
if (typeof options == 'object')
Object.assign(myOptions, options);
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
try {
const promises = [];
if (args.deletes) {
for (const itemTagId of args.deletes) {
const itemTagDeleted = models.ItemTag.destroyById(itemTagId, myOptions);
promises.push(itemTagDeleted);
}
}
if (args.updates) {
for (const row of args.updates) {
if (row.data.priority) {
const itemTag = await models.ItemTag.findById(row.where.id, null, myOptions);
const itemTagUpdatedPriority = itemTag.updateAttributes({
priority: row.data.priority + args.maxPriority
}, myOptions);
promises.push(itemTagUpdatedPriority);
}
}
for (const row of args.updates) {
const itemTag = await models.ItemTag.findById(row.where.id, null, myOptions);
const itemTagUpdated = itemTag.updateAttributes(row.data, myOptions);
promises.push(itemTagUpdated);
}
}
if (args.creates) {
for (const itemTag of args.creates) {
const newItemTag = models.ItemTag.create(itemTag, myOptions);
promises.push(newItemTag);
}
}
const resolvedPromises = await Promise.all(promises);
if (tx) await tx.commit();
return resolvedPromises;
} catch (e) {
if (tx) await tx.rollback();
throw e;
}
};
};

View File

@ -0,0 +1,100 @@
const models = require('vn-loopback/server/server').models;
describe('tag onSubmit()', () => {
it('should delete a tag', async() => {
const tx = await models.Item.beginTransaction({});
const options = {transaction: tx};
try {
const deletes = [40];
const ctx = {
args: {
deletes: deletes
}
};
const result = await models.Tag.onSubmit(ctx, options);
expect(result.length).toEqual(1);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
it('should update a tag', async() => {
const tx = await models.Item.beginTransaction({});
const options = {transaction: tx};
try {
const updates = [{data: {value: 'Container Test'}, where: {id: 36}}];
const ctx = {
args: {
updates: updates
}
};
const result = await models.Tag.onSubmit(ctx, options);
expect(result.length).toEqual(1);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
it('should create a tag', async() => {
const tx = await models.Item.beginTransaction({});
const options = {transaction: tx};
try {
const creates = [{
'itemFk': '6',
'priority': 8,
'$orgIndex': null,
'$oldData': null,
'$isNew': true,
'tagFk': 3,
'value': 'madera'
}];
const ctx = {
args: {
creates: creates
}
};
const result = await models.Tag.onSubmit(ctx, options);
expect(result.length).toEqual(1);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
it('should swap priority for two tags', async() => {
const tx = await models.Item.beginTransaction({});
const options = {transaction: tx};
try {
const updates = [
{data: {priority: 2}, where: {id: 36}},
{data: {priority: 1}, where: {id: 37}}
];
const ctx = {
args: {
updates: updates,
maxPriority: 7,
}
};
const result = await models.Tag.onSubmit(ctx, options);
expect(result.length).toEqual(4);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
});

View File

@ -1,3 +1,4 @@
module.exports = Self => { module.exports = Self => {
require('../methods/tag/filterValue')(Self); require('../methods/tag/filterValue')(Self);
require('../methods/tag/onSubmit')(Self);
}; };

View File

@ -19,7 +19,7 @@
data="tags" data="tags"
auto-load="true"> auto-load="true">
</vn-crud-model> </vn-crud-model>
<form name="form" ng-submit="$ctrl.onSubmit()" class="vn-w-md"> <form name="form" class="vn-w-md">
<vn-card class="vn-pa-lg"> <vn-card class="vn-pa-lg">
<vn-horizontal ng-repeat="itemTag in $ctrl.itemTags"> <vn-horizontal ng-repeat="itemTag in $ctrl.itemTags">
<vn-autocomplete vn-two vn-id="tag" vn-focus <vn-autocomplete vn-two vn-id="tag" vn-focus
@ -74,6 +74,7 @@
</vn-card> </vn-card>
<vn-button-bar> <vn-button-bar>
<vn-submit <vn-submit
ng-click="$ctrl.onSubmit()"
disabled="!watcher.dataChanged()" disabled="!watcher.dataChanged()"
label="Save"> label="Save">
</vn-submit> </vn-submit>

View File

@ -29,11 +29,17 @@ class Controller extends Section {
} }
onSubmit() { onSubmit() {
this.$.watcher.check(); const changes = this.$.model.getChanges();
this.$.model.save().then(() => { const data = {
creates: changes.creates,
deletes: changes.deletes,
updates: changes.updates,
maxPriority: this.getHighestPriority()
};
this.$http.patch(`Tags/onSubmit`, data).then(() => {
this.$.model.refresh();
this.$.watcher.notifySaved(); this.$.watcher.notifySaved();
this.$.watcher.updateOriginalData(); this.$.watcher.updateOriginalData();
this.card.reload();
}); });
} }
} }