feat: Attachment files system.

This commit is contained in:
Ahmed Bouhuolia
2020-05-04 05:11:44 +02:00
parent a807cf6bb8
commit 7f06e3781c
35 changed files with 757 additions and 179 deletions

View File

@@ -10,6 +10,8 @@ exports.up = function (knex) {
table.integer('role_id').unique();
table.string('language');
table.date('last_login_at');
table.date('invite_accepted_at');
table.timestamps();
});
};

View File

@@ -0,0 +1,13 @@
exports.up = function(knex) {
return knex.schema.createTable('media_links', table => {
table.increments();
table.string('model_name');
table.integer('media_id').unsigned();
table.integer('model_id').unsigned();
})
};
exports.down = function(knex) {
return knex.schema.dropTableIfExists('media_links');
};

View File

@@ -82,7 +82,7 @@ export default {
const filter = {
filter_roles: [],
page: 1,
page_size: 10,
page_size: 999,
...req.query,
};
if (filter.stringified_filter_roles) {
@@ -90,8 +90,6 @@ export default {
}
const { Resource, View, ManualJournal } = req.models;
console.log(req.models);
const errorReasons = [];
const manualJournalsResource = await Resource.query()
.where('name', 'manual_journals')
@@ -185,6 +183,8 @@ export default {
check('entries.*.debit').optional({ nullable: true }).isNumeric().toInt(),
check('entries.*.account_id').isNumeric().toInt(),
check('entries.*.note').optional(),
check('media_ids').optional().isArray(),
check('media_ids.*').exists().isNumeric().toInt(),
],
async handler(req, res) {
const validationErrors = validationResult(req);
@@ -198,9 +198,10 @@ export default {
date: new Date(),
transaction_type: 'journal',
reference: '',
media_ids: [],
...req.body,
};
const { ManualJournal, Account } = req.models;
const { ManualJournal, Account, Media, MediaLink } = req.models;
let totalCredit = 0;
let totalDebit = 0;
@@ -233,6 +234,14 @@ export default {
const storedAccountsIds = accounts.map((account) => account.id);
if (form.media_ids.length > 0) {
const storedMedia = await Media.query().whereIn('id', form.media_ids);
const notFoundMedia = difference(form.media_ids, storedMedia.map((m) => m.id));
if (notFoundMedia.length > 0) {
errorReasons.push({ type: 'MEDIA.IDS.NOT.FOUND', code: 400, ids: notFoundMedia });
}
}
if (difference(accountsIds, storedAccountsIds).length > 0) {
errorReasons.push({ type: 'ACCOUNTS.IDS.NOT.FOUND', code: 200 });
}
@@ -279,8 +288,22 @@ export default {
journalPoster.credit(jouranlEntry);
}
});
// Save linked media to the journal model.
const bulkSaveMediaLink = [];
form.media_ids.forEach((mediaId) => {
const oper = MediaLink.query().insert({
model_name: 'Journal',
model_id: manualJournal.id,
media_id: mediaId,
});
bulkSaveMediaLink.push(oper);
});
// Saves the journal entries and accounts balance changes.
await Promise.all([
...bulkSaveMediaLink,
journalPoster.saveEntries(),
(form.status) && journalPoster.saveBalance(),
]);
@@ -313,6 +336,9 @@ export default {
},
},
/**
* Edit the given manual journal.
*/
editManualJournal: {
validation: [
param('id').exists().isNumeric().toInt(),
@@ -326,6 +352,8 @@ export default {
check('entries.*.debit').optional({ nullable: true }).isNumeric().toInt(),
check('entries.*.account_id').isNumeric().toInt(),
check('entries.*.note').optional(),
check('media_ids').optional().isArray(),
check('media_ids.*').isNumeric().toInt(),
],
async handler(req, res) {
const validationErrors = validationResult(req);
@@ -339,14 +367,17 @@ export default {
date: new Date(),
transaction_type: 'journal',
reference: '',
media_ids: [],
...req.body,
};
const { id } = req.params;
const {
ManualJournal, AccountTransaction, Account,
ManualJournal, AccountTransaction, Account, Media, MediaLink,
} = req.models;
const manualJournal = await ManualJournal.query().where('id', id).first();
const manualJournal = await ManualJournal.query()
.where('id', id)
.withGraphFetched('media').first();
if (!manualJournal) {
return res.status(4040).send({
@@ -395,6 +426,16 @@ export default {
if (difference(accountsIds, storedAccountsIds).length > 0) {
errorReasons.push({ type: 'ACCOUNTS.IDS.NOT.FOUND', code: 200 });
}
// Validate if media ids was not already exists on the storage.
if (form.media_ids.length > 0) {
const storedMedia = await Media.query().whereIn('id', form.media_ids);
const notFoundMedia = difference(form.media_ids, storedMedia.map((m) => m.id));
if (notFoundMedia.length > 0) {
errorReasons.push({ type: 'MEDIA.IDS.NOT.FOUND', code: 400, ids: notFoundMedia });
}
}
if (errorReasons.length > 0) {
return res.status(400).send({ errors: errorReasons });
}
@@ -439,7 +480,23 @@ export default {
journal.credit(jouranlEntry);
}
});
// Save links of new inserted media that associated to the journal model.
const journalMediaIds = manualJournal.media.map((m) => m.id);
const newInsertedMedia = difference(form.media_ids, journalMediaIds);
const bulkSaveMediaLink = [];
newInsertedMedia.forEach((mediaId) => {
const oper = MediaLink.query().insert({
model_name: 'Journal',
model_id: manualJournal.id,
media_id: mediaId,
});
bulkSaveMediaLink.push(oper);
});
await Promise.all([
...bulkSaveMediaLink,
journal.deleteEntries(),
journal.saveEntries(),
journal.saveBalance(),
@@ -524,7 +581,9 @@ export default {
const { id } = req.params;
const manualJournal = await ManualJournal.query()
.where('id', id).first();
.where('id', id)
.withGraphFetched('media')
.first();
if (!manualJournal) {
return res.status(404).send({
@@ -564,7 +623,9 @@ export default {
}
const { id } = req.params;
const {
ManualJournal, AccountTransaction,
ManualJournal,
AccountTransaction,
MediaLink,
} = req.models;
const manualJournal = await ManualJournal.query()
.where('id', id).first();
@@ -583,6 +644,11 @@ export default {
journal.loadEntries(transactions);
journal.removeEntries();
await MediaLink.query()
.where('model_name', 'Journal')
.where('model_id', manualJournal.id)
.delete();
await ManualJournal.query()
.where('id', manualJournal.id)
.delete();
@@ -678,7 +744,7 @@ export default {
});
}
const filter = { ...req.query };
const { ManualJournal, AccountTransaction } = req.models;
const { ManualJournal, AccountTransaction, MediaLink } = req.models;
const manualJournals = await ManualJournal.query()
.whereIn('id', filter.ids);
@@ -699,6 +765,11 @@ export default {
journal.loadEntries(transactions);
journal.removeEntries();
await MediaLink.query()
.where('model_name', 'Journal')
.whereIn('model_id', filter.ids)
.delete();
await ManualJournal.query()
.whereIn('id', filter.ids).delete();

View File

@@ -68,6 +68,9 @@ export default {
check('custom_fields.*.value').exists(),
check('note').optional(),
check('media_ids').optional().isArray(),
check('media_ids.*').exists().isNumeric().toInt(),
],
async handler(req, res) {
const validationErrors = validationResult(req);

View File

@@ -119,7 +119,7 @@ export default {
code: 'validation_error', ...validationErrors,
});
}
const { Media } = req.models;
const { Media, MediaLink } = req.models;
const { id } = req.params;
const media = await Media.query().where('id', id).first();
@@ -137,6 +137,8 @@ export default {
} catch (error) {
Logger.log('error', 'Delete item attachment file delete failed.', { error });
}
await MediaLink.query().where('media_id', media.id).delete();
await Media.query().where('id', media.id).delete();
return res.status(200).send();

View File

@@ -1,3 +1,4 @@
import { Model } from 'objection';
import TenantModel from '@/models/TenantModel';
export default class ManualJournal extends TenantModel {
@@ -7,4 +8,26 @@ export default class ManualJournal extends TenantModel {
static get tableName() {
return 'manual_journals';
}
/**
* Relationship mapping.
*/
static get relationMappings() {
const Media = require('@/models/Media');
return {
media: {
relation: Model.ManyToManyRelation,
modelClass: this.relationBindKnex(Media.default),
join: {
from: 'manual_journals.id',
through: {
from: 'media_links.model_id',
to: 'media_links.media_id',
},
to: 'media.id',
}
}
};
}
}

View File

@@ -0,0 +1,10 @@
import TenantModel from '@/models/TenantModel';
export default class MediaLink extends TenantModel {
/**
* Table name
*/
static get tableName() {
return 'media_links';
}
}

View File

@@ -180,7 +180,7 @@ export default class JournalPoster {
async deleteEntries() {
if (this.deletedEntriesIds.length > 0) {
await AccountTransaction.query()
await AccountTransaction.tenant().query()
.whereIn('id', this.deletedEntriesIds)
.delete();
}

View File

@@ -10,8 +10,10 @@ exports.up = function (knex) {
table.boolean('active');
table.integer('role_id').unique();
table.string('language');
table.date('last_login_at');
table.integer('tenant_id').unsigned();
table.date('last_login_at');
table.timestamps();
}).then(() => {
// knex.seed.run({