324 lines
10 KiB
TypeScript
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';
|
|
}
|
|
}
|