foodsoft/app/models/supplier.rb

160 lines
6 KiB
Ruby
Raw Permalink Normal View History

class Supplier < ApplicationRecord
include MarkAsDeletedWithName
include CustomFields
has_many :articles, lambda {
where(type: nil).includes(:article_category).order('article_categories.name', 'articles.name')
}
has_many :stock_articles, -> { includes(:article_category).order('article_categories.name', 'articles.name') }
2009-01-06 11:49:19 +01:00
has_many :orders
has_many :deliveries
has_many :invoices
belongs_to :supplier_category
belongs_to :shared_supplier, optional: true # for the sharedLists-App
validates :name, presence: true, length: { in: 4..30 }
validates :phone, presence: true, length: { in: 8..25 }
validates :address, presence: true, length: { in: 8..50 }
validates :iban, format: { with: /\A[A-Z]{2}[0-9]{2}[0-9A-Z]{,30}\z/, allow_blank: true }
validates :iban, uniqueness: { case_sensitive: false, allow_blank: true }
validates :order_howto, :note, length: { maximum: 250 }
validate :valid_shared_sync_method
validate :uniqueness_of_name
scope :undeleted, -> { where(deleted_at: nil) }
2015-02-18 23:46:50 +01:00
scope :having_articles, -> { where(id: Article.undeleted.select(:supplier_id).distinct) }
def self.ransackable_attributes(_auth_object = nil)
%w[id name]
2018-10-13 16:16:44 +02:00
end
def self.ransackable_associations(_auth_object = nil)
%w[articles stock_articles orders]
2018-10-13 16:16:44 +02:00
end
2009-01-06 11:49:19 +01:00
# sync all articles with the external database
# returns an array with articles(and prices), which should be updated (to use in a form)
# also returns an array with outlisted_articles, which should be deleted
# also returns an array with new articles, which should be added (depending on shared_sync_method)
2009-01-06 11:49:19 +01:00
def sync_all
updated_article_pairs = []
outlisted_articles = []
new_articles = []
existing_articles = Set.new
for article in articles.undeleted
2009-01-06 11:49:19 +01:00
# try to find the associated shared_article
2014-05-21 21:44:35 +02:00
shared_article = article.shared_article(self)
2009-02-10 13:26:10 +01:00
if shared_article # article will be updated
existing_articles.add(shared_article.id)
2014-05-21 21:44:35 +02:00
unequal_attributes = article.shared_article_changed?(self)
if unequal_attributes.present? # skip if shared_article has not been changed
article.attributes = unequal_attributes
2015-03-27 19:03:21 +01:00
updated_article_pairs << [article, unequal_attributes]
2009-01-06 11:49:19 +01:00
end
# Articles with no order number can be used to put non-shared articles
# in a shared supplier, with sync keeping them.
elsif article.order_number.present?
2009-01-06 11:49:19 +01:00
# article isn't in external database anymore
outlisted_articles << article
end
end
# Find any new articles, unless the import is manual
if %w[all_available all_unavailable].include?(shared_sync_method)
# build new articles
shared_supplier
.shared_articles
.where.not(id: existing_articles.to_a)
.find_each { |new_shared_article| new_articles << new_shared_article.build_new_article(self) }
# make them unavailable when desired
new_articles.each { |new_article| new_article.availability = false } if shared_sync_method == 'all_unavailable'
end
[updated_article_pairs, outlisted_articles, new_articles]
2015-03-27 19:03:21 +01:00
end
# Synchronise articles with spreadsheet.
#
# @param file [File] Spreadsheet file to parse
# @param options [Hash] Options passed to {FoodsoftFile#parse} except when listed here.
# @option options [Boolean] :outlist_absent Set to +true+ to remove articles not in spreadsheet.
# @option options [Boolean] :convert_units Omit or set to +true+ to keep current units, recomputing unit quantity and price.
def sync_from_file(file, options = {})
all_order_numbers = []
updated_article_pairs = []
outlisted_articles = []
new_articles = []
FoodsoftFile.parse file, options do |status, new_attrs, line|
2015-03-27 19:03:21 +01:00
article = articles.undeleted.where(order_number: new_attrs[:order_number]).first
new_attrs[:article_category] = ArticleCategory.find_match(new_attrs[:article_category])
new_attrs[:tax] ||= FoodsoftConfig[:tax_default]
new_article = articles.build(new_attrs)
if status.nil?
if article.nil?
new_articles << new_article
else
unequal_attributes = article.unequal_attributes(new_article, options.slice(:convert_units))
2015-03-27 19:03:21 +01:00
unless unequal_attributes.empty?
article.attributes = unequal_attributes
updated_article_pairs << [article, unequal_attributes]
end
end
elsif status == :outlisted && article.present?
outlisted_articles << article
# stop when there is a parsing error
elsif status.is_a? String
# @todo move I18n key to model
raise I18n.t('articles.model.error_parse', msg: status, line: line.to_s)
2015-03-27 19:03:21 +01:00
end
all_order_numbers << article.order_number if article
end
outlisted_articles += articles.undeleted.where.not(order_number: all_order_numbers + [nil]) if options[:outlist_absent]
[updated_article_pairs, outlisted_articles, new_articles]
end
# default value
def shared_sync_method
return unless shared_supplier
self[:shared_sync_method] || 'import'
2009-01-06 11:49:19 +01:00
end
def deleted?
deleted_at.present?
end
def mark_as_deleted
transaction do
super
2020-06-22 16:31:13 +02:00
update_column :iban, nil
articles.each(&:mark_as_deleted)
end
end
2015-09-23 22:38:20 +02:00
# @return [Boolean] Whether there are articles that would use tolerance (unit_quantity > 1)
def has_tolerance?
articles.where('articles.unit_quantity > 1').any?
end
protected
# make sure the shared_sync_method is allowed for the shared supplier
def valid_shared_sync_method
return unless shared_supplier && !shared_supplier.shared_sync_methods.include?(shared_sync_method)
errors.add :shared_sync_method, :included
end
# Make sure, the name is uniq, add usefull message if uniq group is already deleted
def uniqueness_of_name
supplier = Supplier.where(name: name)
supplier = supplier.where.not(id: id) unless new_record?
return unless supplier.exists?
message = supplier.first.deleted? ? :taken_with_deleted : :taken
errors.add :name, message
end
2009-01-06 11:49:19 +01:00
end