Class: Parcel

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

Overview

Informations

License

Ekylibre - Simple agricultural ERP Copyright (C) 2008-2009 Brice Texier, Thibaud Merigon Copyright (C) 2010-2012 Brice Texier Copyright (C) 2012-2019 Brice Texier, David Joulin

This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License along with this program. If not, see www.gnu.org/licenses.

Table: parcels

accounted_at                         :datetime
address_id                           :integer
contract_id                          :integer
created_at                           :datetime         not null
creator_id                           :integer
currency                             :string
custom_fields                        :jsonb
delivery_id                          :integer
delivery_mode                        :string
given_at                             :datetime
id                                   :integer          not null, primary key
in_preparation_at                    :datetime
intervention_id                      :integer
journal_entry_id                     :integer
late_delivery                        :boolean
lock_version                         :integer          default(0), not null
nature                               :string           not null
number                               :string           not null
ordered_at                           :datetime
planned_at                           :datetime         not null
position                             :integer
prepared_at                          :datetime
pretax_amount                        :decimal(19, 4)   default(0.0), not null
purchase_id                          :integer
recipient_id                         :integer
reconciliation_state                 :string
reference_number                     :string
remain_owner                         :boolean          default(FALSE), not null
responsible_id                       :integer
sale_id                              :integer
sender_id                            :integer
separated_stock                      :boolean
state                                :string           not null
storage_id                           :integer
transporter_id                       :integer
type                                 :string
undelivered_invoice_journal_entry_id :integer
updated_at                           :datetime         not null
updater_id                           :integer
with_delivery                        :boolean          default(FALSE), not null

Direct Known Subclasses

Reception, Shipment

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

.convert_to_purchase(parcels) ⇒ Object

Convert parcels to one purchase. Assume that all parcels are checked before. Purchase is written in DB with default values


322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
# File 'app/models/parcel.rb', line 322

def convert_to_purchase(parcels)
  purchase = nil
  transaction do
    parcels = parcels.collect do |d|
      (d.is_a?(self) ? d : find(d))
    end.sort_by(&:first_available_date)
    third = detect_third(parcels)
    planned_at = parcels.last.first_available_date || Time.zone.now
    unless nature = PurchaseNature.by_default
      unless journal = Journal.purchases.opened_on(planned_at).first
        raise 'No purchase journal'
      end
      nature = PurchaseNature.create!(
        active: true,
        currency: Preference[:currency],
        with_accounting: true,
        journal: journal,
        by_default: true,
        name: PurchaseNature.tc('default.name', default: PurchaseNature.model_name.human)
      )
    end
    purchase = Purchase.create!(
      supplier: third,
      nature: nature,
      planned_at: planned_at,
      delivery_address: parcels.last.address
    )

    # Adds items
    parcels.each do |parcel|
      parcel.items.order(:id).each do |item|
        next unless item.variant.purchasable? && item.population && item.population > 0
        catalog_item = Catalog.by_default!(:purchase).items.find_by(variant: item.variant)
        unit_pretax_amount = item.pretax_amount.zero? ? nil : item.pretax_amount
        tax = Tax.current.first
        # check all taxes included to build unit_pretax_amount and tax from catalog with all taxes included
        if catalog_item && catalog_item.all_taxes_included
          unit_pretax_amount ||= catalog_item.reference_tax.pretax_amount_of(catalog_item.amount)
          tax = catalog_item.reference_tax || item.variant.category.purchase_taxes.first || Tax.current.first
        # from catalog without taxes
        elsif catalog_item
          unit_pretax_amount ||= catalog_item.amount
        # from last purchase item
        elsif (last_purchase_items = PurchaseItem.where(variant: item.variant)) && last_purchase_items.any?
          unit_pretax_amount ||= last_purchase_items.order(id: :desc).first.unit_pretax_amount
          tax = last_purchase_items.order(id: :desc).first.tax
        end
        item.purchase_item = purchase.items.create!(
          variant: item.variant,
          unit_pretax_amount: unit_pretax_amount || 0.0,
          tax: tax,
          quantity: item.population
        )
        item.save!
      end
      parcel.reload
      parcel.purchase = purchase
      parcel.save!
    end

    # Refreshes affair
    purchase.save!
  end
  purchase
