Skip to main content

ExpenseAccountingService

Struct ExpenseAccountingService 

Source
pub struct ExpenseAccountingService { /* private fields */ }
Expand description

Service for automatically generating journal entries from expenses

This service implements Belgian accounting logic based on PCMN (AR 12/07/2012):

  • Expense creates debit to expense account (class 6)
  • VAT creates debit to VAT recoverable account (4110)
  • Total amount creates credit to supplier account (4400)

Inspired by Noalyss’ automatic journal entry generation

Implementations§

Source§

impl ExpenseAccountingService

Source

pub fn new(journal_entry_repo: Arc<dyn JournalEntryRepository>) -> Self

Source

pub async fn generate_journal_entry_for_expense( &self, expense: &Expense, created_by: Option<Uuid>, ) -> Result<JournalEntry, String>

Generate journal entry for an expense

§Belgian Accounting Logic (PCMN)

Example: 1,000€ HT + 210€ VAT (21%) = 1,210€ TTC

Debit:  6100 (Expense account)     1,000.00€
Debit:  4110 (VAT Recoverable)       210.00€
Credit: 4400 (Suppliers)           1,210.00€
§Arguments
  • expense: The expense to generate journal entry for
  • created_by: User who created the expense
§Returns
  • Ok(JournalEntry) if generation successful
  • Err(String) if validation fails or expense has no account_code
Source

pub async fn generate_payment_entry( &self, expense: &Expense, payment_account: Option<String>, created_by: Option<Uuid>, ) -> Result<JournalEntry, String>

Generate journal entry for expense payment

When an expense is paid, we record the payment:

Debit:  4400 (Suppliers)           1,210.00€
Credit: 5500 (Bank)                1,210.00€
§Arguments
  • expense: The expense being paid
  • payment_account: Account used for payment (default: 5500 Bank)
  • created_by: User who recorded the payment
Source

pub async fn generate_contribution_receipt_entry( &self, contribution: &OwnerContribution, building_id: Option<Uuid>, payment_account: Option<String>, created_by: Option<Uuid>, ) -> Result<JournalEntry, String>

Génère l’écriture d’ENCAISSEMENT d’une quote-part de copropriétaire.

§Logique comptable (PCMN belge)

Exemple : quote-part de 2 000 € reçue par virement.

Débit :  550 (Compte courant bancaire)   2 000,00 €
Crédit : 400 (Copropriétaires)           2 000,00 €

L’argent entre (actif en hausse), la créance sur le copropriétaire s’éteint (actif en baisse). Journal FIN.

§Ce que cette écriture n’est PAS

Elle ne constate pas le PRODUIT. L’appel de fonds est un produit de classe 7 constaté à son émission, pas à son encaissement ; cette écriture-ci ne fait que solder une créance déjà comptabilisée. Générer un produit ici le compterait deux fois.

§Idempotence

Une quote-part peut être soldée par deux voies : mark-paid depuis l’interface, ou la réussite d’un paiement portant contribution_id. Sans ce contrôle, un même encaissement débiterait la banque deux fois.

Source

pub async fn expense_has_journal_entries( &self, expense_id: Uuid, ) -> Result<bool, String>

Check if expense already has journal entries

Prevents duplicate journal entries for the same expense.

Source

pub async fn get_expense_journal_entries( &self, expense_id: Uuid, ) -> Result<Vec<JournalEntry>, String>

Get journal entries for an expense

Returns all entries (expense entry + payment entry if paid).

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

§

impl<T> Fake for T

§

fn fake<U>(&self) -> U
where Self: FakeBase<U>,

§

fn fake_with_rng<U, R>(&self, rng: &mut R) -> U
where R: RngExt + ?Sized, Self: FakeBase<U>,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
§

impl<Unshared, Shared> IntoShared<Shared> for Unshared
where Shared: FromUnshared<Unshared>,

§

fn into_shared(self) -> Shared

Creates a shared type from an unshared type.
§

impl<T> PolicyExt for T
where T: ?Sized,

§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns [Action::Follow] only if self and other return Action::Follow. Read more
§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns [Action::Follow] if either self or other returns Action::Follow. Read more
§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more