From 896e401de8259486c438b0037425f9fcc4fc9f86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phan=20Sainl=C3=A9ger?= Date: Fri, 30 Jan 2026 17:01:28 +0100 Subject: [PATCH 1/3] new: dev: add payment request management Odoo API, paymentRequest object --- src/backend/odoo/account.ts | 2 - src/backend/odoo/paymentRequest.ts | 105 +++++++++++++++++++++++++++++ src/backend/odoo/userAccount.ts | 26 +++++++ src/type.ts | 15 +++++ 4 files changed, 146 insertions(+), 2 deletions(-) create mode 100644 src/backend/odoo/paymentRequest.ts diff --git a/src/backend/odoo/account.ts b/src/backend/odoo/account.ts index 7b156b8..ac9ad22 100644 --- a/src/backend/odoo/account.ts +++ b/src/backend/odoo/account.ts @@ -1,5 +1,3 @@ -import * as t from '../../type' - import { BridgeObject } from '..' diff --git a/src/backend/odoo/paymentRequest.ts b/src/backend/odoo/paymentRequest.ts new file mode 100644 index 0000000..dcd1567 --- /dev/null +++ b/src/backend/odoo/paymentRequest.ts @@ -0,0 +1,105 @@ +import { BridgeObject } from ".." + + +export default class PaymentRequest extends BridgeObject { + + get isPaymentRequest() { + return true + } + + get id() { + return this.jsonData.id + } + + get amount() { + // Format amount with 2 decimal places (e.g., 40 -> "40.00") + return Number(this.jsonData.amount).toFixed(2) + } + + get message() { + return this.jsonData.message + } + + get state() { + return this.jsonData.state + } + + get creatorWalletUri() { + return this.jsonData.creator_wallet_uri + } + + get creatorName() { + return this.jsonData.creator_name + } + + get date() { + return new Date(this.jsonData.create_date * 1000) + } + + get isCreator() { + return this.creatorWalletUri === this.parent.internalId + } + + get senderWalletUri() { + return this.jsonData.sender_wallet_uri + } + + get receiverWalletUri() { + return this.jsonData.receiver_wallet_uri + } + + get isSender() { + return this.senderWalletUri === this.parent.internalId + } + + get isReceiver() { + return this.receiverWalletUri === this.parent.internalId + } + + get related() { + // Si je suis le sender, afficher le receiver, sinon afficher le sender + if (this.isSender) { + return this.jsonData.receiver_name || this.receiverWalletUri + } + return this.jsonData.sender_name || this.senderWalletUri + } + + get description() { + return this.message + } + + public async cancel(reason?: string) { + return this._updateStatus("cancelled", reason) + } + + public async refuse(reason: string) { + return this._updateStatus("refused", reason) + } + + public async markAsPaid(txId: string) { + return this._updateStatus("paid", txId) + } + + private async _updateStatus(status: string, message?: string) { + const currencyId = this.parent.getCurrencyId() + const backendType = this.parent.internalId.split(':')[0] + const currency_uri = `${backendType}:${currencyId}` + + const res = await this.backends.odoo.$post( + "/payment_request/update-payment-request", + { + wallet_uri: this.parent.internalId, + currency_uri: currency_uri, + payment_request_id: this.id, + status, + message, + } + ) + if (!res) { + throw new Error( + `Failed to update payment request status to ${status}` + ) + } + return + } +} diff --git a/src/backend/odoo/userAccount.ts b/src/backend/odoo/userAccount.ts index 6621032..6560af0 100644 --- a/src/backend/odoo/userAccount.ts +++ b/src/backend/odoo/userAccount.ts @@ -1,13 +1,39 @@ import { BridgeObject } from '..' import { t } from '../..' +import PaymentRequest from './paymentRequest' export default abstract class UserAccount extends BridgeObject { abstract internalId: string + abstract getAccounts(): Promise + abstract getCurrencyId(): string get isTopUpAllowed() { return this.jsonData?.is_topup_allowed !== false } + /** + * Get payment requests for this user account (wallet) + * + * @param state - Array of states to filter (e.g. ["open"], ["paid", "refused"]) + * + * @returns Array of PaymentRequest objects + */ + public async getPaymentRequests(state: string[]): Promise { + const currencyId = this.getCurrencyId() + const backendType = this.internalId.split(':')[0] + const currency_uri = `${backendType}:${currencyId}` + + const requests = await this.backends.odoo.$get( + '/payment_request/list-payment-requests', + { + wallet_uri: this.internalId, + currency_uri: currency_uri, + state: state, + } + ) + return requests.map((e: any) => new PaymentRequest(this.backends, this, e)) + } + } diff --git a/src/type.ts b/src/type.ts index e2bcc90..c1afc83 100644 --- a/src/type.ts +++ b/src/type.ts @@ -68,6 +68,21 @@ export interface ITransaction extends IBridge { export interface ICreditRequest extends ITransaction {} +export interface IPaymentRequest extends IBridge { + id: number + amount: string + message: string + state: string + creatorWalletUri: string + senderWalletUri: string + receiverWalletUri: string + + cancel(): Promise + refuse(reason: string): Promise + markAsPaid(txId: string): Promise +} + + export interface IContact extends IBridge { /** From b8296c0ecffe06429a95358520e5581fa78ba8b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phan=20Sainl=C3=A9ger?= Date: Fri, 30 Jan 2026 17:49:52 +0100 Subject: [PATCH 2/3] new: dev: add payment request creation Odoo api call --- src/backend/odoo/userAccount.ts | 38 +++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/src/backend/odoo/userAccount.ts b/src/backend/odoo/userAccount.ts index 6560af0..21b7870 100644 --- a/src/backend/odoo/userAccount.ts +++ b/src/backend/odoo/userAccount.ts @@ -13,13 +13,6 @@ export default abstract class UserAccount extends BridgeObject { return this.jsonData?.is_topup_allowed !== false } - /** - * Get payment requests for this user account (wallet) - * - * @param state - Array of states to filter (e.g. ["open"], ["paid", "refused"]) - * - * @returns Array of PaymentRequest objects - */ public async getPaymentRequests(state: string[]): Promise { const currencyId = this.getCurrencyId() const backendType = this.internalId.split(':')[0] @@ -36,4 +29,35 @@ export default abstract class UserAccount extends BridgeObject { return requests.map((e: any) => new PaymentRequest(this.backends, this, e)) } + public async createPaymentRequest( + requests: Array<{ + sender_wallet_uri: string + receiver_wallet_uri: string + amount: number + message?: string + }> + ): Promise { + const currencyId = this.getCurrencyId() + const backendType = this.internalId.split(':')[0] + const currency_uri = `${backendType}:${currencyId}` + + const res = await this.backends.odoo.$post( + '/payment_request/create-payment-request', + { + currency_uri: currency_uri, + creator_wallet_uri: this.internalId, + requests: requests.map(req => ({ + sender_wallet_uri: req.sender_wallet_uri, + receiver_wallet_uri: req.receiver_wallet_uri, + amount: req.amount, + message: req.message || null, + })), + } + ) + if (!res || !Array.isArray(res)) { + throw new Error('Failed to create payment request') + } + return res + } + } From 6dafe23f2f52b6397791b047147c42aad331b3e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phan=20Sainl=C3=A9ger?= Date: Wed, 11 Feb 2026 22:16:51 +0100 Subject: [PATCH 3/3] new: dev: add payment request recurrent contract management --- src/backend/odoo/recurrentContract.ts | 113 ++++++++++++++++++++++++++ src/backend/odoo/userAccount.ts | 56 +++++++++++++ src/type.ts | 21 +++++ 3 files changed, 190 insertions(+) create mode 100644 src/backend/odoo/recurrentContract.ts diff --git a/src/backend/odoo/recurrentContract.ts b/src/backend/odoo/recurrentContract.ts new file mode 100644 index 0000000..3613438 --- /dev/null +++ b/src/backend/odoo/recurrentContract.ts @@ -0,0 +1,113 @@ +import { BridgeObject } from ".." +import { t } from "../.." + + +export default class RecurrentContract extends BridgeObject { + + get isRecurrentContract() { + return true + } + + get id() { + return this.jsonData.id + } + + get amount(): string { + return Number(this.jsonData.amount).toFixed(2) + } + + get message(): string { + return this.jsonData.message + } + + get state(): string { + return this.jsonData.state + } + + get creatorWalletUri(): string { + return this.jsonData.creator_wallet_uri + } + + get creatorName(): string { + return this.jsonData.creator_name + } + + get date(): Date { + return new Date(this.jsonData.create_date * 1000) + } + + get isCreator(): boolean { + return this.creatorWalletUri === this.parent.internalId + } + + get senderWalletUri(): string { + return this.jsonData.sender_wallet_uri + } + + get senderName(): string { + return this.jsonData.sender_name + } + + get receiverWalletUri(): string { + return this.jsonData.receiver_wallet_uri + } + + get receiverName(): string { + return this.jsonData.receiver_name + } + + get isSender(): boolean { + return this.senderWalletUri === this.parent.internalId + } + + get isReceiver(): boolean { + return this.receiverWalletUri === this.parent.internalId + } + + get related(): string { + if (this.isSender) { + return this.receiverName || this.receiverWalletUri + } + return this.senderName || this.senderWalletUri + } + + get description(): string { + return this.message + } + + get dateStart(): string | null { + return this.jsonData.date_start + } + + get dateEnd(): string | null { + return this.jsonData.date_end + } + + get recurringRuleType(): t.RecurringRuleType { + return this.jsonData.recurring_rule_type + } + + get recurringInterval(): number { + return this.jsonData.recurring_interval + } + + get nextExecutionDate(): string | null { + return this.jsonData.recurring_next_date + } + + public async delete(): Promise { + const currencyId = this.parent.getCurrencyId() + const backendType = this.parent.internalId.split(':')[0] + const currency_uri = `${backendType}:${currencyId}` + + const res = await this.backends.odoo.$delete( + "/payment_request_recurrent_contract/delete-payment-request-recurrent-contracts", + { + wallet_uri: this.parent.internalId, + currency_uri: currency_uri, + payment_request_ids: [this.id], + } + ) + return res === true + } +} diff --git a/src/backend/odoo/userAccount.ts b/src/backend/odoo/userAccount.ts index 21b7870..464aeb4 100644 --- a/src/backend/odoo/userAccount.ts +++ b/src/backend/odoo/userAccount.ts @@ -1,6 +1,7 @@ import { BridgeObject } from '..' import { t } from '../..' import PaymentRequest from './paymentRequest' +import RecurrentContract from './recurrentContract' export default abstract class UserAccount extends BridgeObject { @@ -60,4 +61,59 @@ export default abstract class UserAccount extends BridgeObject { return res } + public async getRecurrentContracts(state: string[]): Promise { + const currencyId = this.getCurrencyId() + const backendType = this.internalId.split(':')[0] + const currency_uri = `${backendType}:${currencyId}` + + const contracts = await this.backends.odoo.$get( + '/payment_request_recurrent_contract/list-payment-request-recurrent-contracts', + { + wallet_uri: this.internalId, + currency_uri: currency_uri, + state: state, + } + ) + return contracts.map((e: any) => new RecurrentContract(this.backends, this, e)) + } + + public async createRecurrentContract( + contracts: Array<{ + sender_wallet_uri: string + receiver_wallet_uri: string + amount: number + message?: string + date_start: string + date_end?: string + recurring_rule_type: t.RecurringRuleType + recurring_interval: number + }> + ): Promise { + const currencyId = this.getCurrencyId() + const backendType = this.internalId.split(':')[0] + const currency_uri = `${backendType}:${currencyId}` + + const res = await this.backends.odoo.$post( + '/payment_request_recurrent_contract/create-payment-request-recurrent-contract', + { + currency_uri: currency_uri, + creator_wallet_uri: this.internalId, + contracts: contracts.map(contract => ({ + sender_wallet_uri: contract.sender_wallet_uri, + receiver_wallet_uri: contract.receiver_wallet_uri, + amount: contract.amount, + message: contract.message || null, + date_start: contract.date_start, + date_end: contract.date_end || null, + recurring_rule_type: contract.recurring_rule_type, + recurring_interval: contract.recurring_interval, + })), + } + ) + if (!res || !Array.isArray(res)) { + throw new Error('Failed to create recurrent contract') + } + return res + } + } diff --git a/src/type.ts b/src/type.ts index c1afc83..55af32e 100644 --- a/src/type.ts +++ b/src/type.ts @@ -83,6 +83,27 @@ export interface IPaymentRequest extends IBridge { } +export type RecurringRuleType = 'daily' | 'weekly' | 'monthly' | 'yearly' + + +export interface IRecurrentContract extends IBridge { + id: number + amount: string + message: string + state: string + creatorWalletUri: string + senderWalletUri: string + receiverWalletUri: string + dateStart: string | null + dateEnd: string | null + recurringRuleType: RecurringRuleType + recurringInterval: number + nextExecutionDate: string | null + + delete(): Promise +} + + export interface IContact extends IBridge { /**