mirror of
https://github.com/valitydev/koffing.git
synced 2024-11-06 09:15:20 +00:00
FE-672: documents refactoring (#220)
This commit is contained in:
parent
2cd7f01551
commit
54ce986a0b
@ -1,10 +1,15 @@
|
||||
import { FileMeta } from './file-meta';
|
||||
|
||||
export enum ReportType {
|
||||
provisionOfService = 'provisionOfService',
|
||||
paymentRegistry = 'paymentRegistry'
|
||||
}
|
||||
|
||||
export class Report {
|
||||
public id: number;
|
||||
public createdAt: string;
|
||||
public fromTime: string;
|
||||
public toTime: string;
|
||||
public type: string;
|
||||
public type: ReportType;
|
||||
public files: FileMeta[];
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ import {
|
||||
InvoiceSearchResult,
|
||||
PaymentSearchResult,
|
||||
PayoutSearchResult,
|
||||
RefundsSearchResult
|
||||
RefundsSearchResult, Report
|
||||
} from './model';
|
||||
|
||||
@Injectable()
|
||||
@ -52,7 +52,7 @@ export class SearchService {
|
||||
.map((res) => res.json());
|
||||
}
|
||||
|
||||
public getReports(shopID: string, reportParams: SearchReportParams): Observable<any> { // todo type
|
||||
public getReports(shopID: string, reportParams: SearchReportParams): Observable<Report[]> {
|
||||
const search = this.toSearchParams(reportParams);
|
||||
return this.http.get(`${this.config.capiUrl}/shops/${shopID}/reports`, {search})
|
||||
.map((res) => res.json());
|
||||
|
@ -3,9 +3,9 @@
|
||||
.x_panel
|
||||
.x_content
|
||||
ul.nav.nav-tabs.bar_tabs
|
||||
li.hand-cursor([routerLink]="['reports']", [routerLinkActive]="['active']")
|
||||
li.hand-cursor([routerLink]="['reports', reportTypes.provisionOfService]", [routerLinkActive]="['active']")
|
||||
a Акты
|
||||
li.hand-cursor([routerLink]="['registry']", [routerLinkActive]="['active']")
|
||||
li.hand-cursor([routerLink]="['reports', reportTypes.paymentRegistry]", [routerLinkActive]="['active']")
|
||||
a Реестр операций
|
||||
.tab-content
|
||||
router-outlet
|
||||
|
@ -1,8 +1,9 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { ReportType } from 'koffing/backend';
|
||||
|
||||
@Component({
|
||||
templateUrl: 'documents.component.pug'
|
||||
})
|
||||
export class DocumentsComponent {
|
||||
|
||||
public reportTypes = ReportType;
|
||||
}
|
||||
|
@ -5,11 +5,10 @@ import { BrowserModule } from '@angular/platform-browser';
|
||||
import { CommonModule } from 'koffing/common/common.module';
|
||||
import { BackendModule } from 'koffing/backend/backend.module';
|
||||
import { DocumentsComponent } from './documents.component';
|
||||
import { SearchReportsResultComponent } from './reports/search-result/search-reports-result.component';
|
||||
import { ReportFilesComponent } from './reports/search-result/report-files/report-files.component';
|
||||
import { ReportTypePipe } from './reports/search-result/report-type.pipe';
|
||||
import { ReportsComponent } from './reports/reports.component';
|
||||
import { SearchReportsResultComponent } from './reports/search-reports-result/search-reports-result.component';
|
||||
import { ReportFilesComponent } from './reports/search-reports-result/report-files/report-files.component';
|
||||
import { ReportTypePipe } from './reports/search-reports-result/report-type.pipe';
|
||||
import { RegistryComponent } from './registry/registry.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@ -20,11 +19,10 @@ import { RegistryComponent } from './registry/registry.component';
|
||||
],
|
||||
declarations: [
|
||||
DocumentsComponent,
|
||||
ReportsComponent,
|
||||
SearchReportsResultComponent,
|
||||
ReportFilesComponent,
|
||||
ReportTypePipe,
|
||||
RegistryComponent,
|
||||
ReportsComponent
|
||||
]
|
||||
})
|
||||
export class DocumentsModule { }
|
||||
|
@ -1,8 +0,0 @@
|
||||
export class Cell {
|
||||
public v: any;
|
||||
public t: string;
|
||||
public s: object;
|
||||
public w: string;
|
||||
public h: string;
|
||||
public r: string;
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { saveAs } from 'file-saver';
|
||||
|
||||
import { Cell } from './cell';
|
||||
|
||||
declare const XLSX: any;
|
||||
|
||||
@Injectable()
|
||||
export class ExcelService {
|
||||
|
||||
public worksheetFromArrayOfArrays(data: any[], offsetRow?: number, cellStyle?: object): object {
|
||||
offsetRow = offsetRow || 0;
|
||||
const ws = {};
|
||||
const range = {s: {c: 10000000, r: 10000000}, e: {c: 0, r: 0 }};
|
||||
for (let R = 0; R !== data.length; ++R) {
|
||||
for (let C = 0; C !== data[R].length; ++C) {
|
||||
const cell = new Cell();
|
||||
cell.v = data[R][C];
|
||||
if (cell.v === null) {
|
||||
continue;
|
||||
}
|
||||
if (typeof cell.v === 'number') {
|
||||
cell.t = 'n';
|
||||
} else if (typeof cell.v === 'boolean') {
|
||||
cell.t = 'b';
|
||||
} else {
|
||||
cell.t = 's';
|
||||
}
|
||||
if (cellStyle) {
|
||||
cell.s = cellStyle;
|
||||
}
|
||||
const cellRef = XLSX.utils.encode_cell({r: R + offsetRow, c: C});
|
||||
ws[cellRef] = cell;
|
||||
|
||||
if (range.s.r > R) { range.s.r = R; }
|
||||
if (range.s.c > C) { range.s.c = C; }
|
||||
if (range.e.r < R) { range.e.r = R; }
|
||||
if (range.e.c < C) { range.e.c = C; }
|
||||
}
|
||||
}
|
||||
if (range.s.c < 10000000) {
|
||||
range.e.r = range.e.r + offsetRow;
|
||||
ws['!ref'] = XLSX.utils.encode_range(range.s, range.e);
|
||||
}
|
||||
return ws;
|
||||
}
|
||||
|
||||
public getEncodeRange(countRows: number, countsColumns: number): string {
|
||||
const range = {s: {r: 0, c: 0}, e: {r: countRows - 1, c: countsColumns - 1}};
|
||||
return XLSX.utils.encode_range(range.s, range.e);
|
||||
}
|
||||
|
||||
public saveAsXLSX(workbook: any, fileName: string) {
|
||||
function s2ab(s: any) {
|
||||
const buf = new ArrayBuffer(s.length);
|
||||
const view = new Uint8Array(buf);
|
||||
for (let i = 0; i !== s.length; ++i) {
|
||||
view[i] = s.charCodeAt(i) & 0xFF;
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
const wbout = XLSX.write(workbook, {bookType: 'xlsx', bookSST: false, type: 'binary'});
|
||||
saveAs(new Blob([s2ab(wbout)], {type: 'application/octet-stream'}), `${fileName}.xlsx`);
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
export class Workbook {
|
||||
public SheetNames: string[];
|
||||
public Sheets: object;
|
||||
|
||||
constructor() {
|
||||
this.SheetNames = [];
|
||||
this.Sheets = {};
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
export class PaymentRegistryItem {
|
||||
public paymentDate: string;
|
||||
public invoiceID: string;
|
||||
public amount: number;
|
||||
public fee: number;
|
||||
public userEmail: string;
|
||||
public product: string;
|
||||
public description: string;
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
export class RefundRegistryItem {
|
||||
public refundDate: string;
|
||||
public invoiceID: string;
|
||||
public amount: number;
|
||||
}
|
@ -1,171 +0,0 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ceil, concat, get } from 'lodash';
|
||||
import { Observable, Observer } from 'rxjs';
|
||||
|
||||
import {
|
||||
PAYMENT_STATUS,
|
||||
Invoice,
|
||||
Payment,
|
||||
RussianLegalEntity,
|
||||
PaymentResourcePayer,
|
||||
SearchPaymentsParams,
|
||||
PaymentSearchResult,
|
||||
SearchInvoicesParams,
|
||||
RefundsSearchResult,
|
||||
Refund
|
||||
} from 'koffing/backend';
|
||||
import { ShopService } from 'koffing/backend/shop.service';
|
||||
import { ContractService } from 'koffing/backend/contract.service';
|
||||
import { SearchService } from 'koffing/backend/search.service';
|
||||
import { SearchParams } from './search-params';
|
||||
import { Registry } from './registry';
|
||||
import { PaymentRegistryItem } from './payment-registry-item';
|
||||
import { RefundRegistryItem } from './refund-registry-item';
|
||||
import { InvoiceSearchResult } from 'koffing/backend/model/invoice-search-result';
|
||||
import { SearchRefundsParams } from 'koffing/backend/requests';
|
||||
|
||||
type SearchFn<P, R> = (shopID: string, paymentsParams: P) => Observable<R>;
|
||||
|
||||
@Injectable()
|
||||
export class RegistryDataService {
|
||||
|
||||
private limit: number = 1000;
|
||||
|
||||
constructor(public searchService: SearchService,
|
||||
private contractService: ContractService,
|
||||
private shopService: ShopService) {
|
||||
}
|
||||
|
||||
public getRegistry(shopID: string, fromTime: Date, toTime: Date): Observable<Registry> {
|
||||
const paymentsSearchParams: SearchPaymentsParams = {
|
||||
fromTime,
|
||||
toTime,
|
||||
limit: this.limit,
|
||||
paymentStatus: PAYMENT_STATUS.captured
|
||||
};
|
||||
const refundsSearchParams: SearchRefundsParams = {
|
||||
fromTime,
|
||||
toTime,
|
||||
limit: this.limit,
|
||||
refundStatus: 'succeeded'
|
||||
};
|
||||
const invoicesSearchParams = new SearchParams(fromTime, toTime, this.limit);
|
||||
const payments$ = this.loadAllData<SearchPaymentsParams, PaymentSearchResult, Payment>(this.searchService.searchPayments, this.searchService, shopID, paymentsSearchParams);
|
||||
const refunds$ = this.loadAllOffsetData<SearchRefundsParams, RefundsSearchResult, Refund>(this.searchService.searchRefunds, this.searchService, shopID, refundsSearchParams);
|
||||
const invoices$ = this.loadAllData<SearchInvoicesParams, InvoiceSearchResult, Invoice>(this.searchService.searchInvoices, this.searchService, shopID, invoicesSearchParams);
|
||||
const shop$ = this.shopService.getShopByID(shopID);
|
||||
return Observable.create((observer: Observer<Registry>) => {
|
||||
Observable.forkJoin([payments$, refunds$, invoices$, shop$]).subscribe((response: any[]) => {
|
||||
const [payments, refunds, invoices, shop] = response;
|
||||
const contract$ = this.contractService.getContractByID(shop.contractID);
|
||||
return contract$.subscribe((contract) => {
|
||||
const {registeredName: client} = contract.contractor as RussianLegalEntity;
|
||||
const capturedPaymentItems = this.getPaymentRegistryItems(payments, invoices);
|
||||
const refundedPaymentItems = this.getRefundRegistryItems(refunds);
|
||||
observer.next(new Registry(fromTime, toTime, client, capturedPaymentItems, refundedPaymentItems));
|
||||
observer.complete();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// problem with parallel load - some payments may not load (because of historicity, both parts may not contain payment)
|
||||
private loadAllDataParallel<Params extends { fromTime: Date, toTime: Date }, Result extends { continuationToken?: string, result: Item[] }, Item>(fn: SearchFn<Params, Result>, context: any, shopID: string, params: Params, countRequests: number = 1): Observable<Item[]> {
|
||||
const fullIntervalMs = params.toTime.getTime() - params.fromTime.getTime();
|
||||
if (fullIntervalMs < 24 * 60 * 60 * 1000 || fullIntervalMs < countRequests * 1000) {
|
||||
countRequests = 1;
|
||||
}
|
||||
const intervalMs = Math.floor(fullIntervalMs / countRequests / 1000) * 1000;
|
||||
const streamRequests$: Array<Observable<Item[]>> = [];
|
||||
for (let i = 1, lastToTime = params.fromTime; i <= countRequests; i++) {
|
||||
const nextParams = Object.assign({}, params, {});
|
||||
nextParams.fromTime = lastToTime;
|
||||
nextParams.toTime = i === countRequests ? params.toTime : new Date(lastToTime.getTime() + intervalMs);
|
||||
lastToTime = nextParams.toTime;
|
||||
const request$ = this.loadAllData<Params, Result, Item>(fn, context, shopID, nextParams);
|
||||
streamRequests$.push(request$);
|
||||
}
|
||||
let searchData: Item[] = [];
|
||||
return Observable.create((observer: Observer<Item[]>) => {
|
||||
Observable.forkJoin(streamRequests$).subscribe((streamData: Item[][]) => {
|
||||
streamData.forEach((data) => searchData = concat(searchData, data));
|
||||
observer.next(searchData);
|
||||
observer.complete();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private loadAllData<Params extends { fromTime: Date, toTime: Date }, Result extends { continuationToken?: string, result: Item[] }, Item>(fn: SearchFn<Params, Result>, context: any, shopID: string, params: Params): Observable<Item[]> {
|
||||
return Observable.create((observer: Observer<Item[]>) => {
|
||||
const request$ = fn.apply(context, [shopID, params]);
|
||||
request$.subscribe((streamData: Result) => {
|
||||
if (streamData.continuationToken) {
|
||||
const nextParams: Params = Object.assign({}, params, {continuationToken: streamData.continuationToken});
|
||||
this.loadAllData(fn, context, shopID, nextParams).subscribe(
|
||||
(result: Item[]) => {
|
||||
const data = concat(streamData.result, result);
|
||||
observer.next(data);
|
||||
observer.complete();
|
||||
}
|
||||
);
|
||||
} else {
|
||||
observer.next(streamData.result);
|
||||
observer.complete();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private loadAllOffsetData<Params extends { offset?: number, limit: number }, Result extends { totalCount: number, result: Item[] }, Item>(fn: SearchFn<Params, Result>, context: any, shopID: string, params: Params): Observable<Item[]> {
|
||||
return Observable.create((observer: Observer<Item[]>) => {
|
||||
fn.apply(context, [shopID, params]).subscribe((response: Result) => {
|
||||
let searchData: Item[] = response.result;
|
||||
const countRequests = ceil(response.totalCount / params.limit);
|
||||
if (countRequests > 1) {
|
||||
const streamRequests$: Array<Observable<Result>> = [];
|
||||
for (let i = 1; i < countRequests; i++) {
|
||||
const nextParams = Object.assign({}, params, {offset: params.limit * i});
|
||||
const request$ = fn.apply(context, [shopID, nextParams]);
|
||||
streamRequests$.push(request$);
|
||||
}
|
||||
Observable.forkJoin(streamRequests$).subscribe((streamData: Result[]) => {
|
||||
streamData.forEach((data) => searchData = concat(searchData, data.result));
|
||||
observer.next(searchData);
|
||||
observer.complete();
|
||||
});
|
||||
} else {
|
||||
observer.next(searchData);
|
||||
observer.complete();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private getPaymentRegistryItems(payments: Payment[], invoices: Invoice[]): PaymentRegistryItem[] {
|
||||
// optimization: get 'map-object'[key] much faster than 'array'.find({id} => id === key)
|
||||
const invoicesObject: { [id: string]: Invoice } = invoices.reduce((map, invoice) => {
|
||||
map[invoice.id] = invoice;
|
||||
return map;
|
||||
}, {});
|
||||
return payments.map((payment) => {
|
||||
const invoice = invoicesObject[payment.invoiceID];
|
||||
return {
|
||||
invoiceID: `${payment.invoiceID}.${payment.id}`,
|
||||
paymentDate: payment.statusChangedAt || payment.createdAt,
|
||||
amount: payment.amount,
|
||||
fee: payment.fee,
|
||||
userEmail: payment.payer.payerType === 'PaymentResourcePayer' ? (payment.payer as PaymentResourcePayer).contactInfo.email : '',
|
||||
product: get(invoice, 'product', ''),
|
||||
description: get(invoice, 'description', ''),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private getRefundRegistryItems(refunds: Refund[]): RefundRegistryItem[] {
|
||||
return refunds.map((refund) => ({
|
||||
invoiceID: `${refund.invoiceID}.${refund.paymentID}`,
|
||||
refundDate: refund.createdAt,
|
||||
amount: refund.amount
|
||||
}));
|
||||
}
|
||||
}
|
@ -1,157 +0,0 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { merge, map } from 'lodash';
|
||||
import * as moment from 'moment';
|
||||
|
||||
import { CurrencyService } from 'koffing/common/currency.service';
|
||||
import { Registry } from './registry';
|
||||
import { PaymentRegistryItem } from './payment-registry-item';
|
||||
import { RefundRegistryItem } from './refund-registry-item';
|
||||
import { Workbook } from './excel/workbook';
|
||||
import { ExcelService } from './excel/excel.service';
|
||||
import { WorksheetProperties } from './worksheet-properties';
|
||||
|
||||
@Injectable()
|
||||
export class RegistryExportService {
|
||||
|
||||
private capturedPaymentsWorksheet: WorksheetProperties = {
|
||||
name: 'Успешные платежи',
|
||||
headerSizes: {rows: 7, columns: 7}
|
||||
};
|
||||
private refundedPaymentsWorksheet: WorksheetProperties = {
|
||||
name: 'Возвраты',
|
||||
headerSizes: {rows: 7, columns: 6}
|
||||
};
|
||||
private cellBorder = {
|
||||
left: {style: 'thin', color: {auto: 1}},
|
||||
right: {style: 'thin', color: {auto: 1}},
|
||||
top: {style: 'thin', color: {auto: 1}},
|
||||
bottom: {style: 'thin', color: {auto: 1}}
|
||||
};
|
||||
private alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||
|
||||
constructor(
|
||||
private excelService: ExcelService
|
||||
) {
|
||||
}
|
||||
|
||||
public exportRegistryToXLSX(registry: Registry) {
|
||||
const workbook = this.createWorkbookFromRegistry(registry);
|
||||
const fileName = `Реестр операций ${this.getStringifyDateInterval(registry.fromTime, registry.toTime)}`;
|
||||
this.excelService.saveAsXLSX(workbook, fileName);
|
||||
}
|
||||
|
||||
private getStringifyDateInterval(fromTime: Date, toTime: Date): string {
|
||||
return `с ${moment(fromTime).format('DD.MM.YY')} по ${moment(toTime).format('DD.MM.YY')}`;
|
||||
}
|
||||
|
||||
private createWorkbookFromRegistry(registry: Registry): Workbook {
|
||||
const workbook = new Workbook();
|
||||
workbook.SheetNames.push(this.capturedPaymentsWorksheet.name);
|
||||
workbook.SheetNames.push(this.refundedPaymentsWorksheet.name);
|
||||
workbook.Sheets[this.capturedPaymentsWorksheet.name] = this.createCapturedPaymentsWorksheetFromRegistry(registry);
|
||||
workbook.Sheets[this.refundedPaymentsWorksheet.name] = this.createRefundedPaymentsWorksheetFromRegistry(registry);
|
||||
return workbook;
|
||||
}
|
||||
|
||||
private createCapturedPaymentsWorksheetFromRegistry(registry: Registry): object {
|
||||
let worksheet = {};
|
||||
worksheet = merge(worksheet, this.createCapturedPaymentsHeader(registry));
|
||||
worksheet = merge(worksheet, this.createCapturedPaymentsBody(registry.capturedPaymentItems));
|
||||
return worksheet;
|
||||
}
|
||||
|
||||
private createCapturedPaymentsHeader(registry: Registry): object {
|
||||
return this.createHeader({
|
||||
title: 'Успешные платежи за период',
|
||||
header: 'Выполнено переводов в пользу клиента за период:',
|
||||
client: registry.client
|
||||
},
|
||||
registry,
|
||||
[10, 18, 20, 18, 18, 30, 30, 50],
|
||||
[
|
||||
'№ п/п',
|
||||
'Дата платежа',
|
||||
'ID инвойса и платежа',
|
||||
'Принято, руб.',
|
||||
'К зачислению, руб.',
|
||||
'Email плательщика',
|
||||
'Наименование товара',
|
||||
'Описание предоставленных товаров или услуг'
|
||||
]);
|
||||
}
|
||||
|
||||
private createCapturedPaymentsBody(registryItems: PaymentRegistryItem[]): object {
|
||||
const offsetRow = this.capturedPaymentsWorksheet.headerSizes.rows;
|
||||
const arrayOfArrays = map(registryItems, (item: PaymentRegistryItem, index) => ([
|
||||
index + 1,
|
||||
moment(item.paymentDate).format('DD.MM.YY HH:mm:ss'),
|
||||
item.invoiceID,
|
||||
CurrencyService.toMajor(item.amount),
|
||||
CurrencyService.toMajor(item.amount - (item.fee || 0)),
|
||||
item.userEmail,
|
||||
item.product,
|
||||
item.description
|
||||
]));
|
||||
return this.excelService.worksheetFromArrayOfArrays(arrayOfArrays, offsetRow, {border: this.cellBorder});
|
||||
}
|
||||
|
||||
private createRefundedPaymentsWorksheetFromRegistry(registry: Registry): object {
|
||||
let worksheet = {};
|
||||
worksheet = merge(worksheet, this.createRefundedPaymentsHeader(registry));
|
||||
worksheet = merge(worksheet, this.createRefundedPaymentsBody(registry.refundedPaymentItems));
|
||||
return worksheet;
|
||||
}
|
||||
|
||||
private createRefundedPaymentsHeader(registry: Registry): object {
|
||||
return this.createHeader({
|
||||
title: 'Возвраты за период',
|
||||
header: 'Выполнено возвратов за период:',
|
||||
client: registry.client
|
||||
}, registry,
|
||||
[10, 18, 20, 18],
|
||||
['№ п/п', 'Дата возврата', 'ID инвойса и платежа', 'Возвращено, руб.']);
|
||||
}
|
||||
|
||||
private createHeader(titles: { title: string, header: string, client: string }, interval: { fromTime: Date, toTime: Date }, colWidths: number[], tableHeader: string[]) {
|
||||
const tableHeaderLastColNum = tableHeader.length - 1;
|
||||
return {
|
||||
'A1': {
|
||||
v: `${titles.title} с ${this.getStringifyDateInterval(interval.fromTime, interval.toTime)}`,
|
||||
s: {alignment: {horizontal: 'center', vertical: 'center'}, font: {bold: true}}
|
||||
},
|
||||
'A3': {v: 'НКО:', s: {font: {bold: true}}},
|
||||
'B3': {v: 'НКО «ЭПС» (ООО)'},
|
||||
'A4': {v: 'Клиент:', s: {font: {bold: true}}},
|
||||
'B4': {v: titles.client},
|
||||
'A6': {
|
||||
v: titles.header,
|
||||
s: {alignment: {horizontal: 'center', vertical: 'center'}}
|
||||
},
|
||||
...this.createTableHeader(tableHeader, 7),
|
||||
'!ref': this.excelService.getEncodeRange(this.capturedPaymentsWorksheet.headerSizes.rows, this.capturedPaymentsWorksheet.headerSizes.columns),
|
||||
'!cols': colWidths.map(width => ({wch: width})),
|
||||
'!merges': [
|
||||
{s: {r: 0, c: 0}, e: {r: 0, c: tableHeaderLastColNum}},
|
||||
{s: {r: 5, c: 0}, e: {r: 5, c: tableHeaderLastColNum}}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
private createTableHeader(tableHeader: string[], rowNum: number): object {
|
||||
const style = {font: {bold: true}, border: this.cellBorder};
|
||||
const tableHeaderObject: object = {};
|
||||
tableHeader.forEach((item, idx) => tableHeaderObject[this.alphabet[idx] + rowNum] = {v: item, s: style});
|
||||
return tableHeaderObject;
|
||||
}
|
||||
|
||||
private createRefundedPaymentsBody(registryItems: RefundRegistryItem[]): object {
|
||||
const offsetRow = this.capturedPaymentsWorksheet.headerSizes.rows;
|
||||
const arrayOfArrays = map(registryItems, (item: RefundRegistryItem, index) => ([
|
||||
index + 1,
|
||||
moment(item.refundDate).format('DD.MM.YY HH:mm:ss'),
|
||||
item.invoiceID,
|
||||
CurrencyService.toMajor(item.amount)
|
||||
]));
|
||||
return this.excelService.worksheetFromArrayOfArrays(arrayOfArrays, offsetRow, {border: this.cellBorder});
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
.row
|
||||
.col-xs-12.col-sm-6
|
||||
kof-date-range((onSelect)="selectDateRange($event)")
|
||||
.row
|
||||
.col-xs-12
|
||||
button.btn.btn-default([disabled]="isLoading", (click)="exportRegistryToXLSX()")
|
||||
i.fa.fa-cog.fa-spin.fa-1x.fa-fw(*ngIf="isLoading")
|
||||
span Скачать как Excel-файл
|
||||
|
@ -1,46 +0,0 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
|
||||
import { DateRange } from 'koffing/common/date-range/date-range';
|
||||
import { RegistryExportService } from './registry-export.service';
|
||||
import { RegistryDataService } from './registry-data.service';
|
||||
import { ExcelService } from './excel/excel.service';
|
||||
|
||||
@Component({
|
||||
templateUrl: 'registry.component.pug',
|
||||
providers: [
|
||||
RegistryExportService,
|
||||
RegistryDataService,
|
||||
ExcelService
|
||||
]
|
||||
})
|
||||
export class RegistryComponent implements OnInit {
|
||||
|
||||
public shopID: string;
|
||||
public dateRange: DateRange;
|
||||
public isLoading: boolean = false;
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private registryDataService: RegistryDataService,
|
||||
private registryExportService: RegistryExportService
|
||||
) { }
|
||||
|
||||
public ngOnInit() {
|
||||
this.route.parent.parent.params.subscribe((params) => {
|
||||
this.shopID = params['shopID'];
|
||||
});
|
||||
}
|
||||
|
||||
public selectDateRange(dateRange: DateRange) {
|
||||
this.dateRange = dateRange;
|
||||
}
|
||||
|
||||
public exportRegistryToXLSX() {
|
||||
this.isLoading = true;
|
||||
this.registryDataService.getRegistry(this.shopID, this.dateRange.fromTime, this.dateRange.toTime).subscribe((registry) => {
|
||||
this.registryExportService.exportRegistryToXLSX(registry);
|
||||
this.isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
import { PaymentRegistryItem } from './payment-registry-item';
|
||||
import { RefundRegistryItem } from './refund-registry-item';
|
||||
|
||||
export class Registry {
|
||||
public fromTime: Date;
|
||||
public toTime: Date;
|
||||
public client: string;
|
||||
public capturedPaymentItems: PaymentRegistryItem[];
|
||||
public refundedPaymentItems: RefundRegistryItem[];
|
||||
|
||||
constructor(fromTime?: Date, toTime?: Date, client?: string, capturedPaymentItems?: PaymentRegistryItem[], refundedPaymentItems?: RefundRegistryItem[]) {
|
||||
this.fromTime = fromTime;
|
||||
this.toTime = toTime;
|
||||
this.client = client;
|
||||
this.capturedPaymentItems = capturedPaymentItems;
|
||||
this.refundedPaymentItems = refundedPaymentItems;
|
||||
}
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
export class SearchParams {
|
||||
public fromTime: Date;
|
||||
public toTime: Date;
|
||||
public limit: number;
|
||||
public continuationToken?: string;
|
||||
|
||||
constructor(fromTime: Date, toTime: Date, limit: number, continuationToken?: string) {
|
||||
this.fromTime = fromTime;
|
||||
this.toTime = toTime;
|
||||
this.limit = limit;
|
||||
this.continuationToken = continuationToken;
|
||||
}
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
export class WorksheetProperties {
|
||||
public name: string;
|
||||
public headerSizes: {rows: number, columns: number};
|
||||
}
|
4
src/app/documents/reports/reports-filter.ts
Normal file
4
src/app/documents/reports/reports-filter.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export interface ReportsFilter {
|
||||
path: string;
|
||||
value: any;
|
||||
}
|
@ -3,4 +3,4 @@
|
||||
kof-date-range((onSelect)="selectDateRange($event)")
|
||||
.row
|
||||
.col-xs-12
|
||||
kof-search-reports-result([reports$]="reports$")
|
||||
kof-search-reports-result([reports$]="reports$", [filter]="filter")
|
||||
|
@ -1,33 +1,49 @@
|
||||
import { Component, OnInit, ViewEncapsulation } from '@angular/core';
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { Subject } from 'rxjs/Subject';
|
||||
import { Subject } from 'rxjs';
|
||||
|
||||
import { Report, SearchReportParams } from 'koffing/backend';
|
||||
import { SearchService } from 'koffing/backend/search.service';
|
||||
import { Report, ReportType, SearchReportParams } from 'koffing/backend';
|
||||
import { DateRange } from 'koffing/common/date-range/date-range';
|
||||
import { SearchService } from 'koffing/backend/search.service';
|
||||
import { ReportsFilter } from './reports-filter';
|
||||
|
||||
@Component({
|
||||
selector: 'kof-provision-of-service',
|
||||
templateUrl: 'reports.component.pug',
|
||||
styleUrls: ['reports.component.less'],
|
||||
encapsulation: ViewEncapsulation.None
|
||||
styleUrls: ['./reports.component.less']
|
||||
})
|
||||
export class ReportsComponent implements OnInit {
|
||||
|
||||
public reports$: Subject<Report[]> = new Subject();
|
||||
public filter: ReportsFilter;
|
||||
|
||||
private shopID: string;
|
||||
private dateRange: Subject<DateRange> = new Subject();
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private searchService: SearchService
|
||||
) { }
|
||||
) {
|
||||
}
|
||||
|
||||
public ngOnInit() {
|
||||
this.route.params.subscribe((params) => {
|
||||
this.filter = {
|
||||
path: 'report.type',
|
||||
value: params['type']
|
||||
};
|
||||
});
|
||||
this.route.parent.parent.params.subscribe((params) => {
|
||||
this.shopID = params['shopID'];
|
||||
});
|
||||
this.dateRange.subscribe((dateRange) => this.getReports(dateRange));
|
||||
}
|
||||
|
||||
public selectDateRange(dateRange: DateRange) {
|
||||
this.dateRange.next(dateRange);
|
||||
}
|
||||
|
||||
private getReports(dateRange: DateRange) {
|
||||
const params = new SearchReportParams(dateRange.fromTime, dateRange.toTime);
|
||||
this.searchService.getReports(this.shopID, params).subscribe((reports) => {
|
||||
this.reports$.next(reports);
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
|
||||
import { DownloadService } from 'koffing/backend/download.service';
|
||||
import { FileMeta } from 'koffing/backend';
|
||||
import { DownloadService } from 'src/app/backend/download.service';
|
||||
import { FileMeta } from 'src/app/backend/index';
|
||||
|
||||
@Component({
|
||||
selector: 'kof-report-files',
|
@ -1,4 +1,4 @@
|
||||
import { Report } from 'koffing/backend';
|
||||
import { Report } from 'src/app/backend/index';
|
||||
|
||||
export class ReportTableItem {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
|
||||
import { REPORT_TYPE } from 'koffing/backend';
|
||||
import { REPORT_TYPE } from 'src/app/backend/index';
|
||||
|
||||
@Pipe({
|
||||
name: 'kofReportType'
|
@ -4,14 +4,12 @@ table.table.table-striped
|
||||
th Идентификатор
|
||||
th.hidden-xs Временной интервал
|
||||
th Время создания
|
||||
th.hidden-xs Тип отчета
|
||||
th
|
||||
tbody(*ngFor="let item of reportItems")
|
||||
tbody(*ngFor="let item of filtered(reportItems)")
|
||||
tr
|
||||
td {{item.report.id}}
|
||||
td.hidden-xs {{item.report.fromTime | date: "dd.MM.yyyy"}} - {{item.report.toTime | date: "dd.MM.yyyy"}}
|
||||
td {{item.report.createdAt | date: "dd.MM.yyyy HH:mm:ss"}}
|
||||
td.hidden-xs {{item.report.type | kofReportType}}
|
||||
td
|
||||
.pull-right
|
||||
button.btn.btn-xs.btn-default((click)="toggleFilesPanel(item)")
|
@ -1,8 +1,10 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { get } from 'lodash';
|
||||
|
||||
import { Report } from 'koffing/backend';
|
||||
import { Report } from 'src/app/backend';
|
||||
import { ReportTableItem } from './report-item';
|
||||
import { ReportsFilter } from '../reports-filter';
|
||||
|
||||
@Component({
|
||||
selector: 'kof-search-reports-result',
|
||||
@ -13,6 +15,9 @@ export class SearchReportsResultComponent implements OnInit {
|
||||
@Input()
|
||||
public reports$: Observable<Report[]>;
|
||||
|
||||
@Input()
|
||||
public filter: ReportsFilter;
|
||||
|
||||
public reportItems: ReportTableItem[];
|
||||
|
||||
public ngOnInit() {
|
||||
@ -24,4 +29,10 @@ export class SearchReportsResultComponent implements OnInit {
|
||||
public toggleFilesPanel(item: ReportTableItem) {
|
||||
item.isVisible = !item.isVisible;
|
||||
}
|
||||
|
||||
public filtered(reports: ReportTableItem[]): ReportTableItem[] {
|
||||
return this.filter && reports
|
||||
? reports.filter((report) => get(report, this.filter.path) === this.filter.value)
|
||||
: reports;
|
||||
}
|
||||
}
|
@ -17,9 +17,9 @@ import { InvoiceComponent } from 'koffing/invoice/invoice.component';
|
||||
import { ShopInfoComponent } from 'koffing/shop-info/shop-info.component';
|
||||
import { ContractManageComponent } from 'koffing/shop-info/contract-manage/contract-manage.component';
|
||||
import { DocumentsComponent } from 'koffing/documents/documents.component';
|
||||
import { ReportsComponent } from 'koffing/documents/reports/reports.component';
|
||||
import { RegistryComponent } from 'koffing/documents/registry/registry.component';
|
||||
import { InitCreateShopComponent } from 'koffing/management/init-create-shop/init-create-shop.component';
|
||||
import { ReportsComponent } from 'koffing/documents/reports/reports.component';
|
||||
import { ReportType } from 'koffing/backend';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@ -89,16 +89,12 @@ import { InitCreateShopComponent } from 'koffing/management/init-create-shop/ini
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
redirectTo: 'reports',
|
||||
redirectTo: 'reports/' + ReportType.provisionOfService,
|
||||
pathMatch: 'full'
|
||||
},
|
||||
{
|
||||
path: 'reports',
|
||||
path: 'reports/:type',
|
||||
component: ReportsComponent
|
||||
},
|
||||
{
|
||||
path: 'registry',
|
||||
component: RegistryComponent
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -122,4 +118,5 @@ import { InitCreateShopComponent } from 'koffing/management/init-create-shop/ini
|
||||
RouterModule
|
||||
]
|
||||
})
|
||||
export class RootRoutingModule { }
|
||||
export class RootRoutingModule {
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user