end

.convert_to_sale(parcels) ⇒ Object

Convert parcels to one sale. Assume that all parcels are checked before. Sale is written in DB with default values


254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
# File 'app/models/parcel.rb', line 254

def convert_to_sale(parcels)
  sale = nil
  transaction do
    parcels = parcels.collect do |d|
      (d.is_a?(self) ? d : find(d))
    end.sort_by(&:first_available_date)
    third = detect_third(parcels)
    planned_at = parcels.last.first_available_date || Time.zone.now
    unless nature = SaleNature.by_default
      unless journal = Journal.sales.opened_on(planned_at).first
        raise 'No sale journal'
      end
      nature = SaleNature.create!(
        active: true,
        currency: Preference[:currency],
        journal: journal,
        by_default: true,
        name: SaleNature.tc('default.name', default: SaleNature.model_name.human)
      )
    end
    sale = Sale.create!(
      client: third,
      nature: nature,
      # created_at: planned_at,
      delivery_address: parcels.last.address
    )

    # Adds items
    parcels.each do |parcel|
      parcel.items.order(:id).each do |item|
        # raise "#{item.variant.name} cannot be sold" unless item.variant.saleable?
        next unless item.variant.saleable? && item.population && item.population > 0
        catalog_item = Catalog.by_default!(:sale).items.find_by(variant: item.variant)
        # check all taxes included to build unit_pretax_amount and tax from catalog with all taxes included
        unit_pretax_amount = item.pretax_amount.zero? ? nil : item.pretax_amount
        tax = Tax.current.first
        if catalog_item && catalog_item.all_taxes_included
          unit_pretax_amount ||= catalog_item.reference_tax.pretax_amount_of(catalog_item.amount)
          tax = catalog_item.reference_tax || item.variant.category.sale_taxes.first || Tax.current.first
        # from catalog without taxes
        elsif catalog_item
          unit_pretax_amount ||= catalog_item.amount
        # from last sale item
        elsif (last_sale_items = SaleItem.where(variant: item.variant)) && last_sale_items.any?
          unit_pretax_amount ||= last_sale_items.order(id: :desc).first.unit_pretax_amount
          tax = last_sale_items.order(id: :desc).first.tax
        end
        item.sale_item = sale.items.create!(
          variant: item.variant,
          unit_pretax_amount: unit_pretax_amount || 0.0,
          tax: tax,
          quantity: item.population
        )
        item.save!
      end
      parcel.reload
      parcel.sale_id = sale.id
      parcel.save!
    end

    # Refreshes affair
    sale.save!
  end
  sale
end

.detect_third(parcels) ⇒ Object


388
389
390
391
392
# File 'app/models/parcel.rb', line 388

def detect_third(parcels)
  thirds = parcels.map(&:third_id).uniq
  raise "Need unique third (#{thirds.inspect})" if thirds.count != 1
  Entity.find(thirds.first)
end

.transporters_of(parcels) ⇒ Object

Returns an array of all the transporter ids for the given parcels


248
249
250
# File 'app/models/parcel.rb', line 248

def transporters_of(parcels)
  parcels.map(&:transporter_id).compact
end

Instance Method Details

#address_coordinateObject


187
188
189
# File 'app/models/parcel.rb', line 187

def address_coordinate
  address.coordinate if address
end

#address_mail_coordinateObject


191
192
193
# File 'app/models/parcel.rb', line 191

def address_mail_coordinate
  (address || sale.client.default_mail_address).mail_coordinate
end

#all_items_prepared?Boolean

Returns:

  • (Boolean)

