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,223 @@
|
|
|
1
|
+
class Invoice < Vyapari::ApplicationRecord
|
|
2
|
+
|
|
3
|
+
# Constants
|
|
4
|
+
DRAFT = "draft"
|
|
5
|
+
ACTIVE = "active"
|
|
6
|
+
CANCELLED = "cancelled"
|
|
7
|
+
|
|
8
|
+
CASH = "cash"
|
|
9
|
+
CREDIT = "credit"
|
|
10
|
+
CREDIT_CARD = "credit_card"
|
|
11
|
+
CHEQUE = "cheque"
|
|
12
|
+
|
|
13
|
+
STATUS_HASH = {"Draft" => DRAFT, "Active" => ACTIVE, "Cancelled" => CANCELLED}
|
|
14
|
+
STATUS_HASH_REVERSE = {DRAFT => "Draft", ACTIVE => "Active", CANCELLED => "Cancelled"}
|
|
15
|
+
|
|
16
|
+
# Call backs
|
|
17
|
+
before_save :calculate_total_amount
|
|
18
|
+
|
|
19
|
+
# Validations
|
|
20
|
+
validates :invoice_number, :presence=> true
|
|
21
|
+
validates :invoice_date, :presence=> true
|
|
22
|
+
|
|
23
|
+
validates :status, :presence=> true, :inclusion => {:in => STATUS_HASH_REVERSE.keys, :presence_of => :status, :message => "%{value} is not a valid status" }
|
|
24
|
+
|
|
25
|
+
# Associations
|
|
26
|
+
belongs_to :terminal
|
|
27
|
+
belongs_to :store
|
|
28
|
+
belongs_to :user
|
|
29
|
+
has_many :line_items
|
|
30
|
+
has_many :stock_entries
|
|
31
|
+
|
|
32
|
+
# ------------------
|
|
33
|
+
# Class Methods
|
|
34
|
+
# ------------------
|
|
35
|
+
|
|
36
|
+
# Scopes
|
|
37
|
+
|
|
38
|
+
# return an active record relation object with the search query in its where clause
|
|
39
|
+
# Return the ActiveRecord::Relation object
|
|
40
|
+
# == Examples
|
|
41
|
+
# >>> invoice.search(query)
|
|
42
|
+
# => ActiveRecord::Relation object
|
|
43
|
+
scope :search, lambda { |query| where("LOWER(invoice_number) LIKE LOWER('%#{query}%') OR LOWER(customer_name) LIKE LOWER('%#{query}%') OR LOWER(customer_address) LIKE LOWER('%#{query}%')")
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
scope :status, lambda { |status| where("LOWER(status)='#{status}'") }
|
|
47
|
+
|
|
48
|
+
scope :draft, -> { where(status: DRAFT) }
|
|
49
|
+
scope :active, -> { where(status: ACTIVE) }
|
|
50
|
+
scope :cancelled, -> { where(status: CANCELLED) }
|
|
51
|
+
|
|
52
|
+
scope :this_month, lambda { where("created_at >= ? AND created_at <= ?", Time.zone.now.beginning_of_month, Time.zone.now.end_of_month) }
|
|
53
|
+
|
|
54
|
+
# ------------------
|
|
55
|
+
# Instance Methods
|
|
56
|
+
# ------------------
|
|
57
|
+
|
|
58
|
+
def initialize
|
|
59
|
+
super
|
|
60
|
+
self.discount = 0.00
|
|
61
|
+
self.adjustment = 0.00
|
|
62
|
+
self.money_taken = 0.00
|
|
63
|
+
self.total_amount = 0.00
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def display_name
|
|
67
|
+
self.invoice_number
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def display_status
|
|
71
|
+
STATUS_HASH_REVERSE[self.status]
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def display_payment_method
|
|
75
|
+
self.payment_method.titleize
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def slug
|
|
79
|
+
self.invoice_number.parameterize
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def generate_temporary_invoice_number
|
|
83
|
+
self.invoice_number = "#{Time.now.to_i}"
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def reset_invoice_number
|
|
87
|
+
self.update_attribute(:invoice_number, "#{INV}-#{self.terminal.code}-#{self.id}") if self.draft?
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def to_param
|
|
91
|
+
"#{id}-#{slug}"
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# * Return true if the invoice is draft, else false.
|
|
95
|
+
# == Examples
|
|
96
|
+
# >>> invoice.draft?
|
|
97
|
+
# => true
|
|
98
|
+
def draft?
|
|
99
|
+
(status == DRAFT)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# change the status to :draft
|
|
103
|
+
# Return the status
|
|
104
|
+
# == Examples
|
|
105
|
+
# >>> invoice.publish!
|
|
106
|
+
# => "draft"
|
|
107
|
+
def draft!
|
|
108
|
+
self.update_attribute(:status, DRAFT)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# * Return true if the invoice is active, else false.
|
|
112
|
+
# == Examples
|
|
113
|
+
# >>> invoice.active?
|
|
114
|
+
# => true
|
|
115
|
+
def active?
|
|
116
|
+
(status == ACTIVE)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# change the status to :active
|
|
120
|
+
# Return the status
|
|
121
|
+
# == Examples
|
|
122
|
+
# >>> invoice.unpublish!
|
|
123
|
+
# => "active"
|
|
124
|
+
def activate!
|
|
125
|
+
self.update_attributes(status: ACTIVE)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# * Return true if the invoice is cancelled, else false.
|
|
129
|
+
# == Examples
|
|
130
|
+
# >>> invoice.cancelled?
|
|
131
|
+
# => true
|
|
132
|
+
def cancelled?
|
|
133
|
+
(status == CANCELLED)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# change the status to :cancelled
|
|
137
|
+
# Return the status
|
|
138
|
+
# == Examples
|
|
139
|
+
# >>> invoice.remove!
|
|
140
|
+
# => "cancelled"
|
|
141
|
+
def cancel!
|
|
142
|
+
self.update_attributes(status: CANCELLED, featured: false)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# * Return true if the invoice is cash, else false.
|
|
146
|
+
# == Examples
|
|
147
|
+
# >>> invoice.cash?
|
|
148
|
+
# => true
|
|
149
|
+
def cash?
|
|
150
|
+
(payment_method == CASH)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# * Return true if the invoice is credit, else false.
|
|
154
|
+
# == Examples
|
|
155
|
+
# >>> invoice.credit?
|
|
156
|
+
# => true
|
|
157
|
+
def credit?
|
|
158
|
+
(payment_method == CREDIT)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# * Return true if the invoice is credit_card, else false.
|
|
162
|
+
# == Examples
|
|
163
|
+
# >>> invoice.credit_card?
|
|
164
|
+
# => true
|
|
165
|
+
def credit_card?
|
|
166
|
+
(payment_method == CREDIT_CARD)
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# * Return true if the invoice is cheque, else false.
|
|
170
|
+
# == Examples
|
|
171
|
+
# >>> invoice.cheque?
|
|
172
|
+
# => true
|
|
173
|
+
def cheque?
|
|
174
|
+
(payment_method == CHEQUE)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def can_be_draft?
|
|
178
|
+
active? or cancelled?
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def can_be_active?
|
|
182
|
+
draft? or cancelled?
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def can_be_cancelled?
|
|
186
|
+
draft? or active?
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# TODO
|
|
190
|
+
|
|
191
|
+
def can_be_edited?
|
|
192
|
+
true
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def can_be_deleted?
|
|
196
|
+
cancelled? && self.line_items.blank?
|
|
197
|
+
#if self.jobs.any?
|
|
198
|
+
# self.errors.add(:base, DELETE_MESSAGE)
|
|
199
|
+
# return false
|
|
200
|
+
#else
|
|
201
|
+
# return true
|
|
202
|
+
#end
|
|
203
|
+
#return true
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def generate_real_invoice_number!
|
|
207
|
+
self.invoice_number = "INV-#{self.terminal.code}-#{self.id}"
|
|
208
|
+
self.save
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def net_total_amount
|
|
212
|
+
self.total_amount - self.discount
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
def balance_to_give
|
|
216
|
+
self.money_taken - self.net_total_amount
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def calculate_total_amount
|
|
220
|
+
self.total_amount = self.line_items.sum("rate * quantity") || 0.0
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
end
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
class LineItem < Vyapari::ApplicationRecord
|
|
2
|
+
|
|
3
|
+
# Constants
|
|
4
|
+
DRAFT = "draft"
|
|
5
|
+
SOLD = "sold"
|
|
6
|
+
DAMAGED = "damaged"
|
|
7
|
+
RETURNED = "returned"
|
|
8
|
+
|
|
9
|
+
STATUS_HASH = {"Draft" => DRAFT, "Sold" => SOLD, "Damaged" => DAMAGED, "Returned" => RETURNED}
|
|
10
|
+
STATUS_HASH_REVERSE = {DRAFT => "Draft", SOLD => "Sold", DAMAGED => "Damaged", RETURNED => "Returned"}
|
|
11
|
+
|
|
12
|
+
# Call backs
|
|
13
|
+
before_save :calculate_total_amount
|
|
14
|
+
|
|
15
|
+
# Validations
|
|
16
|
+
validates :product, :presence=> true
|
|
17
|
+
validates :quantity, :presence=> true
|
|
18
|
+
validates :rate, :presence=> true
|
|
19
|
+
|
|
20
|
+
validates :status, :presence=> true, :inclusion => {:in => STATUS_HASH_REVERSE.keys, :presence_of => :status, :message => "%{value} is not a valid status" }
|
|
21
|
+
|
|
22
|
+
# Associations
|
|
23
|
+
belongs_to :product, optional: true
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# ------------------
|
|
27
|
+
# Class Methods
|
|
28
|
+
# ------------------
|
|
29
|
+
|
|
30
|
+
# Scopes
|
|
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
|
+
# >>> invoice.search(query)
|
|
36
|
+
# => ActiveRecord::Relation object
|
|
37
|
+
scope :search, lambda { |query| joins(:product).where("LOWER(products.name) LIKE LOWER('%#{query}%')")}
|
|
38
|
+
scope :status, lambda { |status| where("LOWER(status)='#{status}'") }
|
|
39
|
+
|
|
40
|
+
scope :draft, -> { where(status: DRAFT) }
|
|
41
|
+
scope :sold, -> { where(status: SOLD) }
|
|
42
|
+
scope :damaged, -> { where(status: DAMAGED) }
|
|
43
|
+
scope :returned, -> { where(status: RETURNED) }
|
|
44
|
+
|
|
45
|
+
scope :this_month, lambda { where("created_at >= ? AND created_at <= ?", Time.zone.now.beginning_of_month, Time.zone.now.end_of_month) }
|
|
46
|
+
|
|
47
|
+
# ------------------
|
|
48
|
+
# Instance Methods
|
|
49
|
+
# ------------------
|
|
50
|
+
|
|
51
|
+
def display_name
|
|
52
|
+
if product
|
|
53
|
+
self.product.display_name
|
|
54
|
+
else
|
|
55
|
+
"#{self.id} - No Product"
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# * Return true if the invoice is sold, else false.
|
|
60
|
+
# == Examples
|
|
61
|
+
# >>> invoice.sold?
|
|
62
|
+
# => true
|
|
63
|
+
def sold?
|
|
64
|
+
(status == SOLD)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# change the status to :sold
|
|
68
|
+
# Return the status
|
|
69
|
+
# == Examples
|
|
70
|
+
# >>> invoice.publish!
|
|
71
|
+
# => "sold"
|
|
72
|
+
def sold!
|
|
73
|
+
self.update_attribute(:status, SOLD)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# * Return true if the invoice is active, else false.
|
|
77
|
+
# == Examples
|
|
78
|
+
# >>> invoice.active?
|
|
79
|
+
# => true
|
|
80
|
+
def active?
|
|
81
|
+
(status == ACTIVE)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# change the status to :active
|
|
85
|
+
# Return the status
|
|
86
|
+
# == Examples
|
|
87
|
+
# >>> invoice.unpublish!
|
|
88
|
+
# => "active"
|
|
89
|
+
def activate!
|
|
90
|
+
self.update_attributes(status: ACTIVE, featured: false)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# * Return true if the invoice is cancelled, else false.
|
|
94
|
+
# == Examples
|
|
95
|
+
# >>> invoice.cancelled?
|
|
96
|
+
# => true
|
|
97
|
+
def cancelled?
|
|
98
|
+
(status == CANCELLED)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# change the status to :cancelled
|
|
102
|
+
# Return the status
|
|
103
|
+
# == Examples
|
|
104
|
+
# >>> invoice.remove!
|
|
105
|
+
# => "cancelled"
|
|
106
|
+
def cancel!
|
|
107
|
+
self.update_attributes(status: CANCELLED, featured: false)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def can_be_sold?
|
|
111
|
+
active? or cancelled?
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def can_be_active?
|
|
115
|
+
sold? or cancelled?
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def can_be_cancelled?
|
|
119
|
+
sold? or active?
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# change the status to :sold
|
|
123
|
+
# Return the status
|
|
124
|
+
# == Examples
|
|
125
|
+
# >>> stock_entry.sell!
|
|
126
|
+
# => "sold"
|
|
127
|
+
def sell!
|
|
128
|
+
self.update_attribute(:status, SOLD)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# change the status to :damaged
|
|
132
|
+
# Return the status
|
|
133
|
+
# == Examples
|
|
134
|
+
# >>> stock_entry.mark_as_damaged!
|
|
135
|
+
# => "damaged"
|
|
136
|
+
def mark_as_damaged!
|
|
137
|
+
self.update_attribute(:status, DAMAGED)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# change the status to :returned
|
|
141
|
+
# Return the status
|
|
142
|
+
# == Examples
|
|
143
|
+
# >>> stock_entry.reserve!
|
|
144
|
+
# => "returned"
|
|
145
|
+
def return!
|
|
146
|
+
self.update_attribute(:status, RETURN)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# change the status to :draft
|
|
150
|
+
# Return the status
|
|
151
|
+
# == Examples
|
|
152
|
+
# >>> stock_entry.draft!
|
|
153
|
+
# => "draft"
|
|
154
|
+
def draft!
|
|
155
|
+
self.update_attribute(:status, DRAFT)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# * Return true if the stock_entry is sold, else false.
|
|
159
|
+
# == Examples
|
|
160
|
+
# >>> stock_entry.sold?
|
|
161
|
+
# => true
|
|
162
|
+
def sold?
|
|
163
|
+
(status == SOLD)
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# * Return true if the stock_entry is damaged, else false.
|
|
167
|
+
# == Examples
|
|
168
|
+
# >>> stock_entry.damaged?
|
|
169
|
+
# => true
|
|
170
|
+
def damaged?
|
|
171
|
+
(status == DAMAGED)
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# * Return true if the stock_entry is returned, else false.
|
|
175
|
+
# == Examples
|
|
176
|
+
# >>> stock_entry.returned?
|
|
177
|
+
# => true
|
|
178
|
+
def returned?
|
|
179
|
+
(status == RETURNED)
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def calculate_total_amount
|
|
183
|
+
if self.quantity * self.rate
|
|
184
|
+
self.total_amount = self.quantity * self.rate
|
|
185
|
+
else
|
|
186
|
+
self.total_amount = 0.00
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# TODO
|
|
191
|
+
def can_be_edited?
|
|
192
|
+
true
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def can_be_deleted?
|
|
196
|
+
true
|
|
197
|
+
#if self.jobs.any?
|
|
198
|
+
# self.errors.add(:base, DELETE_MESSAGE)
|
|
199
|
+
# return false
|
|
200
|
+
#else
|
|
201
|
+
# return true
|
|
202
|
+
#end
|
|
203
|
+
#return true
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
end
|
data/app/models/product.rb
CHANGED
|
@@ -4,6 +4,7 @@ class Product < Vyapari::ApplicationRecord
|
|
|
4
4
|
PUBLISHED = "published"
|
|
5
5
|
UNPUBLISHED = "unpublished"
|
|
6
6
|
REMOVED = "removed"
|
|
7
|
+
|
|
7
8
|
STATUS_HASH = {"Published" => PUBLISHED, "Unpublished" => UNPUBLISHED, "Removed" => REMOVED}
|
|
8
9
|
STATUS_HASH_REVERSE = {PUBLISHED => "Published", UNPUBLISHED => "Unpublished", REMOVED => "Removed"}
|
|
9
10
|
|
|
@@ -11,18 +12,18 @@ class Product < Vyapari::ApplicationRecord
|
|
|
11
12
|
FEATURED_HASH_REVERSE = {true => "Featured", false => "Non Featured"}
|
|
12
13
|
|
|
13
14
|
# Callbacks
|
|
14
|
-
before_validation :update_permalink
|
|
15
15
|
before_validation :update_top_category
|
|
16
16
|
|
|
17
17
|
# Validations
|
|
18
18
|
validates :name, presence: true
|
|
19
|
+
validates :ean_sku, presence: true
|
|
20
|
+
|
|
19
21
|
#validates :description, presence: true
|
|
20
|
-
validates :permalink, presence: true, uniqueness: true
|
|
21
22
|
validates :status, :presence=> true, :inclusion => {:in => STATUS_HASH_REVERSE.keys, :presence_of => :status, :message => "%{value} is not a valid status" }
|
|
22
23
|
|
|
23
24
|
# Associations
|
|
24
25
|
has_one :product_image, :as => :imageable, :dependent => :destroy, :class_name => "Image::ProductImage"
|
|
25
|
-
has_one :brochure, :as => :documentable, :dependent => :destroy, :class_name => "Document::Brochure"
|
|
26
|
+
#has_one :brochure, :as => :documentable, :dependent => :destroy, :class_name => "Document::Brochure"
|
|
26
27
|
|
|
27
28
|
belongs_to :brand
|
|
28
29
|
belongs_to :category
|
|
@@ -35,7 +36,6 @@ class Product < Vyapari::ApplicationRecord
|
|
|
35
36
|
# => ActiveRecord::Relation object
|
|
36
37
|
scope :search, lambda {|query| joins("INNER JOIN categories on categories.id = products.category_id").
|
|
37
38
|
where("LOWER(products.name) LIKE LOWER('%#{query}%') OR\
|
|
38
|
-
LOWER(products.permalink) LIKE LOWER('%#{query}%') OR\
|
|
39
39
|
LOWER(products.one_liner) LIKE LOWER('%#{query}%') OR\
|
|
40
40
|
LOWER(products.description) LIKE LOWER('%#{query}%') OR\
|
|
41
41
|
LOWER(categories.name) LIKE LOWER('%#{query}%') OR\
|
|
@@ -50,10 +50,57 @@ class Product < Vyapari::ApplicationRecord
|
|
|
50
50
|
scope :unpublished, -> { where(status: UNPUBLISHED) }
|
|
51
51
|
scope :removed, -> { where(status: REMOVED) }
|
|
52
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[:name].blank?
|
|
58
|
+
|
|
59
|
+
product = Product.find_by_name(row[:name]) || Product.new
|
|
60
|
+
|
|
61
|
+
product.name = row[:name]
|
|
62
|
+
product.one_liner = row[:one_liner]
|
|
63
|
+
product.description = row[:description]
|
|
64
|
+
product.ean_sku = row[:ean_sku]
|
|
65
|
+
product.reference_number = row[:reference_number]
|
|
66
|
+
|
|
67
|
+
product.brand = Brand.find_by_name(row[:brand])
|
|
68
|
+
product.category = Category.find_by_name(row[:category])
|
|
69
|
+
product.top_category = product.category.top_category if product.category
|
|
70
|
+
|
|
71
|
+
product.featured = row[:featured]
|
|
72
|
+
product.status = row[:status]
|
|
73
|
+
product.priority = row[:priority]
|
|
74
|
+
|
|
75
|
+
# Initializing error hash for displaying all errors altogether
|
|
76
|
+
error_object = Kuppayam::Importer::ErrorHash.new
|
|
77
|
+
|
|
78
|
+
if product.valid?
|
|
79
|
+
product.save!
|
|
80
|
+
else
|
|
81
|
+
summary = "Error while saving product: #{product.name}"
|
|
82
|
+
details = "Error! #{product.errors.full_messages.to_sentence}"
|
|
83
|
+
error_object.errors << { summary: summary, details: details }
|
|
84
|
+
end
|
|
85
|
+
return error_object
|
|
86
|
+
end
|
|
87
|
+
|
|
53
88
|
# ------------------
|
|
54
89
|
# Instance variables
|
|
55
90
|
# ------------------
|
|
56
91
|
|
|
92
|
+
def display_name
|
|
93
|
+
"#{self.ean_sku} : #{self.name}"
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def slug
|
|
97
|
+
self.name.parameterize
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def to_param
|
|
101
|
+
"#{id}-#{slug}"
|
|
102
|
+
end
|
|
103
|
+
|
|
57
104
|
# * Return true if the brand is published, else false.
|
|
58
105
|
# == Examples
|
|
59
106
|
# >>> brand.published?
|
|
@@ -105,7 +152,22 @@ class Product < Vyapari::ApplicationRecord
|
|
|
105
152
|
self.update_attributes(status: REMOVED, featured: false)
|
|
106
153
|
end
|
|
107
154
|
|
|
108
|
-
|
|
155
|
+
def can_be_published?
|
|
156
|
+
unpublished? or removed?
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def can_be_unpublished?
|
|
160
|
+
published? or removed?
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def can_be_removed?
|
|
164
|
+
published? or unpublished? or removed?
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def can_be_edited?
|
|
168
|
+
true
|
|
169
|
+
end
|
|
170
|
+
|
|
109
171
|
def can_be_deleted?
|
|
110
172
|
#if self.jobs.any?
|
|
111
173
|
# self.errors.add(:base, DELETE_MESSAGE)
|
|
@@ -116,10 +178,6 @@ class Product < Vyapari::ApplicationRecord
|
|
|
116
178
|
return true
|
|
117
179
|
end
|
|
118
180
|
|
|
119
|
-
def default_image_url(size="medium")
|
|
120
|
-
"/assets/defaults/product-#{size}.png"
|
|
121
|
-
end
|
|
122
|
-
|
|
123
181
|
protected
|
|
124
182
|
|
|
125
183
|
def update_top_category
|
|
@@ -132,8 +190,4 @@ class Product < Vyapari::ApplicationRecord
|
|
|
132
190
|
end
|
|
133
191
|
end
|
|
134
192
|
|
|
135
|
-
def update_permalink
|
|
136
|
-
self.permalink = "#{id}-#{name.parameterize}" if self.permalink.blank?
|
|
137
|
-
end
|
|
138
|
-
|
|
139
193
|
end
|
data/app/models/region.rb
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
class Region < Vyapari::ApplicationRecord
|
|
2
2
|
|
|
3
|
-
DELETE_MESSAGE = "Cannot delete this Region. You should first delete all the dependant data like Company Cities, Jobs, Budgets etc tagged with this Region"
|
|
4
|
-
|
|
5
3
|
# Validations
|
|
6
4
|
validates :name, presence: true, length: {minimum: 2, maximum: 250}, allow_blank: false
|
|
7
5
|
validates :code, presence: true, uniqueness: true, length: {minimum: 2, maximum: 32}, allow_blank: false
|
|
@@ -9,7 +7,7 @@ class Region < Vyapari::ApplicationRecord
|
|
|
9
7
|
|
|
10
8
|
# Associations
|
|
11
9
|
belongs_to :country
|
|
12
|
-
|
|
10
|
+
has_many :stores
|
|
13
11
|
|
|
14
12
|
# ------------------
|
|
15
13
|
# Class Methods
|
|
@@ -22,6 +20,31 @@ class Region < Vyapari::ApplicationRecord
|
|
|
22
20
|
# => ActiveRecord::Relation object
|
|
23
21
|
scope :search, lambda {|query| joins(:country).where("LOWER(cities.name) LIKE LOWER('%#{query}%') OR LOWER(countries.name) LIKE LOWER('%#{query}%')")}
|
|
24
22
|
|
|
23
|
+
def self.save_row_data(row)
|
|
24
|
+
|
|
25
|
+
row.headers.each{ |cell| row[cell] = row[cell].to_s.strip }
|
|
26
|
+
|
|
27
|
+
return if row[:name].blank?
|
|
28
|
+
|
|
29
|
+
region = Region.find_by_code(row[:code]) || Region.new
|
|
30
|
+
region.name = row[:name]
|
|
31
|
+
region.code = row[:code]
|
|
32
|
+
region.country = Country.find_by_code(row[:country]) || Country.find_by_name(row[:country])
|
|
33
|
+
|
|
34
|
+
# Initializing error hash for displaying all errors altogether
|
|
35
|
+
error_object = Kuppayam::Importer::ErrorHash.new
|
|
36
|
+
|
|
37
|
+
if region.valid?
|
|
38
|
+
region.save!
|
|
39
|
+
else
|
|
40
|
+
summary = "Error while saving region: #{region.name}"
|
|
41
|
+
details = "Error! #{region.errors.full_messages.to_sentence}"
|
|
42
|
+
error_object.errors << { summary: summary, details: details }
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
return error_object
|
|
46
|
+
end
|
|
47
|
+
|
|
25
48
|
# ------------------
|
|
26
49
|
# Instance Methods
|
|
27
50
|
# ------------------
|