feat(freight): add company logo setting applied to every generated document

New logo-settings module (mirrors stamp-settings): single uploaded logo,
stored via FilesService/MinIO, injected as a data URL into invoice/receipt,
contract, warehouse, train-scheduling, and payment-receipt PDFs. Adds a
matching backoffice settings page and settings:logo:view/manage permissions.

>
This commit is contained in:
Hagernesh
2026-08-13 10:53:42 +00:00
parent 63b40ea4f8
commit c4bf3c9479
31 changed files with 800 additions and 8 deletions

View File

@@ -10,6 +10,7 @@ import { ContractPricingScheduleBuilder, PricingSchedule } from './contract-pric
import { ContractRateScheduleBuilder, RateSchedule } from './contract-rate-schedule.builder';
import { ContractTemplateResolver } from './contract-template.resolver';
import { StampSettingsService } from '../modules/stamp-settings/stamp-settings.service';
import { LogoSettingsService } from '../modules/logo-settings/logo-settings.service';
import { ContractTemplateMeta, getTemplateMeta } from './contract-template.registry';
export interface ContractSignatureView {
@@ -111,6 +112,8 @@ export interface ContractViewModel {
hasCustomerSignature: boolean;
hasStaffSignature: boolean;
dynamicTemplate?: ContractDynamicTemplateView;
/** Company logo for the cover-page header (LogoSettingsService); null renders the "EDR" mark. */
logoImageUrl?: string | null;
}
@Injectable()
@@ -121,6 +124,7 @@ export class ContractViewModelBuilder {
private readonly pricingBuilder: ContractPricingScheduleBuilder,
private readonly rateScheduleBuilder: ContractRateScheduleBuilder,
private readonly stampSettings: StampSettingsService,
private readonly logoSettings: LogoSettingsService,
) {}
async build(bookingId: string): Promise<{ booking: Booking; view: ContractViewModel }> {
@@ -138,6 +142,7 @@ export class ContractViewModelBuilder {
template.freight,
);
const signatures = await this.loadSignatures(bookingId);
const logoImageUrl = await this.logoSettings.getLogoImageUrl();
const hasCustomer = signatures.some((s) => s.role === 'CUSTOMER');
const hasStaff = signatures.some((s) => s.role === 'STAFF');
@@ -194,6 +199,7 @@ export class ContractViewModelBuilder {
hasContractDocument: hasContractFile,
hasCustomerSignature: hasCustomer,
hasStaffSignature: hasStaff,
logoImageUrl,
};
return { booking, view };