218
219
220
# File 'app/models/parcel.rb', line 218

def all_items_prepared?
  any_items? && items.all?(&:prepared?)
end

#allow_items_update?Boolean

Returns:

  • (Boolean)

Raises:

  • (NotImplementedError)

183
184
185
# File 'app/models/parcel.rb', line 183

def allow_items_update?
  raise NotImplementedError
end

#any_items?Boolean

Returns:

  • (Boolean)

222
223
224
# File 'app/models/parcel.rb', line 222

def any_items?
  items.any?
end

#content_sentenceObject


155
156
157
# File 'app/models/parcel.rb', line 155

def content_sentence
  sentence = items.map(&:name).compact.to_sentence
end

#delivery?Boolean

Returns:

  • (Boolean)

171
172
173
# File 'app/models/parcel.rb', line 171

def delivery?
  delivery.present?
end

#delivery_started?Boolean

Returns:

  • (Boolean)

175
176
177
# File 'app/models/parcel.rb', line 175

def delivery_started?
  delivery?
end

#entityObject


143
144
145
# File 'app/models/parcel.rb', line 143

def entity
  incoming? ? sender : recipient
end

#first_available_dateObject


242
243
244
# File 'app/models/parcel.rb', line 242

def first_available_date
  given_at || planned_at || prepared_at || in_preparation_at || ordered_at
end

#giveable?Boolean

Returns:

  • (Boolean)

230
231
232
# File 'app/models/parcel.rb', line 230

def giveable?
  !with_delivery || (with_delivery && delivery.present? && delivery.started?)
end

#human_delivery_modeObject


195
196
197
# File 'app/models/parcel.rb', line 195

def human_delivery_mode
  delivery_mode.text
end

#human_delivery_natureObject


199
200
201
# File 'app/models/parcel.rb', line 199

def human_delivery_nature
  nature.text
end

#invoiceable?Boolean

Returns:

  • (Boolean)

167
168
169
# File 'app/models/parcel.rb', line 167

def invoiceable?
  !invoiced?
end

#invoiced?Boolean

Returns:

  • (Boolean)

163
164
165
# File 'app/models/parcel.rb', line 163

def invoiced?
  purchase.present? || sale.present?
end

#issues?Boolean

Returns:

  • (Boolean)

226
227
228
# File 'app/models/parcel.rb', line 226

def issues?
  issues.any?
end

#items_quantityObject

Number of products delivered


214
215
216
# File 'app/models/parcel.rb', line 214

def items_quantity
  items.sum(:population)
end

#natureObject


203
204
205
206
# File 'app/models/parcel.rb', line 203

def nature
  # ActiveSupport::Deprecation.warn('Parcel#nature is deprecated, please use Parcel#type instead. This method will be removed in next major release 3.0')
  super
end

#nature=(value) ⇒ Object


208
209
210
211
# File 'app/models/parcel.rb', line 208

def nature=(value)
  # ActiveSupport::Deprecation.warn('Parcel#nature= is deprecated, please use STI instead. This method will be removed in next major release 3.0')
  super(value)
end

#printed_atObject


147
148
149
# File 'app/models/parcel.rb', line 147

def printed_at
  given_at || created_at || Time.zone.now
end

#printed_onObject


151
152
153
# File 'app/models/parcel.rb', line 151

def printed_on
  printed_at.to_date
end

#separated_stock?Boolean

Returns:

  • (Boolean)

159
160
161
# File 'app/models/parcel.rb', line 159

def separated_stock?
  separated_stock
end

#shippable?Boolean

Returns:

  • (Boolean)

179
180
181
# File 'app/models/parcel.rb', line 179

def shippable?
  with_delivery && delivery.blank?
end

#statusObject


234
235
236
237
238
239
240
# File 'app/models/parcel.rb', line 234

def status
  if given?
    (issues? ? :caution : :go)
  else
    (issues? ? :stop : :caution)
  end
end