Class: Sale

Inherits:
Ekylibre::Record::Base show all
Includes:
Attachable, Customizable
Defined in:
app/models/sale.rb

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Customizable

#custom_value, #set_custom_value, #validate_custom_fields

Methods inherited from Ekylibre::Record::Base

#already_updated?, #check_if_destroyable?, #check_if_updateable?, columns_definition, #customizable?, customizable?, #customized?, #destroyable?, #editable?, has_picture, #human_attribute_name, nomenclature_reflections, #old_record, #others, refers_to, #unsuppress, #updateable?

Methods included from Userstamp::Stampable

included

Methods included from Userstamp::Stamper

included

Class Method Details

.affair_classObject


285
286
287
# File 'app/models/sale.rb', line 285

def self.affair_class
  "#{name}Affair".constantize
end

.third_attributeObject


281
282
283
# File 'app/models/sale.rb', line 281

def self.third_attribute
  :client
end

Instance Method Details

#build_creditObject

Build a new sale with new items ready for correction and save


506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
# File 'app/models/sale.rb', line 506

def build_credit
  attrs = %i[affair client address responsible nature
             currency invoice_address transporter].each_with_object({}) do |attribute, hash|
    hash[attribute] = send(attribute) unless send(attribute).nil?
    hash
  end
  attrs[:invoiced_at] = Time.zone.now
  attrs[:credit] = true
  attrs[:credited_sale] = self
  sale_credit = Sale.new(attrs)
  x = []
  items.each do |item|
    attrs = %i[account currency variant reduction_percentage tax
               compute_from unit_pretax_amount unit_amount].each_with_object({}) do |attribute, hash|
      hash[attribute] = item.send(attribute) unless item.send(attribute).nil?
      hash
    end
    %i[pretax_amount amount].each do |v|
      attrs[v] = -1 * item.send(v)
    end
    attrs[:credited_quantity] = item.creditable_quantity
    attrs[:quantity] = -1 * item.creditable_quantity
    attrs[:credited_item] = item
    if attrs[:credited_quantity] > 0
      sale_credit_item = sale_credit.items.build(attrs)
      sale_credit_item.valid?
    end
  end
  sale_credit
end

#can_generate_parcel?Boolean

Check if sale can generate parcel from all the items of the sale

Returns:

  • (Boolean)

353
354
355
# File 'app/models/sale.rb', line 353

def can_generate_parcel?
  items.any? && delivery_address && (order? || invoice?)
end

#cancel!Object


499
500
501
502
503
# File 'app/models/sale.rb', line 499

def cancel!
  s = build_credit
  s.save!
  s.invoice!
end

#cancellable?Boolean

Returns true if sale is cancellable as an invoice

Returns:

  • (Boolean)

495
496
497
# File 'app/models/sale.rb', line 495

def cancellable?
  !credit? && invoice? && amount + credits.sum(:amount) > 0
end

#confirm(confirmed_at = Time.zone.now) ⇒ Object

Confirm the sale order. This permits to define parcels and assert validity of sale


364
365
366
367
368
# File 'app/models/sale.rb', line 364

def confirm(confirmed_at = Time.zone.now)
  return false unless can_confirm?
  update_column(:confirmed_at, confirmed_at || Time.zone.now)
  super
end

#correctObject

Return at draft state


358
359
360
361
# File 'app/models/sale.rb', line 358

def correct
  return false unless can_correct?
  super
end

#deal_amountObject

Gives the amount to use for affair bookkeeping


303
304
305
# File 'app/models/sale.rb', line 303

def deal_amount
  (aborted? || refused? ? 0 : credit? ? -amount : amount)
end

#deal_taxes(mode = :debit) ⇒ Object

Globalizes taxes into an array of hash


308
309
310
311
312
313
314
315
316
317
318
# File 'app/models/sale.rb', line 308

def deal_taxes(mode = :debit)
  return [] if deal_mode_amount(mode).zero?
  taxes = {}
  coeff = (credit? ? -1 : 1).to_d
  # coeff *= (self.send("deal_#{mode}?") ? 1 : -1)
  for item in items
    taxes[item.tax_id] ||= { amount: 0.0.to_d, tax: item.tax }
    taxes[item.tax_id][:amount] += coeff * item.amount
  end
  taxes.values
end

#dealt_atObject

Gives the date to use for affair bookkeeping


298
299
300
# File 'app/models/sale.rb', line 298

def dealt_at
  (invoice? ? invoiced_at : self.created_at)
end

#default_currencyObject


289
290
291
# File 'app/models/sale.rb', line 289

def default_currency
  currency || nature.currency
end

#deliverable?Boolean

Returns true if there is some products to deliver

Returns:

  • (Boolean)

437
438
439
440
441
# File 'app/models/sale.rb', line 437

def deliverable?
  # not self.undelivered(:quantity).zero? and (self.invoice? or self.order?)
  # !self.undelivered_items.count.zero? and (self.invoice? or self.order?)
  true
end

#duplicatable?Boolean

Returns:

  • (Boolean)

397
398
399
# File 'app/models/sale.rb', line 397

def duplicatable?
  !credit
end

#duplicate(attributes = {}) ⇒ Object

Duplicates a sale in estimate state with its items and its active subscriptions

Raises:

  • (StandardError)

403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
# File 'app/models/sale.rb', line 403

