vyapari 0.1.4 → 0.1.5dev
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/assets/images/vyapari/sample_stock_bundle.csv +1 -0
- data/app/controllers/vyapari/admin/base_controller.rb +10 -0
- data/app/controllers/vyapari/admin/brands_controller.rb +99 -0
- data/app/controllers/vyapari/admin/categories_controller.rb +115 -0
- data/app/controllers/vyapari/admin/countries_controller.rb +2 -2
- data/app/controllers/vyapari/admin/dashboard_controller.rb +3 -1
- data/app/controllers/vyapari/admin/exchange_rates_controller.rb +2 -2
- data/app/controllers/vyapari/admin/products_controller.rb +79 -0
- data/app/controllers/vyapari/admin/regions_controller.rb +1 -1
- data/app/controllers/vyapari/admin/resource_controller.rb +1 -0
- data/app/controllers/vyapari/admin/stores_controller.rb +67 -0
- data/app/controllers/vyapari/admin/suppliers_controller.rb +67 -0
- data/app/controllers/vyapari/admin/terminals_controller.rb +150 -0
- data/app/controllers/vyapari/application_controller.rb +12 -0
- data/app/controllers/vyapari/store_manager/base_controller.rb +28 -0
- data/app/controllers/vyapari/store_manager/dashboard_controller.rb +30 -0
- data/app/controllers/vyapari/store_manager/resource_controller.rb +17 -0
- data/app/controllers/vyapari/store_manager/stock_bundles_controller.rb +206 -0
- data/app/controllers/vyapari/store_manager/stock_entries_controller.rb +161 -0
- data/app/controllers/vyapari/terminal_staff/base_controller.rb +29 -0
- data/app/controllers/vyapari/terminal_staff/dashboard_controller.rb +29 -0
- data/app/controllers/vyapari/terminal_staff/invoices_controller.rb +138 -0
- data/app/controllers/vyapari/terminal_staff/line_items_controller.rb +123 -0
- data/app/controllers/vyapari/terminal_staff/resource_controller.rb +17 -0
- data/app/controllers/vyapari/user_dashboard_controller.rb +31 -0
- data/app/models/brand.rb +63 -10
- data/app/models/category.rb +60 -16
- data/app/models/country.rb +27 -5
- data/app/models/exchange_rate.rb +31 -6
- data/app/models/image/brand_image.rb +3 -0
- data/app/models/image/category_image.rb +3 -0
- data/app/models/image/product_image.rb +3 -0
- data/app/models/invoice.rb +223 -0
- data/app/models/line_item.rb +206 -0
- data/app/models/product.rb +67 -13
- data/app/models/region.rb +26 -3
- data/app/models/stock_bundle.rb +249 -0
- data/app/models/stock_entry.rb +271 -0
- data/app/models/store.rb +216 -0
- data/app/models/supplier.rb +85 -0
- data/app/models/terminal.rb +158 -0
- data/app/models/vyapari/application_record.rb +4 -0
- data/app/uploaders/brand_image_uploader.rb +14 -0
- data/app/uploaders/category_image_uploader.rb +14 -0
- data/app/uploaders/product_image_uploader.rb +14 -0
- data/app/uploaders/stock_bundle_uploader.rb +10 -0
- data/app/views/layouts/kuppayam/_footer.html.erb +1 -1
- data/app/views/layouts/kuppayam/_sidebar.html.erb +45 -44
- data/app/views/layouts/vyapari/_store_manager_menu.html.erb +107 -0
- data/app/views/layouts/vyapari/_terminal_staff_menu.html.erb +103 -0
- data/app/views/layouts/vyapari/store_manager.html.erb +112 -0
- data/app/views/layouts/vyapari/terminal_staff.html.erb +116 -0
- data/app/views/vyapari/admin/brands/_form.html.erb +23 -0
- data/app/views/vyapari/admin/brands/_index.html.erb +89 -0
- data/app/views/vyapari/admin/brands/_row.html.erb +63 -0
- data/app/views/vyapari/admin/brands/_show.html.erb +104 -0
- data/app/views/vyapari/admin/brands/index.html.erb +48 -0
- data/app/views/vyapari/admin/categories/_form.html.erb +28 -0
- data/app/views/vyapari/admin/categories/_index.html.erb +101 -0
- data/app/views/vyapari/admin/categories/_row.html.erb +69 -0
- data/app/views/vyapari/admin/categories/_show.html.erb +115 -0
- data/app/views/vyapari/admin/{users → categories}/index.html.erb +8 -24
- data/app/views/vyapari/admin/countries/_index.html.erb +1 -1
- data/app/views/vyapari/admin/countries/index.html.erb +19 -3
- data/app/views/vyapari/admin/exchange_rates/_form.html.erb +3 -4
- data/app/views/vyapari/admin/exchange_rates/_index.html.erb +7 -7
- data/app/views/vyapari/admin/exchange_rates/_row.html.erb +3 -3
- data/app/views/vyapari/admin/exchange_rates/_show.html.erb +1 -0
- data/app/views/vyapari/admin/exchange_rates/index.html.erb +19 -3
- data/app/views/vyapari/admin/products/_form.html.erb +32 -0
- data/app/views/vyapari/admin/products/_index.html.erb +58 -0
- data/app/views/vyapari/admin/products/_row.html.erb +30 -0
- data/app/views/vyapari/admin/products/_show.html.erb +81 -0
- data/app/views/vyapari/admin/products/index.html.erb +48 -0
- data/app/views/vyapari/admin/regions/_index.html.erb +1 -1
- data/app/views/vyapari/admin/regions/index.html.erb +19 -3
- data/app/views/vyapari/admin/stores/_form.html.erb +37 -0
- data/app/views/vyapari/admin/stores/_index.html.erb +84 -0
- data/app/views/vyapari/admin/stores/_row.html.erb +55 -0
- data/app/views/vyapari/admin/stores/_show.html.erb +110 -0
- data/app/views/vyapari/admin/stores/index.html.erb +48 -0
- data/app/views/vyapari/admin/suppliers/_form.html.erb +32 -0
- data/app/views/vyapari/admin/suppliers/_index.html.erb +58 -0
- data/app/views/vyapari/admin/suppliers/_row.html.erb +30 -0
- data/app/views/vyapari/admin/suppliers/_show.html.erb +81 -0
- data/app/views/vyapari/admin/suppliers/index.html.erb +48 -0
- data/app/views/vyapari/admin/terminals/_form.html.erb +24 -0
- data/app/views/vyapari/admin/terminals/_index.html.erb +75 -0
- data/app/views/vyapari/admin/terminals/_row.html.erb +49 -0
- data/app/views/vyapari/admin/terminals/_show.html.erb +74 -0
- data/app/views/vyapari/store_manager/dashboard/index.html.erb +93 -0
- data/app/views/vyapari/store_manager/stock_bundles/_form.html.erb +99 -0
- data/app/views/vyapari/store_manager/stock_bundles/_index.html.erb +74 -0
- data/app/views/vyapari/store_manager/stock_bundles/_row.html.erb +44 -0
- data/app/views/vyapari/store_manager/stock_bundles/_show.html.erb +110 -0
- data/app/views/vyapari/store_manager/stock_bundles/create.html.erb +57 -0
- data/app/views/vyapari/store_manager/stock_bundles/index.html.erb +36 -0
- data/app/views/vyapari/store_manager/stock_bundles/update.html.erb +57 -0
- data/app/views/vyapari/store_manager/stock_entries/_form.html.erb +50 -0
- data/app/views/vyapari/store_manager/stock_entries/_index.html.erb +80 -0
- data/app/views/vyapari/store_manager/stock_entries/_row.html.erb +33 -0
- data/app/views/vyapari/store_manager/stock_entries/_show.html.erb +110 -0
- data/app/views/vyapari/store_manager/stock_entries/_stock_bundle.html.erb +61 -0
- data/app/views/vyapari/store_manager/stock_entries/index.html.erb +45 -0
- data/app/views/vyapari/terminal_staff/dashboard/_counts.html.erb +88 -0
- data/app/views/vyapari/terminal_staff/dashboard/_invoices.html.erb +37 -0
- data/app/views/vyapari/terminal_staff/dashboard/index.html.erb +36 -0
- data/app/views/vyapari/terminal_staff/invoices/_draft.html.erb +62 -0
- data/app/views/vyapari/terminal_staff/invoices/_form.html.erb +120 -0
- data/app/views/vyapari/terminal_staff/invoices/_index.html.erb +64 -0
- data/app/views/vyapari/terminal_staff/invoices/_row.html.erb +33 -0
- data/app/views/vyapari/terminal_staff/invoices/_show.html.erb +171 -0
- data/app/views/vyapari/terminal_staff/invoices/index.html.erb +37 -0
- data/app/views/vyapari/terminal_staff/invoices/new.js.erb +13 -0
- data/app/views/vyapari/terminal_staff/invoices/show.html.erb +1 -0
- data/app/views/vyapari/terminal_staff/line_items/_form.html.erb +40 -0
- data/app/views/vyapari/terminal_staff/line_items/_index.html.erb +94 -0
- data/app/views/vyapari/terminal_staff/line_items/create.js.erb +34 -0
- data/app/views/vyapari/terminal_staff/line_items/destroy.js.erb +25 -0
- data/app/views/vyapari/terminal_staff/stores/index.html.erb +24 -0
- data/app/views/vyapari/user_dashboard/index.html.erb +51 -0
- data/config/routes.rb +62 -5
- data/db/import_data/brands.csv +7 -0
- data/db/import_data/categories.csv +12 -0
- data/db/import_data/countries.csv +1 -0
- data/db/import_data/dummy/brands.csv +7 -0
- data/db/import_data/dummy/categories.csv +12 -0
- data/db/import_data/dummy/countries.csv +1 -0
- data/db/import_data/dummy/exchange_rates.csv +5 -0
- data/db/import_data/dummy/products-copy.csv +1 -0
- data/db/import_data/dummy/products.csv +1 -0
- data/db/import_data/dummy/products.xlsx +0 -0
- data/db/import_data/dummy/regions.csv +13 -0
- data/db/import_data/dummy/stores.csv +10 -0
- data/db/import_data/dummy/suppliers.csv +14 -0
- data/db/import_data/dummy/terminals.csv +11 -0
- data/db/import_data/exchange_rates.csv +5 -0
- data/db/import_data/regions.csv +13 -0
- data/db/import_data/stores.csv +3 -0
- data/db/import_data/suppliers.csv +14 -0
- data/db/import_data/terminals.csv +3 -0
- data/db/migrate/20170000000200_create_exchange_rates.rb +3 -2
- data/db/migrate/20170000000203_create_contacts.rb +22 -0
- data/db/migrate/20170000000204_create_bank_accounts.rb +21 -0
- data/db/migrate/20170000000205_create_suppliers.rb +18 -0
- data/db/migrate/20170000000206_create_stores.rb +21 -0
- data/db/migrate/20170000000207_create_terminals.rb +18 -0
- data/db/migrate/20170000000210_create_brands.rb +14 -0
- data/db/migrate/20170000000211_create_categories.rb +22 -0
- data/db/migrate/20170000000212_create_products.rb +29 -0
- data/db/migrate/20170000000213_create_invoices.rb +58 -0
- data/db/migrate/20170000000215_create_stock_bundles.rb +17 -0
- data/db/migrate/20170000000216_create_stock_entries.rb +20 -0
- data/db/sample_reports/products.xlsx +0 -0
- data/lib/tasks/vyapari/all.rake +73 -0
- data/lib/vyapari/version.rb +1 -1
- metadata +150 -15
- data/app/controllers/vyapari/admin/users_controller.rb +0 -130
- data/app/views/vyapari/admin/users/_form.html.erb +0 -39
- data/app/views/vyapari/admin/users/_index.html.erb +0 -101
- data/app/views/vyapari/admin/users/_row.html.erb +0 -72
- data/app/views/vyapari/admin/users/_show.html.erb +0 -199
- data/lib/tasks/vyapari_tasks.rake +0 -4
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
require 'csv'
|
|
2
|
+
|
|
3
|
+
class StockBundle < ActiveRecord::Base
|
|
4
|
+
|
|
5
|
+
# Constants
|
|
6
|
+
PENDING = "pending" # Default Status
|
|
7
|
+
ERRORED = "errored"
|
|
8
|
+
APPROVED = "approved"
|
|
9
|
+
|
|
10
|
+
STATUS_HASH = {"Pending" => PENDING, "Approved" => APPROVED, "Errored" => ERRORED}
|
|
11
|
+
STATUS_HASH_REVERSE = {PENDING => "Pending", APPROVED => "Approved", ERRORED => "Errored"}
|
|
12
|
+
|
|
13
|
+
# Validations
|
|
14
|
+
validates :name, :presence=> true, uniqueness: true
|
|
15
|
+
validates :uploaded_date, presence: true
|
|
16
|
+
validates :status, :presence=> true, :inclusion => {:in => STATUS_HASH_REVERSE.keys, :presence_of => :status, :message => "%{value} is not a valid status" }
|
|
17
|
+
# validates :file, :presence=> true
|
|
18
|
+
|
|
19
|
+
# Associations
|
|
20
|
+
has_many :stock_entries
|
|
21
|
+
belongs_to :uploader, class_name: 'User'
|
|
22
|
+
belongs_to :supplier, optional: true
|
|
23
|
+
belongs_to :store
|
|
24
|
+
|
|
25
|
+
# Uploader
|
|
26
|
+
mount_uploader :file, File::StockBundleUploader
|
|
27
|
+
mount_uploader :error_file, File::StockBundleUploader
|
|
28
|
+
|
|
29
|
+
# ------------------
|
|
30
|
+
# Class Methods
|
|
31
|
+
# ------------------
|
|
32
|
+
|
|
33
|
+
# return an active record relation object with the search query in its where clause
|
|
34
|
+
# Return the ActiveRecord::Relation object
|
|
35
|
+
# == Examples
|
|
36
|
+
# >>> obj.search(query)
|
|
37
|
+
# => ActiveRecord::Relation object
|
|
38
|
+
scope :search, lambda {|query| joins(:supplier, :store).where("LOWER(suppliers.name) LIKE LOWER('%#{query}%') OR LOWER(stores.name) LIKE LOWER('%#{query}%')")}
|
|
39
|
+
|
|
40
|
+
scope :status, lambda { |status| where ("LOWER(stock_bundles.status)='#{status}'") }
|
|
41
|
+
|
|
42
|
+
scope :pending, -> { where(status: PENDING) }
|
|
43
|
+
scope :approved, -> { where(status: APPROVED) }
|
|
44
|
+
|
|
45
|
+
# ------------------
|
|
46
|
+
# Class Methods
|
|
47
|
+
# ------------------
|
|
48
|
+
|
|
49
|
+
def parse_stocks
|
|
50
|
+
|
|
51
|
+
# For some reasn it throws error for self.error_details unless we reload
|
|
52
|
+
self.reload
|
|
53
|
+
|
|
54
|
+
path = "#{Rails.root}/public#{self.file.url}"
|
|
55
|
+
begin
|
|
56
|
+
csv_table = CSV.table(path, {headers: true, converters: nil, header_converters: :symbol})
|
|
57
|
+
rescue CSV::MalformedCSVError => e
|
|
58
|
+
self.error_summary = "The Uploaded File is corrupted."
|
|
59
|
+
self.error_details = "#{e.class}: #{e.message}"
|
|
60
|
+
self.save
|
|
61
|
+
puts self.error_summary.red
|
|
62
|
+
puts self.error_details.red
|
|
63
|
+
return false
|
|
64
|
+
rescue Exception => e
|
|
65
|
+
self.error_summary = "The Uploaded File format is not supported."
|
|
66
|
+
self.error_details = "#{e.class}: #{e.message}"
|
|
67
|
+
puts self.error_summary.red
|
|
68
|
+
puts self.error_details.red
|
|
69
|
+
self.save
|
|
70
|
+
return false
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
headers = csv_table.headers
|
|
74
|
+
|
|
75
|
+
StockEntry.where(stock_bundle: self.id).destroy_all
|
|
76
|
+
|
|
77
|
+
# We need a collection of all the column headings to pass to error hander to reproduce the errors in same format
|
|
78
|
+
columns = [:env_sku, :name, :reference_number, :one_liner, :description, :purchased_price, :landed_price, :selling_price, :retail_price, :brand, :category, :quantity]
|
|
79
|
+
|
|
80
|
+
# Initializing the Data Error to store errors for each column
|
|
81
|
+
data_error = Kuppayam::Importer::DataError.new
|
|
82
|
+
data_error.columns = columns
|
|
83
|
+
|
|
84
|
+
csv_table.each_with_index do |row, i|
|
|
85
|
+
|
|
86
|
+
row.headers.each{ |cell| row[cell] = row[cell].to_s.strip }
|
|
87
|
+
|
|
88
|
+
if row[:ean_sku].blank?
|
|
89
|
+
data_error.add_column_error(:ean_sku, "", "ENV / SKU number is blank", i)
|
|
90
|
+
next
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
if row[:quantity].blank?
|
|
94
|
+
data_error.add_column_error(:quantity, "", "Quantity is blank", i)
|
|
95
|
+
next
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
product = Product.where("ean_sku = ?", row[:ean_sku]).first || Product.new
|
|
99
|
+
|
|
100
|
+
product.ean_sku = row[:ean_sku]
|
|
101
|
+
product.name = row[:name]
|
|
102
|
+
product.reference_number = row[:reference_number]
|
|
103
|
+
product.one_liner = row[:one_liner]
|
|
104
|
+
product.description = row[:description]
|
|
105
|
+
|
|
106
|
+
product.purchased_price = row[:purchased_price]
|
|
107
|
+
product.landed_price = row[:landed_price]
|
|
108
|
+
product.selling_price = row[:selling_price]
|
|
109
|
+
product.retail_price = row[:retail_price]
|
|
110
|
+
|
|
111
|
+
product.purchased_price = 0.00 if product.purchased_price.blank?
|
|
112
|
+
product.landed_price = 0.00 if product.landed_price.blank?
|
|
113
|
+
product.selling_price = 0.00 if product.selling_price.blank?
|
|
114
|
+
product.retail_price = 0.00 if product.retail_price.blank?
|
|
115
|
+
|
|
116
|
+
product.brand = Brand.where("name = ?", row[:brand]).first || product.build_brand(name: row[:brand]) if row[:brand]
|
|
117
|
+
product.category = Category.where("name = ?", row[:category]).first || product.build_category(name: row[:category]) if row[:category]
|
|
118
|
+
|
|
119
|
+
stock_entry = StockEntry.new()
|
|
120
|
+
|
|
121
|
+
stock_entry.store = self.store
|
|
122
|
+
stock_entry.product = product
|
|
123
|
+
stock_entry.supplier = self.supplier
|
|
124
|
+
stock_entry.quantity = row[:quantity]
|
|
125
|
+
stock_entry.stock_bundle = self
|
|
126
|
+
stock_entry.status = :pending
|
|
127
|
+
|
|
128
|
+
if product.valid? && stock_entry.valid?
|
|
129
|
+
product.save!
|
|
130
|
+
stock_entry.save!
|
|
131
|
+
else
|
|
132
|
+
unless product.valid?
|
|
133
|
+
columns.each do |col_name|
|
|
134
|
+
data_error.add_column_error(col_name, row[col_name], product.errors[col_name], i) if product.errors.has_key?(col_name)
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
unless stock_entry.valid?
|
|
138
|
+
columns.each do |col_name|
|
|
139
|
+
data_error.add_column_error(col_name, row[col_name], stock_entry.errors[col_name], i) if stock_entry.errors.has_key?(col_name)
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
if data_error.errors.any?
|
|
147
|
+
self.error_summary = "There are few errors in some of the rows in the CSV file. Open the error file to know more."
|
|
148
|
+
# self.error_file = data_error.generate_error_file
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
self.save!
|
|
152
|
+
self.stock_entries.update_all(status: :active) if data_error.errors.blank?
|
|
153
|
+
|
|
154
|
+
return data_error.errors.blank?
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# ------------------
|
|
158
|
+
# Instance Methods
|
|
159
|
+
# ------------------
|
|
160
|
+
|
|
161
|
+
def display_name
|
|
162
|
+
"Stock Bundle - #{self.id} from #{supplier.try(:name)}"
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def display_status
|
|
166
|
+
STATUS_HASH_REVERSE[self.status]
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def display_file_name
|
|
170
|
+
if self.file && self.file.file
|
|
171
|
+
self.file.file.filename
|
|
172
|
+
else
|
|
173
|
+
self.display_name
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def original_file_path
|
|
178
|
+
self.file && self.file.file ? self.file.file.path : nil
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def original_error_file_path
|
|
182
|
+
self.error_file && self.error_file.file ? self.error_file.file.path : nil
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# * Return true if the stock_entry is approved, else false.
|
|
186
|
+
# == Examples
|
|
187
|
+
# >>> stock_entry.pending?
|
|
188
|
+
# => true
|
|
189
|
+
def pending?
|
|
190
|
+
(status == PENDING)
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# change the status of the stock_entry to :pending
|
|
194
|
+
# Return the status
|
|
195
|
+
# == Examples
|
|
196
|
+
# >>> stock_entry.mark_as_pending!
|
|
197
|
+
# => "pending"
|
|
198
|
+
def mark_as_pending!
|
|
199
|
+
self.update_attribute(:status, PENDING)
|
|
200
|
+
self.stock_entries.update_all(status: StockEntry::PENDING)
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
# * Return true if the stock_entry is approved, else false.
|
|
204
|
+
# == Examples
|
|
205
|
+
# >>> stock_entry.approved?
|
|
206
|
+
# => true
|
|
207
|
+
def approved?
|
|
208
|
+
(status == APPROVED)
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# change the status to :approved
|
|
212
|
+
# Return the status
|
|
213
|
+
# == Examples
|
|
214
|
+
# >>> stock_entry.approve!
|
|
215
|
+
# => "approve"
|
|
216
|
+
def approve!
|
|
217
|
+
self.update_attribute(:status, APPROVED)
|
|
218
|
+
self.stock_entries.update_all(status: StockEntry::ACTIVE)
|
|
219
|
+
true
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
# * Return true if the stock_entry is errored, else false.
|
|
223
|
+
# == Examples
|
|
224
|
+
# >>> stock_entry.mark_as_errored?
|
|
225
|
+
# => true
|
|
226
|
+
def mark_as_errored?
|
|
227
|
+
(status == ERRORED)
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
# change the status to :errored
|
|
231
|
+
# Return the status
|
|
232
|
+
# == Examples
|
|
233
|
+
# >>> stock_entry.mark_as_errored!
|
|
234
|
+
# => "approve"
|
|
235
|
+
def mark_as_errored!
|
|
236
|
+
self.update_attribute(:status, ERRORED)
|
|
237
|
+
self.stock_entries.update_all(status: StockEntry::PENDING)
|
|
238
|
+
true
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def can_be_deleted?
|
|
242
|
+
true
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
def can_be_edited?
|
|
246
|
+
true
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
end
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
class StockEntry < Vyapari::ApplicationRecord
|
|
2
|
+
|
|
3
|
+
# Constants
|
|
4
|
+
ACTIVE = "active"
|
|
5
|
+
SOLD = "sold"
|
|
6
|
+
DAMAGED = "damaged"
|
|
7
|
+
RECEIVED = "received"
|
|
8
|
+
RETURNED = "returned"
|
|
9
|
+
RESERVED = "reserved"
|
|
10
|
+
PENDING = "pending"
|
|
11
|
+
|
|
12
|
+
STATUS_HASH = {"Active" => ACTIVE, "Sold" => SOLD, "Damaged" => DAMAGED, "Received" => RECEIVED, "Reserved" => RESERVED, "Pending" => PENDING, "Returned" => RETURNED}
|
|
13
|
+
STATUS_HASH_REVERSE = {ACTIVE => "Active", SOLD => "Sold", DAMAGED => "Damaged", RECEIVED => "Received", RESERVED => "Reserved", PENDING => "Pending", RETURNED => "Returned"}
|
|
14
|
+
|
|
15
|
+
# Validations
|
|
16
|
+
validates :store, presence: true
|
|
17
|
+
validates :product, presence: true
|
|
18
|
+
validates :quantity, presence: true, numericality: true
|
|
19
|
+
validates :status, :presence=> true, :inclusion => {:in => STATUS_HASH_REVERSE.keys, :presence_of => :status, :message => "%{value} is not a valid status" }
|
|
20
|
+
|
|
21
|
+
# Associations
|
|
22
|
+
belongs_to :store
|
|
23
|
+
belongs_to :product
|
|
24
|
+
belongs_to :supplier, optional: true
|
|
25
|
+
belongs_to :stock_bundle, optional: true
|
|
26
|
+
belongs_to :invoice, optional: true
|
|
27
|
+
|
|
28
|
+
# ------------------
|
|
29
|
+
# Class Methods
|
|
30
|
+
# ------------------
|
|
31
|
+
|
|
32
|
+
# return an active record relation object with the search query in its where clause
|
|
33
|
+
# Return the ActiveRecord::Relation object
|
|
34
|
+
# == Examples
|
|
35
|
+
# >>> obj.search(query)
|
|
36
|
+
# => ActiveRecord::Relation object
|
|
37
|
+
scope :search, lambda {|query| joins("LEFT JOIN `stores` ON `stores`.`id` = `stock_entries`.`store_id` LEFT JOIN `products` ON `products`.`id` = `stock_entries`.`product_id` LEFT JOIN `suppliers` ON `suppliers`.`id` = `stock_entries`.`supplier_id`").where("LOWER(products.name) LIKE LOWER('%#{query}%') OR
|
|
38
|
+
LOWER(suppliers.name) LIKE LOWER('%#{query}%') OR
|
|
39
|
+
LOWER(products.ean_sku) LIKE LOWER('%#{query}%') OR
|
|
40
|
+
LOWER(products.one_liner) LIKE LOWER('%#{query}%') OR
|
|
41
|
+
LOWER(products.description) LIKE LOWER('%#{query}%') OR
|
|
42
|
+
LOWER(stores.name) LIKE LOWER('%#{query}%')")}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
scope :active, -> { where(status: ACTIVE) }
|
|
46
|
+
scope :sold, -> { where(status: SOLD) }
|
|
47
|
+
scope :damaged, -> { where(status: DAMAGED) }
|
|
48
|
+
scope :returned, -> { where(status: RETURNED) }
|
|
49
|
+
scope :received, -> { where(status: RECEIVED) }
|
|
50
|
+
scope :reserved, -> { where(status: RESERVED) }
|
|
51
|
+
scope :in_stock, -> { where(status: [ACTIVE, RECEIVED, RETURNED]) }
|
|
52
|
+
|
|
53
|
+
def self.save_row_data(row)
|
|
54
|
+
|
|
55
|
+
row.headers.each{ |cell| row[cell] = row[cell].to_s.strip }
|
|
56
|
+
|
|
57
|
+
return if row[:ean_sku].blank?
|
|
58
|
+
|
|
59
|
+
# Initializing error hash for displaying all errors altogether
|
|
60
|
+
error_object = Kuppayam::Importer::ErrorHash.new
|
|
61
|
+
|
|
62
|
+
store = Store.find_by_code(row[:stock])
|
|
63
|
+
unless store
|
|
64
|
+
summary = "Store doesn't exist."
|
|
65
|
+
details = "Error! The store with code #{row[:store]} doesn't exists in the database"
|
|
66
|
+
error_object.errors << { summary: summary, details: details }
|
|
67
|
+
return
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
product = Product.find_by_ean_sku(row[:ean_sku]) || Product.new
|
|
71
|
+
|
|
72
|
+
product.name = row[:name]
|
|
73
|
+
product.code = row[:code]
|
|
74
|
+
|
|
75
|
+
if product.valid?
|
|
76
|
+
product.save!
|
|
77
|
+
else
|
|
78
|
+
summary = "Error while saving product: #{product.name}"
|
|
79
|
+
details = "Error! #{product.errors.full_messages.to_sentence}"
|
|
80
|
+
error_object.errors << { summary: summary, details: details }
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
stock_entry.store = store
|
|
85
|
+
stock_entry.product = product
|
|
86
|
+
stock_entry.quantity = row[:quantity]
|
|
87
|
+
|
|
88
|
+
stock_entry.set_stock_entry_type(row[:stock_entry_type])
|
|
89
|
+
|
|
90
|
+
stock_entry.region = Region.find_by_code(row[:region]) || Country.find_by_name(row[:country])
|
|
91
|
+
stock_entry.country = Country.find_by_code(row[:country]) || Country.find_by_name(row[:country])
|
|
92
|
+
|
|
93
|
+
if stock_entry.valid?
|
|
94
|
+
stock_entry.save!
|
|
95
|
+
else
|
|
96
|
+
summary = "Error while saving stock_entry: #{stock_entry.name}"
|
|
97
|
+
details = "Error! #{stock_entry.errors.full_messages.to_sentence}"
|
|
98
|
+
error_object.errors << { summary: summary, details: details }
|
|
99
|
+
end
|
|
100
|
+
return error_object
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# ------------------
|
|
104
|
+
# Instance Methods
|
|
105
|
+
# ------------------
|
|
106
|
+
|
|
107
|
+
def display_name
|
|
108
|
+
"Stock from #{self.supplier.try(:name)}"
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def display_store_type
|
|
112
|
+
STATUS_HASH[self.status]
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def display_status
|
|
116
|
+
STATUS_HASH_REVERSE[self.status]
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def slug
|
|
120
|
+
if self.product
|
|
121
|
+
self.product.name.parameterize
|
|
122
|
+
else
|
|
123
|
+
self.id
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def to_param
|
|
128
|
+
"#{id}-#{slug}"
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# change the status to :active
|
|
132
|
+
# Return the status
|
|
133
|
+
# == Examples
|
|
134
|
+
# >>> stock_entry.activate!
|
|
135
|
+
# => "active"
|
|
136
|
+
def activate!
|
|
137
|
+
self.update_attribute(:status, ACTIVE)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# change the status to :sold
|
|
141
|
+
# Return the status
|
|
142
|
+
# == Examples
|
|
143
|
+
# >>> stock_entry.sell!
|
|
144
|
+
# => "sold"
|
|
145
|
+
def sell!
|
|
146
|
+
self.update_attribute(:status, SOLD)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# change the status to :damaged
|
|
150
|
+
# Return the status
|
|
151
|
+
# == Examples
|
|
152
|
+
# >>> stock_entry.mark_as_damaged!
|
|
153
|
+
# => "damaged"
|
|
154
|
+
def mark_as_damaged!
|
|
155
|
+
self.update_attribute(:status, DAMAGED)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# change the status to :received
|
|
159
|
+
# Return the status
|
|
160
|
+
# == Examples
|
|
161
|
+
# >>> stock_entry.mark_as_received!
|
|
162
|
+
# => "received"
|
|
163
|
+
def mark_as_received!
|
|
164
|
+
self.update_attribute(:status, RECEIVED)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# change the status to :reserved
|
|
168
|
+
# Return the status
|
|
169
|
+
# == Examples
|
|
170
|
+
# >>> stock_entry.reserve!
|
|
171
|
+
# => "reserved"
|
|
172
|
+
def reserve!
|
|
173
|
+
self.update_attribute(:status, RESERVED)
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# change the status to :returned
|
|
177
|
+
# Return the status
|
|
178
|
+
# == Examples
|
|
179
|
+
# >>> stock_entry.reserve!
|
|
180
|
+
# => "returned"
|
|
181
|
+
def return!
|
|
182
|
+
self.update_attribute(:status, RETURN)
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# change the status to :pending
|
|
186
|
+
# Return the status
|
|
187
|
+
# == Examples
|
|
188
|
+
# >>> stock_entry.mark_as_pending!
|
|
189
|
+
# => "pending"
|
|
190
|
+
def mark_as_pending!
|
|
191
|
+
self.update_attribute(:status, PENDING)
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# * Return true if the stock_entry is active, else false.
|
|
195
|
+
# == Examples
|
|
196
|
+
# >>> stock_entry.active?
|
|
197
|
+
# => true
|
|
198
|
+
def active?
|
|
199
|
+
(status == ACTIVE)
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
# * Return true if the stock_entry is sold, else false.
|
|
203
|
+
# == Examples
|
|
204
|
+
# >>> stock_entry.sold?
|
|
205
|
+
# => true
|
|
206
|
+
def sold?
|
|
207
|
+
(status == SOLD)
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# * Return true if the stock_entry is damaged, else false.
|
|
211
|
+
# == Examples
|
|
212
|
+
# >>> stock_entry.damaged?
|
|
213
|
+
# => true
|
|
214
|
+
def damaged?
|
|
215
|
+
(status == DAMAGED)
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
# * Return true if the stock_entry is received, else false.
|
|
219
|
+
# == Examples
|
|
220
|
+
# >>> stock_entry.received?
|
|
221
|
+
# => true
|
|
222
|
+
def received?
|
|
223
|
+
(status == RECEIVED)
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
# * Return true if the stock_entry is returned, else false.
|
|
227
|
+
# == Examples
|
|
228
|
+
# >>> stock_entry.returned?
|
|
229
|
+
# => true
|
|
230
|
+
def returned?
|
|
231
|
+
(status == RETURNED)
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
# * Return true if the stock_entry is reserved, else false.
|
|
235
|
+
# == Examples
|
|
236
|
+
# >>> stock_entry.reserved?
|
|
237
|
+
# => true
|
|
238
|
+
def reserved?
|
|
239
|
+
(status == RESERVED)
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
# * Return true if the stock_entry is pending, else false.
|
|
243
|
+
# == Examples
|
|
244
|
+
# >>> stock_entry.pending?
|
|
245
|
+
# => true
|
|
246
|
+
def pending?
|
|
247
|
+
(status == PENDING)
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
def can_be_edited?
|
|
251
|
+
true
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
def can_be_deleted?
|
|
255
|
+
# if self.products.any?
|
|
256
|
+
# #self.errors.add(:base, DELETE_MESSAGE)
|
|
257
|
+
# return false
|
|
258
|
+
# else
|
|
259
|
+
# return true
|
|
260
|
+
# end
|
|
261
|
+
return true
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
def report_heading
|
|
265
|
+
rh = []
|
|
266
|
+
rh << self.company.try(:name) if self.company.name
|
|
267
|
+
rh << self.display_name
|
|
268
|
+
rh.join(", ")
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
end
|