Permalink
Browse files

Merge pull request #2336 from ekylibre/refacto/bookkeep

Make bookkeep able to handle classes instead of blocks only
  • Loading branch information...
Aquaj committed Nov 15, 2018
2 parents 9315832 + f62d782 commit 946a70ea12134977b3365f74dbdb6fda8676a90c
@@ -0,0 +1,67 @@
class ParcelBookkeeper < Ekylibre::Bookkeeper
def call
return unless given?
# For purchase_not_received or sale_not_emitted
bookkeep_payables_not_billed if Preference[:unbilled_payables]
# For permanent stock inventory
bookkeep_stock_inventory if Preference[:permanent_stock_inventory]
end
private
def bookkeep_payables_not_billed
label = tc(:undelivered_invoice,
resource: resource.class.model_name.human,
number: number, entity: entity.full_name, mode: nature.l)
usages = { incoming: :suppliers_invoices_not_received,
outgoing: :invoice_to_create_clients }
account = Account.find_or_import_from_nomenclature(usages[nature.to_sym])
# For unbilled payables
journal = Journal.used_for_unbilled_payables!(currency: currency)
journal_entry(journal, printed_on: printed_on, as: :undelivered_invoice) do |entry|
items.each do |item|
amount = (item.trade_item && item.trade_item.pretax_amount) || item.stock_amount
next unless item.variant && item.variant.charge_account && amount.nonzero?
accounts = { unbilled: account.id,
expense: item.variant.charge_account.id }
generate_entry(entry, amount, label: label, from: accounts.to_a.first, to: accounts.to_a.last, item: item)
end
end
end
# This method permits to add stock journal entries corresponding to the
# incoming or outgoing parcels.
# It depends on the preferences which permit to activate the "permanent stock
# inventory" and "automatic bookkeeping".
#
# | Parcel mode | Debit | Credit |
# | incoming parcel | stock (3X) | stock_movement (603X/71X) |
# | outgoing parcel | stock_movement (603X/71X) | stock (3X) |
def bookkeep_stock_inventory
journal = Journal.used_for_permanent_stock_inventory!(currency: resource.currency)
journal_entry(journal, printed_on: printed_on) do |entry|
label = tc(:bookkeep, resource: resource.class.model_name.human,
number: number, entity: entity.full_name, mode: nature.l)
items.each do |item|
variant = item.variant
next unless variant && variant.storable? && item.stock_amount.nonzero?
accounts = { stock_movement: variant.stock_account_id,
stock: variant.stock_movement_account_id }
generate_entry entry, item.stock_amount, label: label, from: accounts.to_a.first, to: accounts.to_a.last, item: item
end
end
end
def generate_entry(entry_recorder, amount, label:, from:, to:, item:)
from, to = to, from if outgoing?
from_as, from_account = *from
to_as, to_account = *to
entry_recorder.add_debit label, from_account, amount, resource: item, as: from_as, variant: item.variant
entry_recorder.add_credit label, to_account, amount, resource: item, as: to_as, variant: item.variant
end
end
@@ -320,7 +320,7 @@ class Intervention < Ekylibre::Record::Base
# | outputs | stock (3X) | stock_movement (603X/71X) |
# | inputs | stock_movement (603X/71X) | stock (3X) |
bookkeep do |b|
stock_journal = unsuppress { Journal.find_or_create_by!(nature: :stocks) }
stock_journal = Journal.find_or_create_by!(nature: :stocks)
b.journal_entry(stock_journal, printed_on: printed_on, if: (Preference[:permanent_stock_inventory] && record?)) do |entry|
write_parameter_entry_items = lambda do |parameter, input|
variant = parameter.variant
@@ -75,7 +75,7 @@ class Inventory < Ekylibre::Record::Base
# exchange current balance| - balance (3X) | - balance (603X/71X) |
# physical inventory | stock(3X) | stock_movement(603X/71X) |
bookkeep do |b|
journal = unsuppress { Journal.find_or_create_by!(nature: :stocks) }
journal = Journal.find_or_create_by!(nature: :stocks)
# get all variants corresponding to current items
variants = ProductNatureVariant.where(id: Product.where(id: items.pluck(:product_id)).pluck(:variant_id).uniq)
@@ -148,11 +148,11 @@ class Loan < Ekylibre::Record::Base
label = tc(:bookkeep, resource: self.class.model_name.human, name: name)
entry.add_debit(label, cash.account_id, amount, as: :bank)
entry.add_credit(label, unsuppress { loan_account_id }, amount, as: :loan)
entry.add_credit(label, loan_account_id, amount, as: :loan)
if use_bank_guarantee?
label_guarantee = tc(:bookkeep_guarantee_payment, resource: self.class.model_name.human, name: name)
entry.add_debit(label_guarantee, unsuppress { bank_guarantee_account_id }, bank_guarantee_amount, as: :bank_guarantee)
entry.add_debit(label_guarantee, bank_guarantee_account_id, bank_guarantee_amount, as: :bank_guarantee)
entry.add_credit(label_guarantee, cash.account_id, bank_guarantee_amount, as: :bank)
end
end
@@ -81,15 +81,13 @@ class LoanRepayment < Ekylibre::Record::Base
# when payment arrive (due_on)
financial_year = FinancialYear.on(due_on)
# puts [journal.writable_on?(due_on), !locked, accountable, amount > 0, due_on <= Time.zone.today, financial_year.present?, loan.ongoing?].inspect.yellow
unsuppress do
b.journal_entry(journal, printed_on: due_on, if: (!locked && accountable && amount > 0 && due_on <= Time.zone.today && financial_year.present? && loan.ongoing?)) do |entry|
label = tc(:bookkeep, resource: self.class.model_name.human, name: name, year: due_on.year, month: due_on.month, position: position)
# puts label.inspect.magenta
entry.add_debit(label, unsuppress { loan.loan_account_id }, base_amount, as: :repayment)
entry.add_debit(label, unsuppress { loan.interest_account_id }, interest_amount, as: :interest)
entry.add_debit(label, unsuppress { loan.insurance_account_id }, insurance_amount, as: :insurance) if insurance_amount.nonzero?
entry.add_credit(label, cash.account_id, amount, as: :bank)
end
b.journal_entry(journal, printed_on: due_on, if: (!locked && accountable && amount > 0 && due_on <= Time.zone.today && financial_year.present? && loan.ongoing?)) do |entry|
label = tc(:bookkeep, resource: self.class.model_name.human, name: name, year: due_on.year, month: due_on.month, position: position)
# puts label.inspect.magenta
entry.add_debit(label, loan.loan_account_id, base_amount, as: :repayment)
entry.add_debit(label, loan.interest_account_id, interest_amount, as: :interest)
entry.add_debit(label, loan.insurance_account_id, insurance_amount, as: :insurance) if insurance_amount.nonzero?
entry.add_credit(label, cash.account_id, amount, as: :bank)
end
true
end
@@ -171,62 +171,7 @@ class Parcel < Ekylibre::Record::Base
prepared? || given?
end
# This method permits to add stock journal entries corresponding to the
# incoming or outgoing parcels.
# It depends on the preferences which permit to activate the "permanent stock
# inventory" and "automatic bookkeeping".
#
# | Parcel mode | Debit | Credit |
# | incoming parcel | stock (3X) | stock_movement (603X/71X) |
# | outgoing parcel | stock_movement (603X/71X) | stock (3X) |
bookkeep do |b|
# For purchase_not_received or sale_not_emitted
invoice = lambda do |usage, order|
lambda do |entry|
label = tc(:undelivered_invoice,
resource: self.class.model_name.human,
number: number, entity: entity.full_name, mode: nature.l)
account = Account.find_or_import_from_nomenclature(usage)
items.each do |item|
amount = (item.trade_item && item.trade_item.pretax_amount) || item.stock_amount
next unless item.variant && item.variant.charge_account && amount.nonzero?
if order
entry.add_credit label, account.id, amount, resource: item, as: :unbilled, variant: item.variant
entry.add_debit label, item.variant.charge_account.id, amount, resource: item, as: :expense, variant: item.variant
else
entry.add_debit label, account.id, amount, resource: item, as: :unbilled, variant: item.variant
entry.add_credit label, item.variant.charge_account.id, amount, resource: item, as: :expense, variant: item.variant
end
end
end
end
ufb_accountable = Preference[:unbilled_payables] && given?
# For unbilled payables
journal = unsuppress { Journal.used_for_unbilled_payables!(currency: self.currency) }
b.journal_entry(journal, printed_on: printed_on, as: :undelivered_invoice, if: ufb_accountable && incoming?, &invoice.call(:suppliers_invoices_not_received, true))
b.journal_entry(journal, printed_on: printed_on, as: :undelivered_invoice, if: ufb_accountable && outgoing?, &invoice.call(:invoice_to_create_clients, false))
accountable = Preference[:permanent_stock_inventory] && given?
# For permanent stock inventory
journal = unsuppress { Journal.used_for_permanent_stock_inventory!(currency: self.currency) }
b.journal_entry(journal, printed_on: printed_on, if: (Preference[:permanent_stock_inventory] && given?)) do |entry|
label = tc(:bookkeep, resource: self.class.model_name.human,
number: number, entity: entity.full_name, mode: nature.l)
items.each do |item|
variant = item.variant
next unless variant && variant.storable? && item.stock_amount.nonzero?
if incoming?
entry.add_credit(label, variant.stock_movement_account_id, item.stock_amount, resource: item, as: :stock_movement, variant: item.variant)
entry.add_debit(label, variant.stock_account_id, item.stock_amount, resource: item, as: :stock, variant: item.variant)
elsif outgoing?
entry.add_debit(label, variant.stock_movement_account_id, item.stock_amount, resource: item, as: :stock_movement, variant: item.variant)
entry.add_credit(label, variant.stock_account_id, item.stock_amount, resource: item, as: :stock, variant: item.variant)
end
end
end
end
bookkeep
def entity
incoming? ? sender : recipient
@@ -204,7 +204,7 @@ class Purchase < Ekylibre::Record::Base
# For undelivered invoice
# exchange undelivered invoice from parcel
journal = unsuppress { Journal.used_for_unbilled_payables!(currency: currency) }
journal = Journal.used_for_unbilled_payables!(currency: currency)
b.journal_entry(journal, printed_on: invoiced_on, as: :undelivered_invoice, if: (with_accounting && invoice?)) do |entry|
parcels.each do |parcel|
next unless parcel.undelivered_invoice_journal_entry
@@ -219,7 +219,7 @@ class Purchase < Ekylibre::Record::Base
# For gap between parcel item quantity and purchase item quantity
# if more quantity on purchase than parcel then i have value in D of stock account
journal = unsuppress { Journal.used_for_permanent_stock_inventory!(currency: currency) }
journal = Journal.used_for_permanent_stock_inventory!(currency: currency)
b.journal_entry(journal, printed_on: invoiced_on, as: :quantity_gap_on_invoice, if: (with_accounting && invoice? && items.any?)) do |entry|
label = tc(:quantity_gap_on_invoice, resource: self.class.model_name.human, number: number, entity: supplier.full_name)
items.each do |item|
@@ -46,12 +46,12 @@ class PurchaseGap < Gap
acts_as_affairable :supplier, good: :profit?, debit: :loss?, class_name: 'PurchaseAffair'
bookkeep do |b|
b.journal_entry(unsuppress { Journal.used_for_gaps!(currency: currency) },
b.journal_entry(Journal.used_for_gaps!(currency: currency),
printed_on: printed_on, unless: amount.zero?) do |entry|
label = tc(:bookkeep, resource: direction.l, number: number, supplier: supplier.full_name)
entry.add_debit(label, supplier.account(:supplier).id, relative_amount, as: :supplier)
items.each do |item|
entry.add_credit(label, unsuppress { Account.find_or_import_from_nomenclature(profit? ? :other_usual_running_profits : :other_usual_running_expenses) }, item.relative_pretax_amount, resource: item, as: :item_product)
entry.add_credit(label, Account.find_or_import_from_nomenclature(profit? ? :other_usual_running_profits : :other_usual_running_expenses), item.relative_pretax_amount, resource: item, as: :item_product)
entry.add_credit(label, profit? ? item.tax.collect_account_id : item.tax.deduction_account_id, item.relative_taxes_amount, tax: item.tax, pretax_amount: item.relative_pretax_amount, resource: item, as: :item_tax)
end
end
@@ -231,7 +231,7 @@ class Sale < Ekylibre::Record::Base
# For undelivered invoice
# exchange undelivered invoice from parcel
journal = unsuppress { Journal.used_for_unbilled_payables!(currency: self.currency) }
journal = Journal.used_for_unbilled_payables!(currency: self.currency)
b.journal_entry(journal, printed_on: invoiced_on, as: :undelivered_invoice, if: (with_accounting && invoice?)) do |entry|
parcels.each do |parcel|
next unless parcel.undelivered_invoice_journal_entry
@@ -246,7 +246,7 @@ class Sale < Ekylibre::Record::Base
# For gap between parcel item quantity and sale item quantity
# if more quantity on sale than parcel then i have value in C of stock account
journal = unsuppress { Journal.used_for_permanent_stock_inventory!(currency: self.currency) }
journal = Journal.used_for_permanent_stock_inventory!(currency: self.currency)
b.journal_entry(journal, printed_on: invoiced_on, as: :quantity_gap_on_invoice, if: (with_accounting && invoice? && items.any?)) do |entry|
label = tc(:quantity_gap_on_invoice, resource: self.class.model_name.human, number: number, entity: client.full_name)
items.each do |item|
@@ -46,12 +46,12 @@ class SaleGap < Gap
acts_as_affairable :client, good: :profit?, debit: :loss?, class_name: 'SaleAffair'
bookkeep do |b|
b.journal_entry(unsuppress { Journal.used_for_gaps!(currency: currency) },
b.journal_entry(Journal.used_for_gaps!(currency: currency),
printed_on: printed_on, unless: amount.zero?) do |entry|
label = tc(:bookkeep, resource: direction.l, number: number, client: client.full_name)
entry.add_debit(label, client.account(:client).id, relative_amount, as: :client)
items.each do |item|
entry.add_credit(label, unsuppress { Account.find_or_import_from_nomenclature(profit? ? :other_usual_running_profits : :other_usual_running_expenses) }, item.relative_pretax_amount, resource: item, as: :item_product)
entry.add_credit(label, Account.find_or_import_from_nomenclature(profit? ? :other_usual_running_profits : :other_usual_running_expenses), item.relative_pretax_amount, resource: item, as: :item_product)
entry.add_credit(label, profit? ? item.tax.collect_account_id : item.tax.deduction_account_id, item.relative_taxes_amount, tax: item.tax, pretax_amount: item.relative_pretax_amount, resource: item, as: :item_tax)
end
end
@@ -150,7 +150,7 @@ def state_label
# This callback bookkeeps the sale depending on its state
bookkeep do |b|
journal = unsuppress { Journal.used_for_tax_declarations!(currency: currency) }
journal = Journal.used_for_tax_declarations!(currency: currency)
b.journal_entry(journal, printed_on: invoiced_on, if: (has_content? && (validated? || sent?))) do |entry|
label = tc(:bookkeep, resource: self.class.model_name.human, number: number, started_on: started_on.l, stopped_on: stopped_on.l)
items.each do |item|
@@ -12,6 +12,7 @@ class Application < Rails::Application
# Application configuration should go into files in config/initializers
# -- all .rb files in that directory are automatically loaded.
config.autoload_paths << Rails.root.join('lib')
config.autoload_paths << Rails.root.join('app', 'models', 'bookkeepers')
# We want to use the structure.sql file
config.active_record.schema_format = :sql
@@ -1,5 +1,6 @@
module Ekylibre
autoload :Access, 'ekylibre/access'
autoload :Bookkeeper, 'ekylibre/bookkeeper'
autoload :CorporateIdentity, 'ekylibre/corporate_identity'
autoload :Export, 'ekylibre/export'
autoload :FirstRun, 'ekylibre/first_run'
@@ -0,0 +1,25 @@
module Ekylibre
class Bookkeeper
attr_reader :resource, :recorder
def initialize(recorder)
@resource = recorder.resource
@recorder = recorder
end
private
def journal_entry(*args, &block)
recorder.journal_entry(*args, &block)
end
def method_missing(method_name, *args, &block)
super unless resource.respond_to? method_name
resource.send(method_name, *args, &block)
end
def respond_to_missing?(method_name)
resource.respond_to? method_name
end
end
end
@@ -77,9 +77,12 @@ def already_updated?
def unsuppress
yield
rescue ActiveRecord::RecordInvalid => e
Rails.logger.info e.inspect
raise Ekylibre::Record::RecordInvalid.new(e.message, e.record)
rescue ActiveRecord::RecordInvalid => would_be_silently_dropped
Rails.logger.info would_be_silently_dropped.inspect
wont_be_dropped = Ekylibre::Record::RecordInvalid.new(would_be_silently_dropped.message,
would_be_silently_dropped.record)
wont_be_dropped.set_backtrace(would_be_silently_dropped.backtrace)
raise wont_be_dropped
end
class << self
@@ -126,9 +126,22 @@ def self.included(base)
end
module ClassMethods
def bookkeep(options = {}, &block)
raise ArgumentError, 'No given block' unless block_given?
raise ArgumentError, "Wrong number of arguments (#{block.arity} for 1)" unless block.arity == 1
def bookkeep(options_or_klass = nil, options = {}, &block)
klass = nil
if block
options = options_or_klass || options
raise ArgumentError, "Wrong number of arguments (#{block.arity} for 1)" unless block.arity == 1
else
klass = options_or_klass
implicit_bookkeeper_name = "#{self.name}Bookkeeper"
if klass.nil? || const_defined?(implicit_bookkeeper_name)
klass ||= const_get(implicit_bookkeeper_name)
end
raise ArgumentError, 'Provided class does not respond to #call method' unless klass.nil? || klass.instance_methods.include?(:call)
end
raise ArgumentError, 'Neither bookkeeping class nor block given' unless klass || block
configuration = { on: Ekylibre::Record::Bookkeep.actions, column: :accounted_at, method_name: __method__ }
configuration.update(options) if options.is_a?(Hash)
configuration[:column] = configuration[:column].to_s
@@ -144,7 +157,9 @@ def bookkeep(options = {}, &block)
define_method method_name do |action = :create, draft = nil|
draft = ::Preference[:bookkeep_in_draft] if draft.nil?
send(core_method_name, Ekylibre::Record::Bookkeep::Base.new(self, action, draft))
unsuppress do
send(core_method_name, Ekylibre::Record::Bookkeep::Base.new(self, action, draft))
end
self.class.where(id: id).update_all(configuration[:column] => Time.zone.now)
end
@@ -159,7 +174,13 @@ def bookkeep(options = {}, &block)
end
end
send(:define_method, core_method_name, &block)
if block
send(:define_method, core_method_name, &block)
else
send(:define_method, core_method_name) { |*args|
klass.new(*args).call
}
end
end
end
Oops, something went wrong.

0 comments on commit 946a70e

Please sign in to comment.