class LucaDeal::Fee
Public Class Methods
new(date = nil)
click to toggle source
# File lib/luca_deal/fee.rb, line 16 def initialize(date = nil) @date = issue_date(date) end
Public Instance Methods
compose_mail(dat, mode: nil, attachment: :html)
click to toggle source
# File lib/luca_deal/fee.rb, line 98 def compose_mail(dat, mode: nil, attachment: :html) @company = set_company fee_vars(dat) mail = Mail.new mail.to = dat.dig('customer', 'to') if mode.nil? mail.subject = CONFIG.dig('fee', 'mail_subject') || 'Your Report is available' if mode == :preview mail.cc = CONFIG.dig('mail', 'preview') || CONFIG.dig('mail', 'from') mail.subject = '[preview] ' + mail.subject end mail.text_part = Mail::Part.new(body: render_erb(search_template('fee-report-mail.txt.erb')), charset: 'UTF-8') mail.attachments[attachment_name(dat, attachment)] = render_report(attachment) mail end
deliver_mail(attachment_type = nil, mode: nil, skip_no_item: true)
click to toggle source
# File lib/luca_deal/fee.rb, line 69 def deliver_mail(attachment_type = nil, mode: nil, skip_no_item: true) attachment_type = CONFIG.dig('fee', 'attachment') || :html fees = self.class.asof(@date.year, @date.month) raise "No report for #{@date.year}/#{@date.month}" if fees.count.zero? fees.each do |dat, path| next if has_status?(dat, 'mail_delivered') next if skip_no_item && dat['items'].empty? mail = compose_mail(dat, mode: mode, attachment: attachment_type.to_sym) LucaSupport::Mail.new(mail, PJDIR).deliver self.class.add_status!(path, 'mail_delivered') if mode.nil? end end
export_json()
click to toggle source
# File lib/luca_deal/fee.rb, line 155 def export_json labels = export_labels [].tap do |res| self.class.asof(@date.year, @date.month) do |dat| item = {} item['date'] = dat['issue_date'] item['debit'] = [] item['credit'] = [] sub = dat['sales_fee'] if readable(sub['fee']) != 0 item['debit'] << { 'label' => labels[:debit][:fee], 'amount' => readable(sub['fee']) } item['credit'] << { 'label' => labels[:credit][:fee], 'amount' => readable(sub['fee']) } end if readable(sub['tax']) != 0 item['debit'] << { 'label' => labels[:debit][:tax], 'amount' => readable(sub['tax']) } item['credit'] << { 'label' => labels[:credit][:tax], 'amount' => readable(sub['tax']) } end if readable(sub['deduction']) != 0 item['debit'] << { 'label' => labels[:debit][:deduction], 'amount' => readable(sub['deduction'] * -1) } item['credit'] << { 'label' => sub['deduction_label'] || labels[:credit][:deduction], 'amount' => readable(sub['deduction'] * -1) } end item['x-customer'] = dat['customer']['name'] if dat.dig('customer', 'name') item['x-editor'] = 'LucaDeal' res << item end puts JSON.dump(res) end end
get_customer(id)
click to toggle source
# File lib/luca_deal/fee.rb, line 195 def get_customer(id) {}.tap do |res| Customer.find(id) do |dat| customer = parse_current(dat) res['id'] = customer['id'] res['name'] = customer.dig('name') res['address'] = customer.dig('address') res['address2'] = customer.dig('address2') res['to'] = customer.dig('contacts').map { |h| take_current(h, 'mail') }.compact end end end
monthly_fee()
click to toggle source
calculate fee, based on invoices
# File lib/luca_deal/fee.rb, line 22 def monthly_fee Contract.asof(@date.year, @date.month, @date.day) do |contract| next if contract.dig('terms', 'category') != 'sales_fee' next if duplicated_contract? contract['id'] @rate = { 'default' => BigDecimal(contract.dig('rate', 'default')) } @rate['initial'] = contract.dig('rate', 'initial') ? BigDecimal(contract.dig('rate', 'initial')) : @rate['default'] limit = contract.dig('terms', 'limit') fee = { 'contract_id' => contract['id'], 'items' => [], 'sales_fee' => { 'fee' => 0, 'tax' => 0, 'deduction' => 0, 'deduction_label' => contract.dig('terms', 'deduction_label') } } fee['customer'] = get_customer(contract['customer_id']) fee['issue_date'] = @date Invoice.asof(@date.year, @date.month) do |invoice| next if invoice.dig('sales_fee', 'id') != contract['id'] next if exceed_limit?(invoice, limit) invoice['items'].each do |item| rate = item['type'] == 'initial' ? @rate['initial'] : @rate['default'] fee['items'] << fee_record(invoice, item, rate) end subtotal(fee['items']).each{ |k, v| fee['sales_fee'][k] += v } end NoInvoice.asof(@date.year, @date.month) do |no_invoice| next if no_invoice.dig('sales_fee', 'id') != contract['id'] next if exceed_limit?(no_invoice, limit) no_invoice['items'].each do |item| rate = item['type'] == 'initial' ? @rate['initial'] : @rate['default'] fee['items'] << fee_record(no_invoice, item, rate) end subtotal(fee['items']).each{ |k, v| fee['sales_fee'][k] += v } end deduction_rate = contract.dig('rate', 'deduction') fee['sales_fee']['deduction'] = -1 * (fee['sales_fee']['fee'] * deduction_rate).floor if deduction_rate self.class.create(fee, date: @date, codes: Array(contract['id'])) end end
preview_mail(attachment_type = nil)
click to toggle source
# File lib/luca_deal/fee.rb, line 84 def preview_mail(attachment_type = nil) deliver_mail(attachment_type, mode: :preview) end
preview_stdout()
click to toggle source
Render HTML to console
# File lib/luca_deal/fee.rb, line 90 def preview_stdout self.class.asof(@date.year, @date.month) do |dat, _| @company = set_company fee_vars(dat) puts render_report end end
render_report(file_type = :html)
click to toggle source
# File lib/luca_deal/fee.rb, line 184 def render_report(file_type = :html) case file_type when :html render_erb(search_template('fee-report.html.erb')) when :pdf erb2pdf(search_template('fee-report.html.erb')) else raise 'This filetype is not supported.' end end
stats(count = 1)
click to toggle source
Output seriarized fee data to stdout. Returns previous N months on multiple count
Example YAML output¶ ↑
--- - records: - customer: Example Co. subtotal: 100000 tax: 10000 due: 2020-10-31 issue_date: '2020-09-30' count: 1 total: 100000 tax: 10000
# File lib/luca_deal/fee.rb, line 129 def stats(count = 1) [].tap do |collection| scan_date = @date.next_month count.times do scan_date = scan_date.prev_month {}.tap do |stat| stat['records'] = self.class.asof(scan_date.year, scan_date.month).map do |fee| { 'customer' => fee.dig('customer', 'name'), 'client' => fee['items'].map{ |item| item.dig('customer_name') }.join(' / '), 'subtotal' => fee.dig('sales_fee', 'fee'), 'tax' => fee.dig('sales_fee', 'tax'), 'due' => fee.dig('due_date'), 'mail' => fee.dig('status')&.select { |a| a.keys.include?('mail_delivered') }&.first } end stat['issue_date'] = scan_date.to_s stat['count'] = stat['records'].count stat['total'] = stat['records'].inject(0) { |sum, rec| sum + rec.dig('subtotal') } stat['tax'] = stat['records'].inject(0) { |sum, rec| sum + rec.dig('tax') } collection << readable(stat) end end end end
Private Instance Methods
attachment_name(dat, type)
click to toggle source
# File lib/luca_deal/fee.rb, line 265 def attachment_name(dat, type) id = %r{/}.match(dat['id']) ? dat['id'].gsub('/', '') : dat['id'][0, 7] "feereport-#{id}.#{type}" end
due_date(date)
click to toggle source
TODO: support due_date
variation
# File lib/luca_deal/fee.rb, line 276 def due_date(date) next_month = date.next_month Date.new(next_month.year, next_month.month, -1) end
duplicated_contract?(id)
click to toggle source
Fees are unique contract_id in each month If update needed, remove the target fee file.
# File lib/luca_deal/fee.rb, line 292 def duplicated_contract?(id) self.class.asof(@date.year, @date.month, @date.day) do |_f, path| return true if path.include?(id) end false end
exceed_limit?(invoice, limit)
click to toggle source
# File lib/luca_deal/fee.rb, line 310 def exceed_limit?(invoice, limit) return false if limit.nil? contract_start = Contract.find(invoice['contract_id']).dig('terms', 'effective') contract_start.next_month(limit).prev_day < @date end
export_labels()
click to toggle source
TODO: load labels from CONFIG before country defaults
# File lib/luca_deal/fee.rb, line 229 def export_labels case CONFIG['country'] when 'jp' { debit: { fee: '支払手数料', tax: '支払手数料', deduction: '未払費用' }, credit: { fee: '未払費用', tax: '未払費用', deduction: '雑収入' } } else { debit: { fee: 'Fees and commisions', tax: 'Fees and commisions', deduction: 'Accounts payable - other' }, credit: { fee: 'Accounts payable - other', tax: 'Accounts payable - other', deduction: 'Miscellaneous income' } } end end
fee_record(invoice, item, rate)
click to toggle source
# File lib/luca_deal/fee.rb, line 299 def fee_record(invoice, item, rate) { 'invoice_id' => invoice['id'], 'customer_name' => invoice.dig('customer', 'name'), 'name' => item['name'], 'price' => item['price'], 'qty' => item['qty'], 'fee' => item['price'] * item['qty'] * rate } end
fee_vars(fee_dat)
click to toggle source
set variables for ERB template
# File lib/luca_deal/fee.rb, line 212 def fee_vars(fee_dat) @customer = fee_dat['customer'] @items = readable(fee_dat['items']) @sales_fee = readable(fee_dat['sales_fee']) @issue_date = fee_dat['issue_date'] @due_date = fee_dat['due_date'] @amount = readable(fee_dat['sales_fee'] .reject{ |k, _v| k == 'deduction_label' } .inject(0) { |sum, (_k, v)| sum + v }) end
issue_date(date)
click to toggle source
# File lib/luca_deal/fee.rb, line 270 def issue_date(date) base = date.nil? ? Date.today : Date.parse(date) Date.new(base.year, base.month, -1) end
lib_path()
click to toggle source
# File lib/luca_deal/fee.rb, line 223 def lib_path __dir__ end
load_tax_rate(name)
click to toggle source
load Tax Rate from config.
# File lib/luca_deal/fee.rb, line 283 def load_tax_rate(name) return 0 if CONFIG.dig('tax_rate', name).nil? BigDecimal(take_current(CONFIG['tax_rate'], name).to_s) end
set_company()
click to toggle source
load user company profile from config.
# File lib/luca_deal/fee.rb, line 246 def set_company {}.tap do |h| h['name'] = CONFIG.dig('company', 'name') h['address'] = CONFIG.dig('company', 'address') h['address2'] = CONFIG.dig('company', 'address2') end end
subtotal(items)
click to toggle source
calc fee & tax amount by tax category
# File lib/luca_deal/fee.rb, line 256 def subtotal(items) { 'fee' => 0, 'tax' => 0 }.tap do |subtotal| items.each do |i| subtotal['fee'] += i['fee'] end subtotal['tax'] = (subtotal['fee'] * load_tax_rate('default')).to_i end end