Skip to content

Commit 444a53d

Browse files
authored
Merge pull request #3040 from DFXswiss/develop
Release: develop -> main
2 parents ddbb1c7 + c2d2b2a commit 444a53d

5 files changed

Lines changed: 51 additions & 20 deletions

File tree

src/subdomains/core/buy-crypto/routes/buy/buy.controller.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ export class BuyController {
161161
@ApiOkResponse({ type: PdfDto })
162162
async generateInvoicePDF(@GetJwt() jwt: JwtPayload, @Param('id') id: string): Promise<PdfDto> {
163163
const request = await this.transactionRequestService.getOrThrow(+id, jwt.user);
164-
if (!request.userData.isDataComplete) throw new BadRequestException('User data is not complete');
164+
if (!request.userData.isInvoiceDataComplete) throw new BadRequestException('User data is not complete');
165165
if (!request.isValid) throw new BadRequestException('Transaction request is not valid');
166166
if (request.isComplete) throw new ConflictException('Transaction request is already confirmed');
167167

src/subdomains/core/history/controllers/transaction.controller.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -447,7 +447,7 @@ export class TransactionController {
447447
if (request) {
448448
// Validate ownership and state
449449
if (request.user.userData.id !== jwt.account) throw new ForbiddenException('Not your transaction request');
450-
if (!request.userData.isDataComplete) throw new BadRequestException('User data is not complete');
450+
if (!request.userData.isInvoiceDataComplete) throw new BadRequestException('User data is not complete');
451451
if (!request.isValid) throw new BadRequestException('Transaction request is not valid');
452452

453453
// Generate invoice from request (pending transaction)

src/subdomains/generic/user/models/user-data/user-data.entity.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -748,6 +748,16 @@ export class UserData extends IEntity {
748748
return this.requiredKycFields.every((f) => this[f]);
749749
}
750750

751+
get requiredInvoiceFields(): string[] {
752+
return ['accountType'].concat(
753+
!this.accountType || this.accountType === AccountType.PERSONAL ? ['firstname', 'surname'] : ['organizationName'],
754+
);
755+
}
756+
757+
get isInvoiceDataComplete(): boolean {
758+
return this.requiredInvoiceFields.every((f) => this[f]);
759+
}
760+
751761
get hasBankTxVerification(): boolean {
752762
return [CheckStatus.PASS, CheckStatus.UNNECESSARY, CheckStatus.GSHEET].includes(this.bankTransactionVerification);
753763
}

src/subdomains/supporting/payment/services/swiss-qr.service.ts

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,6 @@ export class SwissQRService {
6565
}
6666

6767
const data = this.generateQrData(amount, currency, bankInfo, reference, request.userData);
68-
if (!data.debtor) throw new Error('Debtor is required');
6968

7069
const userLanguage = request.userData.language.symbol.toUpperCase();
7170
const language = this.isSupportedInvoiceLanguage(userLanguage) ? userLanguage : 'EN';
@@ -82,15 +81,22 @@ export class SwissQRService {
8281
date: request.created,
8382
};
8483

85-
return this.generatePdfInvoice(tableData, language, data, true, TransactionType.BUY);
84+
return this.generatePdfInvoice(
85+
tableData,
86+
language,
87+
data,
88+
true,
89+
TransactionType.BUY,
90+
PdfBrand.DFX,
91+
request.userData.completeName,
92+
);
8693
}
8794

8895
async createTxStatement(
8996
{ statementType, transactionType, transaction, currency, bankInfo, reference, request }: TxStatementDetails,
9097
brand: PdfBrand = PdfBrand.DFX,
9198
): Promise<string> {
9299
const debtor = this.getDebtor(transaction.userData);
93-
if (!debtor) throw new Error('Debtor is required');
94100

95101
currency = Config.invoice.currencies.includes(currency) ? currency : Config.invoice.defaultCurrency;
96102
if (!this.isSupportedInvoiceCurrency(currency)) {
@@ -111,7 +117,15 @@ export class SwissQRService {
111117
message: reference,
112118
};
113119

114-
return this.generatePdfInvoice(tableData, language, billData, !!bankInfo, transactionType, brand);
120+
return this.generatePdfInvoice(
121+
tableData,
122+
language,
123+
billData,
124+
!!bankInfo,
125+
transactionType,
126+
brand,
127+
transaction.userData.completeName,
128+
);
115129
}
116130

117131
private generatePdfInvoice(
@@ -121,6 +135,7 @@ export class SwissQRService {
121135
includeQrBill: boolean,
122136
transactionType: TransactionType,
123137
brand: PdfBrand = PdfBrand.DFX,
138+
debtorName?: string,
124139
): Promise<string> {
125140
return new Promise((resolve, reject) => {
126141
try {
@@ -156,18 +171,20 @@ export class SwissQRService {
156171
);
157172

158173
// Debtor address
159-
pdf.fontSize(12);
160-
pdf.font('Helvetica');
161-
pdf.text(
162-
`${billData.debtor.name}\n${billData.debtor.address} ${billData.debtor.buildingNumber}\n${billData.debtor.zip} ${billData.debtor.city}`,
163-
mm2pt(130),
164-
mm2pt(60),
165-
{
174+
const displayName = billData.debtor?.name ?? debtorName;
175+
if (displayName) {
176+
pdf.fontSize(12);
177+
pdf.font('Helvetica');
178+
const addressLine = billData.debtor
179+
? [billData.debtor.address, billData.debtor.buildingNumber].filter(Boolean).join(' ')
180+
: '';
181+
const cityLine = billData.debtor ? [billData.debtor.zip, billData.debtor.city].filter(Boolean).join(' ') : '';
182+
pdf.text([displayName, addressLine, cityLine].filter(Boolean).join('\n'), mm2pt(130), mm2pt(60), {
166183
align: 'left',
167184
height: mm2pt(50),
168185
width: mm2pt(70),
169-
},
170-
);
186+
});
187+
}
171188

172189
// Title
173190
pdf.fontSize(14);
@@ -440,17 +457,21 @@ export class SwissQRService {
440457
}
441458

442459
private getDebtor(userData?: UserData): Debtor | undefined {
443-
if (!userData?.isDataComplete) return undefined;
460+
if (!userData?.isInvoiceDataComplete) return undefined;
444461

445462
const name = userData.completeName;
446463
const address = userData.address;
447464

465+
// SwissQRBill requires country to be exactly 2 characters
466+
// If no valid address, return undefined (debtor is optional in QR bill)
467+
if (!address?.country?.symbol) return undefined;
468+
448469
const debtor: Debtor = {
449470
name,
450-
address: address.street,
451-
city: address.city,
471+
address: address.street ?? '',
472+
city: address.city ?? '',
452473
country: address.country.symbol,
453-
zip: address.zip,
474+
zip: address.zip ?? '',
454475
};
455476
if (address.houseNumber != null) debtor.buildingNumber = address.houseNumber;
456477

src/subdomains/supporting/payment/services/transaction-helper.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -511,7 +511,7 @@ export class TransactionHelper implements OnModuleInit {
511511
: await this.transactionService.getTransactionByUid(txIdOrUid, relations);
512512

513513
if (!transaction) throw new BadRequestException('Transaction not found');
514-
if (!transaction.userData.isDataComplete) throw new BadRequestException('User data is not complete');
514+
if (!transaction.userData.isInvoiceDataComplete) throw new BadRequestException('User data is not complete');
515515
if (transaction.userData.id !== userDataId) throw new ForbiddenException('Not your transaction');
516516

517517
// Handle pending transactions (no targetEntity yet, but has request)

0 commit comments

Comments
 (0)