def duplicate(attributes = {})
  raise StandardError, 'Uncancelable sale' unless duplicatable?
  hash = %i[
    client_id nature_id letter_format annotation subject
    function_title introduction conclusion description
  ].each_with_object({}) do |field, h|
    h[field] = send(field)
  end
  # Items
  items_attributes = {}
  items.order(:position).each_with_index do |item, index|
    attrs = %i[
      variant_id quantity amount label pretax_amount annotation
      reduction_percentage tax_id unit_amount unit_pretax_amount
    ].each_with_object({}) do |field, h|
      h[field] = item.send(field)
    end
    # Subscription
    subscription = item.subscription
    if subscription
      attrs[:subscription_attributes] = subscription.following_attributes
    end
    items_attributes[index.to_s] = attrs
  end
  hash[:items_attributes] = items_attributes
  self.class.create!(hash.with_indifferent_access.deep_merge(attributes))
end

#has_content?Boolean

Test if there is some items in the sale.

Returns:

  • (Boolean)

342
343
344
# File 'app/models/sale.rb', line 342

def has_content?
  items.any?
end

#invoice(invoiced_at = Time.zone.now) ⇒ Object

Invoices all the products creating the delivery if necessary. Changes number with an invoice number saving exiting number in initial_number.


372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
# File 'app/models/sale.rb', line 372

def invoice(invoiced_at = Time.zone.now)
  return false unless can_invoice?
  ActiveRecord::Base.transaction do
    # Set values for invoice
    self.invoiced_at ||= invoiced_at
    self.confirmed_at ||= self.invoiced_at
    self.payment_at ||= Delay.new(self.payment_delay).compute(self.invoiced_at)
    self.initial_number = number

    if sequence = Sequence.of(:sales_invoices)
      loop do
        self.number = sequence.next_value!
        break unless self.class.find_by(number: number)
      end
    end

    save!

    client.add_event(:sales_invoice_creation, updater.person) if updater

    return super
  end
  false
end

#invoiced_onObject


277
278
279
# File 'app/models/sale.rb', line 277

def invoiced_on
  dealt_at.to_date
end

#letter?Boolean

Alias for letter_format? method

Returns:

  • (Boolean)

450
451
452
# File 'app/models/sale.rb', line 450

def letter?
  letter_format?
end

#mail_addressObject


454
455
456
# File 'app/models/sale.rb', line 454

def mail_address
  (address || client.default_mail_address).mail_coordinate
end

#nameObject Also known as: label

Label of the sales order depending on the state and the number


444
445
446
# File 'app/models/sale.rb', line 444

def name
  tc("label.#{credit? && invoice? ? :credit : state}", number: number)
end

#nature=(value) ⇒ Object


331
332
333
334
# File 'app/models/sale.rb', line 331

def nature=(value)
  super(value)
  self.currency = self.nature.currency if self.nature
end

#number_labelObject


458
459
460
# File 'app/models/sale.rb', line 458

def number_label
  tc('number_label.' + (estimate? ? 'proposal' : 'command'), number: number)
end

#partially_closed?Boolean

Returns:

  • (Boolean)

320
321
322
# File 'app/models/sale.rb', line 320

def partially_closed?
  !affair.debit.zero? && !affair.credit.zero?
end

#productsObject


490
491
492
# File 'app/models/sale.rb', line 490

def products
  items.map { |item| item.product.name }.join(', ')
end

#refreshObject

Save a new time


337
338
339
# File 'app/models/sale.rb', line 337

def refresh
  save
end

#sales_conditionsObject

Build general sales condition for the sale order


477
478
479
480
481
482
483
484
# File 'app/models/sale.rb', line 477

def sales_conditions
  c = []
  c << tc('sales_conditions.downpayment', percentage: self.nature.downpayment_percentage, amount: self.downpayment_amount.l(currency: self.currency)) if amount > self.nature.downpayment_minimum && has_downpayment
  c << tc('sales_conditions.validity', expiration: self.expired_at.l)
  c += self.nature.sales_conditions.to_s.split(/\s*\n\s*/) if self.nature.sales_conditions
  # c += self.responsible.team.sales_conditions.to_s.split(/\s*\n\s*/) if self.responsible and self.responsible.team
  c
end

#sales_mentionsObject


466
467
468
469
470
471
472
473
474
# File 'app/models/sale.rb', line 466

def sales_mentions
  # get preference for sales conditions
  preference_sales_conditions = Preference.global.find_by(name: :sales_conditions)
  if preference_sales_conditions
    return preference_sales_conditions.value
  else
    return nil
  end
end

#sold?Boolean

Returns if the sale has been validated and so if it can be considered as sold.

Returns:

  • (Boolean)

348
349
350
# File 'app/models/sale.rb', line 348

def sold?
  (order? || invoice?)
end

#state_labelObject

Prints human name of current state


432
433
434
# File 'app/models/sale.rb', line 432

def state_label
  self.class.state_machine.state(state.to_sym).human_name
end

#statusObject

Returns status of affair if invoiced else “stop”


538
539
540
541
# File 'app/models/sale.rb', line 538

def status
  return affair.status if invoice? && affair
  :stop
end

#supplierObject


324
325
326
# File 'app/models/sale.rb', line 324

def supplier
  Entity.of_company
end

#taxes_amountObject


462
463
464
# File 'app/models/sale.rb', line 462

def taxes_amount
  amount - pretax_amount
end

#thirdObject


293
294
295
# File 'app/models/sale.rb', line 293

def third
  send(third_attribute)
end

#unpaid_daysObject


486
487
488
# File 'app/models/sale.rb', line 486

def unpaid_days
  (Time.zone.now - self.invoiced_at) if invoice?
end