agmission/Development/client/src/app/invoices/customer-settings/customer-settings.component.ts

324 lines
10 KiB
TypeScript

import { AfterViewInit, Component, ElementRef, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { BaseComp } from '@app/shared/base/base.component';
import { ActivatedRoute } from '@angular/router';
import { createNewCustomerSetting, CustomerInvoiceSetting } from '@app/invoices/models/customer-invoice-setting.model';
import { allowedLogoFormats, globals, maxLogoSize, allowedLogoFileExt, RoleIds } from '@app/shared/global';
import { select } from '@ngrx/store';
import * as fromClients from '@app/client/reducers';
import * as SettingActions from '@app/invoices/actions/setting.actions';
import { SelectItem } from 'primeng/api';
import { InvoiceService } from '@app/domain/services/invoice.service';
import { InputNumber } from 'primeng/inputnumber';
import { GAService } from '@app/shared/ga.service';
@Component({
selector: 'agm-invoices-customer-settings',
templateUrl: './customer-settings.component.html',
styleUrls: ['./customer-settings.component.css']
})
export class CustomerSettingsComponent extends BaseComp implements OnInit, AfterViewInit, OnDestroy {
readonly globals = globals;
previewLogo;
errorLogoFormat = false;
errorLogoSize = false;
maxLogoSize = maxLogoSize;
allowedLogoFileExt = allowedLogoFileExt.join(', ');
currClient;
routeId;
canClear = false;
private _isNew: boolean;
get isNew() {
return this._isNew;
}
set isNew(isNew) {
this._isNew = isNew;
}
private _setting: CustomerInvoiceSetting;
get setting(): CustomerInvoiceSetting {
return this._setting;
}
set setting(setting: CustomerInvoiceSetting) {
this._setting = setting;
}
paymentTermOpts;
private _paymentTerm;
get paymentTerm() {
return this._paymentTerm;
}
set paymentTerm(paymentTerm) {
this._paymentTerm = paymentTerm;
}
addingNewTerm = false;
customTerm;
@ViewChild('name') name: ElementRef;
@ViewChild('term') termEl: InputNumber;
@ViewChild('taxValue') taxValue: InputNumber;
@ViewChild('discount') discount: InputNumber;
formData: FormData = new FormData();
get requiredFieldTrimmed() {
return this.invoiceSvc.requiredSettingFieldTrimmed(this.setting);
}
constructor(
private readonly route: ActivatedRoute,
private readonly invoiceSvc: InvoiceService
) {
super();
}
ngOnInit(): void {
this.sub$ = this.route.data.subscribe((data) => {
const setting = data[0] as CustomerInvoiceSetting || null;
if (setting) {
this.isNew = (setting._id === '0');
this.canClear = !(setting._id === '0');
if (setting._id === '0') {
setting.companyName = this.invoiceSvc.defaultSetting.companyName;
setting.address = this.invoiceSvc.defaultSetting.address;
setting.taxValue = this.invoiceSvc.defaultSetting.taxValue;
setting.discount = this.invoiceSvc.defaultSetting.discount;
setting.paymentTerm = this.invoiceSvc.defaultSetting.paymentTerm;
setting.note = this.invoiceSvc.defaultSetting.note;
setting.termOpts = this.invoiceSvc.defaultSetting.termOpts;
}
this.setting = {
...setting,
};
this.paymentTermOpts = this.setting.termOpts.map(t => <SelectItem>{
label: $localize`:@@numOfDays:#day# days`.replace('#day#', t),
value: t
});
this.previewLogo = setting.logo;
const paymentTerm = this.paymentTermOpts.find(i => i.value == setting.paymentTerm);
if (paymentTerm) {
this.paymentTerm = paymentTerm.value;
} else {
this.paymentTermOpts
.push(<SelectItem>{
label: $localize`:@@numOfDays:#day# days`.replace('#day#', `${setting.paymentTerm}`),
value: setting.paymentTerm
});
this.paymentTermOpts = this.paymentTermOpts.sort((a, b) => a.value - b.value);
this.paymentTerm = setting.paymentTerm;
}
}
});
this.sub$.add(this.store.pipe(select(fromClients.getAllClients)).subscribe(clients => {
if (clients) {
this.route.params.subscribe(params => {
this.routeId = params['id'];
});
this.currClient = clients.find(client => client._id == this.routeId);
}
}));
}
ngAfterViewInit() {
this.focusName();
}
focusName() {
const timer = setInterval(() => {
if (this.setting) {
if (this.name.nativeElement) {
this.name.nativeElement.focus();
clearInterval(timer);
}
} else {
clearInterval(timer);
}
}, 500);
setTimeout(() => {
clearInterval(timer);
}, 1500);
}
onInputTerm(evt) {
const memo = this.customTerm;
if (evt.value > 9999) {
this.customTerm = memo;
evt.originalEvent.stopPropagation();
} else {
this.customTerm = evt.value;
}
}
addNewTerm() {
this.addingNewTerm = true;
const timer = setInterval(() => {
if (this.termEl.input.nativeElement) {
this.termEl.input.nativeElement.focus();
clearInterval(timer);
} else {
clearInterval(timer);
}
}, 300);
setTimeout(() => {
clearInterval(timer);
}, 1500);
}
cancelNewTerm() {
this.customTerm = null;
this.addingNewTerm = false;
}
saveNewTerm() {
const newTerm = this.customTerm;
if (newTerm > 1 && !this.paymentTermOpts.map(i => i.value).includes(newTerm)) {
this.paymentTermOpts.push(<SelectItem>{ label: $localize`:@@numOfDayss:#day# days`.replace('#day#', String(newTerm)), value: newTerm });
this.paymentTermOpts = this.paymentTermOpts.sort((a, b) => a.value - b.value);
if (!this.isNew) {
this.invoiceSvc.setTermOptions(this.setting._id, { termOpts: [...this.paymentTermOpts.map(o => o.value)] })
.subscribe(() => { }, () => {
this.msgSvc.addFailedMsg(globals.doThingsFailed.replace('#do#', globals.create).replace('#thing#', $localize`:@@Terms:Terms`));
});
}
this.paymentTerm = newTerm;
}
this.customTerm = null;
this.addingNewTerm = false;
}
removeTermOpt(value) {
this.paymentTermOpts = this.paymentTermOpts.filter(i => i.value != value);
this.paymentTerm = this.paymentTermOpts[0].value;
this.invoiceSvc.setTermOptions(this.setting._id, { termOpts: [...this.paymentTermOpts.map(o => o.value)] })
.subscribe(() => { }, () => {
this.msgSvc.addFailedMsg(globals.doThingsFailed.replace('#do#', globals.delete).replace('#thing#', $localize`:@@Terms:Terms`));
});
}
onFileSelected(event: any) {
const file: File = event.target.files[0];
if (!file) {
return;
}
if (!allowedLogoFormats.includes(file.type)) {
this.errorLogoFormat = true;
return;
}
if ((file.size / (1024 * 1024)) > this.maxLogoSize) {
this.errorLogoSize = true;
return;
}
this.setting.logo = file;
const reader = new FileReader();
reader.onload = (e: any) => {
this.previewLogo = e.target.result;
this.errorLogoFormat = false;
this.errorLogoSize = false;
};
reader.readAsDataURL(file);
}
saveSetting() {
const payload = {
...this.setting,
companyName: this.setting?.companyName?.trim(),
address: this.setting?.address?.trim(),
};
payload.paymentTerm = this.paymentTerm;
// Track settings changes
const settingsModified = this.getModifiedSettings(payload);
this.gaSvc.trackCustomerInvoiceSettingsUpdated({
client_id: this.currClient?._id || 'unknown',
settings_modified: settingsModified,
automation_enabled: this.hasAutomationEnabled(payload),
payment_terms_changed: this.hasPaymentTermsChanged(payload),
billing_preferences_updated: this.hasBillingPreferencesUpdated(payload),
user_id: this.authSvc.user?._id,
user_role: this.getUserRole(),
platform: 'web'
});
if (this.isNew) {
this.store.dispatch(new SettingActions.Create(payload));
} else {
this.store.dispatch(new SettingActions.Update(payload));
}
this.goBack();
}
goBack() {
this.router.navigate(['../'], { relativeTo: this.route });
}
delete() {
this.confirmSvc.confirm({
message: globals.confirmDeleteThing.replace('#thing#', globals.invoiceSetting),
accept: () => {
this.store.dispatch(new SettingActions.Delete(this.setting));
this.setting = createNewCustomerSetting(this.currClient);
this.previewLogo = '';
}
});
}
ngOnDestroy(): void {
super.ngOnDestroy();
}
private getModifiedSettings(payload: CustomerInvoiceSetting): string[] {
const modified: string[] = [];
// Compare with original setting or default values
const original = this.isNew ? this.invoiceSvc.defaultSetting : this.setting;
if (payload.companyName !== original.companyName) modified.push('company_name');
if (payload.address !== original.address) modified.push('address');
if (payload.taxValue !== original.taxValue) modified.push('tax_value');
if (payload.discount !== original.discount) modified.push('discount');
if (payload.paymentTerm !== original.paymentTerm) modified.push('payment_term');
if (payload.note !== original.note) modified.push('note');
if (payload.logo !== original.logo) modified.push('logo');
return modified;
}
private hasAutomationEnabled(payload: CustomerInvoiceSetting): boolean {
// Check if any automation features are enabled
return payload.taxValue > 0 || payload.discount > 0 || payload.paymentTerm > 0;
}
private hasPaymentTermsChanged(payload: CustomerInvoiceSetting): boolean {
const original = this.isNew ? this.invoiceSvc.defaultSetting : this.setting;
return payload.paymentTerm !== original.paymentTerm;
}
private hasBillingPreferencesUpdated(payload: CustomerInvoiceSetting): boolean {
const original = this.isNew ? this.invoiceSvc.defaultSetting : this.setting;
return payload.companyName !== original.companyName ||
payload.address !== original.address ||
payload.logo !== original.logo;
}
private getUserRole(): 'admin' | 'applicator' | 'office_admin' | 'client' | 'officer' | 'pilot' | 'inspector' | 'aircraft' {
const roles = this.authSvc.user?.roles || [];
if (roles.includes(RoleIds.ADMIN)) return 'admin';
if (roles.includes(RoleIds.APP)) return 'applicator';
if (roles.includes(RoleIds.APP_ADM)) return 'office_admin';
if (roles.includes(RoleIds.PILOT)) return 'pilot';
if (roles.includes(RoleIds.OFFICER)) return 'officer';
if (roles.includes(RoleIds.INSPECTOR)) return 'inspector';
if (roles.includes(RoleIds.DEVICE)) return 'aircraft';
return 'client';
}
}