replace skeleton files by previous version; apply obvious fixes
This commit is contained in:
parent
1f5cbcd6d1
commit
2133be2436
35 changed files with 1086 additions and 39 deletions
|
@ -1,3 +0,0 @@
|
|||
class ApplicationRecord < ActiveRecord::Base
|
||||
self.abstract_class = true
|
||||
end
|
62
app/models/article.rb
Normal file
62
app/models/article.rb
Normal file
|
@ -0,0 +1,62 @@
|
|||
# == Schema Information
|
||||
#
|
||||
# Table name: articles
|
||||
#
|
||||
# id :integer(4) not null, primary key
|
||||
# name :string(255) not null
|
||||
# supplier_id :integer(4) not null
|
||||
# number :string(255)
|
||||
# note :string(255)
|
||||
# manufacturer :string(255)
|
||||
# origin :string(255)
|
||||
# unit :string(255)
|
||||
# price :decimal(8, 2) default(0.0), not null
|
||||
# tax :decimal(3, 1) default(7.0), not null
|
||||
# deposit :decimal(8, 2) default(0.0), not null
|
||||
# unit_quantity :decimal(4, 1) default(1.0), not null
|
||||
# scale_quantity :decimal(4, 2)
|
||||
# scale_price :decimal(8, 2)
|
||||
# created_on :datetime
|
||||
# updated_on :datetime
|
||||
# list :string(255)
|
||||
# category :string(255)
|
||||
#
|
||||
|
||||
class Article < ActiveRecord::Base
|
||||
belongs_to :supplier
|
||||
|
||||
validates_numericality_of :price, :tax, :deposit, :unit_quantity
|
||||
validates_uniqueness_of :number, :scope => :supplier_id
|
||||
validates_presence_of :name, :price
|
||||
|
||||
|
||||
# Custom attribute setter that accepts decimal numbers using localized decimal separator.
|
||||
def price=(price)
|
||||
self[:price] = delocalizeDecimalString(price)
|
||||
end
|
||||
def unit_quantity=(unit_quantity)
|
||||
self[:unit_quantity] = delocalizeDecimalString(unit_quantity)
|
||||
end
|
||||
def tax=(tax)
|
||||
self[:tax] = delocalizeDecimalString(tax)
|
||||
end
|
||||
def deposit=(deposit)
|
||||
self[:deposit] = delocalizeDecimalString(deposit)
|
||||
end
|
||||
def scale_quantity=(scale_quantity)
|
||||
self[:scale_quantity] = delocalizeDecimalString(scale_quantity)
|
||||
end
|
||||
def scale_price=(scale_price)
|
||||
self[:scale_price] = delocalizeDecimalString(scale_price)
|
||||
end
|
||||
|
||||
def delocalizeDecimalString(string)
|
||||
if (string && string.is_a?(String) && !string.empty?)
|
||||
separator = ","
|
||||
if (separator != '.' && string.index(separator))
|
||||
string = string.sub(separator, '.')
|
||||
end
|
||||
end
|
||||
return string
|
||||
end
|
||||
end
|
146
app/models/supplier.rb
Normal file
146
app/models/supplier.rb
Normal file
|
@ -0,0 +1,146 @@
|
|||
class Supplier < ActiveRecord::Base
|
||||
has_many :articles, :dependent => :destroy
|
||||
has_many :user_accesses, :dependent => :destroy
|
||||
has_many :users, :through => :user_accesses
|
||||
|
||||
# save lists in an array in database
|
||||
serialize :lists
|
||||
|
||||
FTP_TYPES = ['bnn', 'foodsoft'].freeze
|
||||
EMAIL_RE = /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i.freeze
|
||||
|
||||
validates :name, :address, :phone, presence: true
|
||||
validates :ftp_host, :ftp_user, :ftp_password, :ftp_sync, presence: true, if: :ftp_sync?
|
||||
validates :ftp_type, presence: true, inclusion: { in: FTP_TYPES }, if: :ftp_sync?
|
||||
validates :mail_from, presence: true, format: { with: EMAIL_RE }, if: :mail_sync?
|
||||
validates :mail_type, inclusion: { in: ArticleImport.file_formats.keys }, presence: true, if: :mail_sync?
|
||||
|
||||
scope :ftp_sync, ->{ where(ftp_sync: true) }
|
||||
scope :mail_sync, ->{ where(mail_sync: true) }
|
||||
|
||||
before_create :create_salt
|
||||
|
||||
def ftp_path
|
||||
# path should read "ftp_files", but has "bnn_files" for historical reasons
|
||||
Rails.root.join("supplier_assets", "bnn_files", id.to_s)
|
||||
end
|
||||
|
||||
def mail_path
|
||||
Rails.root.join("supplier_assets", "mail_files", id.to_s)
|
||||
end
|
||||
|
||||
# mail hash checked on receiving articles update mail
|
||||
def articles_mail_hash
|
||||
digest = Digest::SHA1.new
|
||||
digest.update self.id.to_s
|
||||
digest.update ":"
|
||||
digest.update salt
|
||||
digest.update ":mail:articles"
|
||||
Base32.encode(digest.digest[1..10])
|
||||
end
|
||||
|
||||
def articles_mail_address
|
||||
return unless salt.present?
|
||||
"#{ENV["MAILER_PREFIX"]}#{id}.#{articles_mail_hash}@#{ENV["MAILER_DOMAIN"]}"
|
||||
end
|
||||
|
||||
def sync_ftp_files
|
||||
new_files = FtpSync::sync(self)
|
||||
|
||||
unless new_files.empty?
|
||||
logger.info "New FTP-synced files for #{name}: #{new_files.inspect}"
|
||||
|
||||
new_files.each do |file|
|
||||
logger.debug "parse #{file}..."
|
||||
outlisted_counter, new_counter, updated_counter, invalid_articles =
|
||||
update_articles_from_file(File.join(ftp_path, file), type: ftp_type)
|
||||
logger.info "#{file} successfully parsed: #{new_counter} new, #{updated_counter} updated, #{outlisted_counter} outlisted, #{invalid_articles.size} invalid"
|
||||
end
|
||||
|
||||
if $missing_bnn_codes
|
||||
logger.info "missing bnn-codes: #{$missing_bnn_codes.uniq.sort.join(", ")}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# parses file and updates articles
|
||||
#
|
||||
# @param file [File] File to update articles from
|
||||
# @returns [Array] counters for outlisted, new and updated articles, and invalid articles
|
||||
# @note options are passed on to {ArticleImport.parse}
|
||||
def update_articles_from_file(file, **opts)
|
||||
|
||||
specials = invalid_articles = Array.new
|
||||
outlisted_counter, new_counter, updated_counter = 0, 0, 0
|
||||
listed = Array.new
|
||||
upload_lists = Hash.new(0)
|
||||
|
||||
ArticleImport.parse(file, **opts) do |parsed_article, status|
|
||||
parsed_article[:upload_list] = opts[:upload_list] if opts[:upload_list]
|
||||
upload_lists[parsed_article[:upload_list]] += 1 if parsed_article[:upload_list]
|
||||
|
||||
article = articles.find_by_number(parsed_article[:number])
|
||||
# create new article
|
||||
if status.nil? && article.nil?
|
||||
new_article = articles.build(parsed_article)
|
||||
if new_article.valid? && new_article.save
|
||||
new_counter += 1
|
||||
listed << new_article.id
|
||||
else
|
||||
invalid_articles << new_article
|
||||
end
|
||||
|
||||
# update existing article
|
||||
elsif status.nil? && article
|
||||
updated_counter += 1 if article.update_attributes(parsed_article)
|
||||
listed << article.id
|
||||
|
||||
# delete outlisted article
|
||||
elsif status == :outlisted && article
|
||||
article.destroy && outlisted_counter += 1
|
||||
|
||||
# remember special info for article; store data to allow article after its special
|
||||
elsif status == :special && article
|
||||
specials << article
|
||||
|
||||
# mention parsing problems
|
||||
elsif status.is_a?(String)
|
||||
new_article = articles.build(parsed_article)
|
||||
new_article.valid?
|
||||
new_article.errors[''] = status
|
||||
invalid_articles << new_article
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
# updates articles with special infos
|
||||
specials.each do |special|
|
||||
if article = articles.find_by_number(special[:number])
|
||||
if article.note
|
||||
article.note += " | #{special[:note]}"
|
||||
else
|
||||
article.note = special[:note]
|
||||
end
|
||||
article.save
|
||||
end
|
||||
end
|
||||
|
||||
# remove unlisted articles when requested
|
||||
if opts[:outlist_unlisted] || ArticleImport.file_formats[opts[:type]]::OUTLIST
|
||||
to_delete = articles.where('id NOT IN (?)', listed) # @rails4 `.where.not()`
|
||||
outlisted_counter += to_delete.delete_all
|
||||
end
|
||||
|
||||
return [outlisted_counter, new_counter, updated_counter, invalid_articles]
|
||||
end
|
||||
|
||||
def articles_updated_at
|
||||
articles.order('articles.updated_on DESC').first.try(:updated_on)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def create_salt
|
||||
self.salt = [Array.new(6){rand(256).chr}.join].pack("m").chomp
|
||||
end
|
||||
end
|
40
app/models/user.rb
Normal file
40
app/models/user.rb
Normal file
|
@ -0,0 +1,40 @@
|
|||
class User < ActiveRecord::Base
|
||||
|
||||
has_many :user_accesses, :dependent => :destroy
|
||||
has_many :suppliers, :through => :user_accesses
|
||||
|
||||
attr_accessible :email, :password, :password_confirmation
|
||||
|
||||
attr_accessor :password
|
||||
before_save :encrypt_password
|
||||
|
||||
validates_confirmation_of :password
|
||||
validates_presence_of :password, :on => :create
|
||||
validates_presence_of :email
|
||||
validates_uniqueness_of :email
|
||||
|
||||
def self.authenticate(email, password)
|
||||
user = find_by_email(email)
|
||||
if user && user.password_hash == BCrypt::Engine.hash_secret(password, user.password_salt)
|
||||
user
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def encrypt_password
|
||||
if password.present?
|
||||
self.password_salt = BCrypt::Engine.generate_salt
|
||||
self.password_hash = BCrypt::Engine.hash_secret(password, password_salt)
|
||||
end
|
||||
end
|
||||
|
||||
def has_access_to?(supplier)
|
||||
admin? or !UserAccess.first(:conditions => {:supplier_id => supplier.id, :user_id => id}).nil?
|
||||
end
|
||||
|
||||
def admin?
|
||||
!!admin
|
||||
end
|
||||
|
||||
end
|
4
app/models/user_access.rb
Normal file
4
app/models/user_access.rb
Normal file
|
@ -0,0 +1,4 @@
|
|||
class UserAccess < ActiveRecord::Base
|
||||
belongs_to :user
|
||||
belongs_to :supplier
|
||||
end
|
Loading…
Add table
Add a link
Reference in a new issue