mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-20 23:00:34 +00:00
Merge pull request #638 from bigcapitalhq/use-standard-date-format-export
fix: use standard ISO 8601 format for exported data
This commit is contained in:
@@ -2,6 +2,7 @@ import moment from 'moment';
|
|||||||
import * as R from 'ramda';
|
import * as R from 'ramda';
|
||||||
import { includes, isFunction, isObject, isUndefined, omit } from 'lodash';
|
import { includes, isFunction, isObject, isUndefined, omit } from 'lodash';
|
||||||
import { formatNumber, sortObjectKeysAlphabetically } from 'utils';
|
import { formatNumber, sortObjectKeysAlphabetically } from 'utils';
|
||||||
|
import { EXPORT_DTE_FORMAT } from '@/services/Export/constants';
|
||||||
|
|
||||||
export class Transformer {
|
export class Transformer {
|
||||||
public context: any;
|
public context: any;
|
||||||
@@ -156,14 +157,26 @@ export class Transformer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Format date.
|
||||||
* @param date
|
* @param {} date
|
||||||
* @returns
|
* @param {string} format -
|
||||||
|
* @returns {}
|
||||||
*/
|
*/
|
||||||
protected formatDate(date) {
|
protected formatDate(date, format?: string) {
|
||||||
return date ? moment(date).format(this.dateFormat) : '';
|
// Use the export date format if the async operation is in exporting,
|
||||||
|
// otherwise use the given or default format.
|
||||||
|
const _format = this.context.exportAls.isExport
|
||||||
|
? EXPORT_DTE_FORMAT
|
||||||
|
: format || this.dateFormat;
|
||||||
|
|
||||||
|
return date ? moment(date).format(_format) : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param date
|
||||||
|
* @returns {}
|
||||||
|
*/
|
||||||
protected formatDateFromNow(date) {
|
protected formatDateFromNow(date) {
|
||||||
return date ? moment(date).fromNow(true) : '';
|
return date ? moment(date).fromNow(true) : '';
|
||||||
}
|
}
|
||||||
@@ -171,7 +184,7 @@ export class Transformer {
|
|||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param number
|
* @param number
|
||||||
* @returns
|
* @returns {}
|
||||||
*/
|
*/
|
||||||
protected formatNumber(number, props?) {
|
protected formatNumber(number, props?) {
|
||||||
return formatNumber(number, { money: false, ...props });
|
return formatNumber(number, { money: false, ...props });
|
||||||
@@ -181,7 +194,7 @@ export class Transformer {
|
|||||||
*
|
*
|
||||||
* @param money
|
* @param money
|
||||||
* @param options
|
* @param options
|
||||||
* @returns
|
* @returns {}
|
||||||
*/
|
*/
|
||||||
protected formatMoney(money, options?) {
|
protected formatMoney(money, options?) {
|
||||||
return formatNumber(money, {
|
return formatNumber(money, {
|
||||||
|
|||||||
@@ -3,12 +3,17 @@ import { isNull } from 'lodash';
|
|||||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||||
import { TenantMetadata } from '@/system/models';
|
import { TenantMetadata } from '@/system/models';
|
||||||
import { Transformer } from './Transformer';
|
import { Transformer } from './Transformer';
|
||||||
|
import { ImportAls } from '@/services/Import/ImportALS';
|
||||||
|
import { ExportAls } from '@/services/Export/ExportAls';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class TransformerInjectable {
|
export class TransformerInjectable {
|
||||||
@Inject()
|
@Inject()
|
||||||
private tenancy: HasTenancyService;
|
private tenancy: HasTenancyService;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private exportAls: ExportAls;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the application context of all tenant transformers.
|
* Retrieves the application context of all tenant transformers.
|
||||||
* @param {number} tenantId
|
* @param {number} tenantId
|
||||||
@@ -17,10 +22,12 @@ export class TransformerInjectable {
|
|||||||
async getApplicationContext(tenantId: number) {
|
async getApplicationContext(tenantId: number) {
|
||||||
const i18n = this.tenancy.i18n(tenantId);
|
const i18n = this.tenancy.i18n(tenantId);
|
||||||
const organization = await TenantMetadata.query().findOne({ tenantId });
|
const organization = await TenantMetadata.query().findOne({ tenantId });
|
||||||
|
const exportAls = this.exportAls;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
organization,
|
organization,
|
||||||
i18n,
|
i18n,
|
||||||
|
exportAls,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
48
packages/server/src/services/Export/ExportAls.ts
Normal file
48
packages/server/src/services/Export/ExportAls.ts
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import { Service } from 'typedi';
|
||||||
|
import { AsyncLocalStorage } from 'async_hooks';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export class ExportAls {
|
||||||
|
private als: AsyncLocalStorage<Map<string, any>>;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.als = new AsyncLocalStorage();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs a callback function within the context of a new AsyncLocalStorage store.
|
||||||
|
* @param callback The function to be executed within the AsyncLocalStorage context.
|
||||||
|
* @returns The result of the callback function.
|
||||||
|
*/
|
||||||
|
public run<T>(callback: () => T): T {
|
||||||
|
return this.als.run<T>(new Map(), () => {
|
||||||
|
this.markAsExport();
|
||||||
|
|
||||||
|
return callback();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the current AsyncLocalStorage store.
|
||||||
|
* @returns The current store or undefined if not in a valid context.
|
||||||
|
*/
|
||||||
|
public getStore(): Map<string, any> | undefined {
|
||||||
|
return this.als.getStore();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks the current context as an export operation.
|
||||||
|
* @param flag Boolean flag to set or unset the export status. Defaults to true.
|
||||||
|
*/
|
||||||
|
public markAsExport(flag: boolean = true): void {
|
||||||
|
const store = this.getStore();
|
||||||
|
store?.set('isExport', flag);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Checks if the current context is an export operation.
|
||||||
|
* @returns {boolean} True if the context is an export operation, false otherwise.
|
||||||
|
*/
|
||||||
|
public get isExport(): boolean {
|
||||||
|
return !!this.getStore()?.get('isExport');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ import { Errors, ExportFormat } from './common';
|
|||||||
import { IModelMeta, IModelMetaColumn } from '@/interfaces';
|
import { IModelMeta, IModelMetaColumn } from '@/interfaces';
|
||||||
import { flatDataCollections, getDataAccessor } from './utils';
|
import { flatDataCollections, getDataAccessor } from './utils';
|
||||||
import { ExportPdf } from './ExportPdf';
|
import { ExportPdf } from './ExportPdf';
|
||||||
|
import { ExportAls } from './ExportAls';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class ExportResourceService {
|
export class ExportResourceService {
|
||||||
@@ -22,13 +23,33 @@ export class ExportResourceService {
|
|||||||
@Inject()
|
@Inject()
|
||||||
private exportPdf: ExportPdf;
|
private exportPdf: ExportPdf;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private exportAls: ExportAls;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {string} resourceName
|
||||||
|
* @param {ExportFormat} format
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
public async export(
|
||||||
|
tenantId: number,
|
||||||
|
resourceName: string,
|
||||||
|
format: ExportFormat = ExportFormat.Csv
|
||||||
|
) {
|
||||||
|
return this.exportAls.run(() =>
|
||||||
|
this.exportAlsRun(tenantId, resourceName, format)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exports the given resource data through csv, xlsx or pdf.
|
* Exports the given resource data through csv, xlsx or pdf.
|
||||||
* @param {number} tenantId - Tenant id.
|
* @param {number} tenantId - Tenant id.
|
||||||
* @param {string} resourceName - Resource name.
|
* @param {string} resourceName - Resource name.
|
||||||
* @param {ExportFormat} format - File format.
|
* @param {ExportFormat} format - File format.
|
||||||
*/
|
*/
|
||||||
public async export(
|
public async exportAlsRun(
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
resourceName: string,
|
resourceName: string,
|
||||||
format: ExportFormat = ExportFormat.Csv
|
format: ExportFormat = ExportFormat.Csv
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
export const EXPORT_SIZE_LIMIT = 9999999;
|
export const EXPORT_SIZE_LIMIT = 9999999;
|
||||||
|
export const EXPORT_DTE_FORMAT = 'YYYY-MM-DD';
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ export class ImportAls {
|
|||||||
constructor() {
|
constructor() {
|
||||||
this.als = new AsyncLocalStorage();
|
this.als = new AsyncLocalStorage();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Runs a callback function within the context of a new AsyncLocalStorage store.
|
* Runs a callback function within the context of a new AsyncLocalStorage store.
|
||||||
* @param callback The function to be executed within the AsyncLocalStorage context.
|
* @param callback The function to be executed within the AsyncLocalStorage context.
|
||||||
@@ -82,7 +83,7 @@ export class ImportAls {
|
|||||||
* Checks if the current context is an import operation.
|
* Checks if the current context is an import operation.
|
||||||
* @returns {boolean} True if the context is an import operation, false otherwise.
|
* @returns {boolean} True if the context is an import operation, false otherwise.
|
||||||
*/
|
*/
|
||||||
public isImport(): boolean {
|
public get isImport(): boolean {
|
||||||
return !!this.getStore()?.get('isImport');
|
return !!this.getStore()?.get('isImport');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,7 +91,7 @@ export class ImportAls {
|
|||||||
* Checks if the current context is an import commit operation.
|
* Checks if the current context is an import commit operation.
|
||||||
* @returns {boolean} True if the context is an import commit operation, false otherwise.
|
* @returns {boolean} True if the context is an import commit operation, false otherwise.
|
||||||
*/
|
*/
|
||||||
public isImportCommit(): boolean {
|
public get isImportCommit(): boolean {
|
||||||
return !!this.getStore()?.get('isImportCommit');
|
return !!this.getStore()?.get('isImportCommit');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,7 +99,7 @@ export class ImportAls {
|
|||||||
* Checks if the current context is an import preview operation.
|
* Checks if the current context is an import preview operation.
|
||||||
* @returns {boolean} True if the context is an import preview operation, false otherwise.
|
* @returns {boolean} True if the context is an import preview operation, false otherwise.
|
||||||
*/
|
*/
|
||||||
public isImportPreview(): boolean {
|
public get isImportPreview(): boolean {
|
||||||
return !!this.getStore()?.get('isImportPreview');
|
return !!this.getStore()?.get('isImportPreview');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ export default class InventorySubscriber {
|
|||||||
inventoryTransactions,
|
inventoryTransactions,
|
||||||
trx,
|
trx,
|
||||||
}: IInventoryTransactionsCreatedPayload) => {
|
}: IInventoryTransactionsCreatedPayload) => {
|
||||||
const inImportPreviewScope = this.importAls.isImportPreview();
|
const inImportPreviewScope = this.importAls.isImportPreview;
|
||||||
|
|
||||||
// Avoid running the cost items job if the async process is in import preview.
|
// Avoid running the cost items job if the async process is in import preview.
|
||||||
if (inImportPreviewScope) return;
|
if (inImportPreviewScope) return;
|
||||||
|
|||||||
Reference in New Issue
Block a user