lucadeal 0.2.7 → 0.2.16

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c9b1e922691e9d8fbc64c992e50e39068bd2652590b601ec70a13a3a49ede551
4
- data.tar.gz: cbecc49c20bbe61e7d1920f61ee3357d53ff4f44b7fc3582ee92e716a0cc670a
3
+ metadata.gz: 5f17d75af6f101c60eced0ecfb770e39ac66b0f35725a6976f135216a0c75099
4
+ data.tar.gz: d3e60d51908e78ab72e177f29affc26864744afb584d1c5dadd507b18ff888f6
5
5
  SHA512:
6
- metadata.gz: f8e181b5272c1ced02af7b0fa5523004b352301ee9310d5aebbe8918ab7899d13a75c337c8627765f8be3fdfe9b9415598e372d8bc98b45b3b4f53615a8731d2
7
- data.tar.gz: e491c40bff3806dc911df1aae738d858c8e6537a992aa9b996ffeff0fa7ab83010d7f486a840556d6d08330097fa890ffb02a4ad0881064fd9a98a2df8d7cbcb
6
+ metadata.gz: bd534c31a33405478cdeba88777481f5c17af6337cf743e2d16a8e5027209a65d1efb8329cf7f6e72318dfea8c3daa1965d9cf1e833ce93b85d24925c17638f6
7
+ data.tar.gz: f1d809de08ce94cf7da913c5bc733515f95bba596dc155596880cb4776837113c36f93ee4247c35fe34990f2a45fd758df1bddec3d2e587d182062cdb5893c60
@@ -0,0 +1,11 @@
1
+ ## LucaDeal 0.2.14
2
+
3
+ * Introduce Product for selling items template.
4
+
5
+ ## LucaDeal 0.2.12
6
+
7
+ * Introduce Sales fee calculation.
8
+
9
+ ## LucaDeal 0.2.10
10
+
11
+ * items can have one time cost at initial month of contract.
data/README.md CHANGED
@@ -1,15 +1,15 @@
1
- # Luca::Deal
1
+ # LucaDeal
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/luca/deal`. To experiment with that code, run `bin/console` for an interactive prompt.
3
+ [![Gem Version](https://badge.fury.io/rb/lucadeal.svg)](https://badge.fury.io/rb/lucadeal)
4
4
 
5
- TODO: Delete this and the text above, and describe your gem
5
+ LucaDeal is Sales contract management application.
6
6
 
7
7
  ## Installation
8
8
 
9
9
  Add this line to your application's Gemfile:
10
10
 
11
11
  ```ruby
12
- gem 'luca-deal'
12
+ gem 'lucadeal'
13
13
  ```
14
14
 
15
15
  And then execute:
@@ -18,12 +18,141 @@ And then execute:
18
18
 
19
19
  Or install it yourself as:
20
20
 
21
- $ gem install luca-deal
21
+ $ gem install lucadeal
22
22
 
23
23
  ## Usage
24
24
 
25
25
  TODO: Write usage instructions here
26
26
 
27
+
28
+ ## Data Structure
29
+
30
+ Records are stored in YAML format. On historical records, see [LucaRecord](../lucarecord/README.md#historical-field).
31
+
32
+ ### Customer
33
+
34
+ Customer consists of label information.
35
+
36
+ | Top level | Second level | | historical | Description |
37
+ |-----------|--------------|------|------------|--------------------------------|
38
+ | id | | auto | | uuid |
39
+ | name | | must | x | customer's name |
40
+ | address | | | x | |
41
+ | address2 | | | x | |
42
+ | contacts | | | | Array of contact information |
43
+ | | mail | | | mail address receiving invoice |
44
+
45
+
46
+ ### Product
47
+
48
+ Product is items template referred by Contract.
49
+
50
+ | Top level | Second level | | historical | Description |
51
+ |-----------|--------------|----------|------------|------------------------------------------------------------------------------------------------------|
52
+ | id | | auto | | uuid |
53
+ | name | | | x | Product name. |
54
+ | items | | | | Array of items. |
55
+ | | name | | x | Item name. |
56
+ | | price | | x | Item price. |
57
+ | | qty | optional | x | quantity. Default: 1. |
58
+ | | type | optional | | If 'initial', this item is treated as initial cost, applied only on the first month of the contract. |
59
+
60
+
61
+ ### Contract
62
+
63
+ Contract is core object for calculation. Common fields are as follows:
64
+
65
+ | Top level | Second level | | historical | Description |
66
+ |-------------|---------------|----------|------------|------------------------------------------------------------------------------------------------------|
67
+ | id | | auto | | uuid |
68
+ | customer_id | | must | x | customer's uuid |
69
+ | terms | | | | |
70
+ | | effective | must | | Start date of the contract. |
71
+ | | defunct | | | End date of the contract. |
72
+
73
+ Fields for subscription customers are as bellows:
74
+
75
+ | Top level | Second level | | historical | Description |
76
+ |-----------|---------------|----------|------------|------------------------------------------------------------------------------------------------------|
77
+ | terms | | | | |
78
+ | | billing_cycle | optional | | If 'monthly', invoices are generated on each month. |
79
+ | | category | optional | | Default: 'subscription' |
80
+ | products | | | | Array of products. |
81
+ | | id | | | reference for Product |
82
+ | items | | | | Array of items. |
83
+ | | name | | x | Item name. |
84
+ | | price | | x | Item price. |
85
+ | | qty | optional | x | quantity. Default: 1. |
86
+ | | type | optional | | If 'initial', this item is treated as initial cost, applied only on the first month of the contract. |
87
+ | sales_fee | | optional | | |
88
+ | | id | | | contract id of fee with sales partner. |
89
+
90
+
91
+ Fields for sales fee are as bellows:
92
+
93
+ | Top level | Second level | | historical | Description |
94
+ |-----------|--------------|----------|------------|-------------------------------------------------------------------------------------|
95
+ | terms | | | | |
96
+ | | category | | | If 'sales_fee', contract is treated as selling commission. |
97
+ | rate | | optional | | |
98
+ | | default | | | sales fee rate. |
99
+ | | initial | | | sales fee rate for items of type=initial. |
100
+
101
+
102
+ ### Invoice
103
+
104
+ Invoice is basically auto generated from Customer and Contract objects.
105
+
106
+ | Top level | Second level | Description |
107
+ |------------|--------------|------------------------------------------|
108
+ | id | | uuid |
109
+ | issue_date | | |
110
+ | due_date | | |
111
+ | customer | | |
112
+ | | id | customer's uuid |
113
+ | | name | customer name |
114
+ | | address | |
115
+ | | address2 | |
116
+ | | to | Array of mail addresses |
117
+ | items | | Array of items. |
118
+ | | name | Item name. |
119
+ | | price | Item price. |
120
+ | | qty | quantity. Default: 1. |
121
+ | | type | |
122
+ | | product_id | refrence for Product |
123
+ | subtotal | | Array of subtotal by tax category. |
124
+ | | items | amount of items |
125
+ | | tax | amount of tax |
126
+ | | rate | applied tax category. Default: 'default' |
127
+ | sales_fee | | |
128
+ | | id | contract id of fee with sales partner. |
129
+ | status | | Array of status with timestamp. |
130
+
131
+
132
+ ### Fee
133
+
134
+ Fee is basically auto generated from Contract and Invoice objects.
135
+
136
+ | Top level | Second level | Description |
137
+ |-----------|--------------|----------------------------------------------|
138
+ | id | | uuid |
139
+ | sales_fee | | |
140
+ | | id | contract id with sales partner. |
141
+ | | default.fee | Amount of fee on dafault rate. |
142
+ | | default.tax | Amount of tax for default.fee. |
143
+ | | initial.fee | Amount of fee on initial cost. |
144
+ | | initial.tax | Amount of tax for initial.fee. |
145
+ | invoice | | Carbon copy of Invoice attributes. |
146
+ | | id | |
147
+ | | contract_id | |
148
+ | | issue_date | |
149
+ | | due_date | |
150
+ | customer | | Carbon copy of Invoice customer except 'to'. |
151
+ | items | | Carbon copy of Invoice items. |
152
+ | subtotal | | Carbon copy of Invoice subtotal. |
153
+ | status | | Array of status with timestamp. |
154
+
155
+
27
156
  ## Development
28
157
 
29
158
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -32,4 +161,4 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
32
161
 
33
162
  ## Contributing
34
163
 
35
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/luca-deal.
164
+ Bug reports and pull requests are welcome on GitHub at https://github.com/chumaltd/luca .
@@ -11,7 +11,7 @@ def customer(args = nil, params = {})
11
11
  LucaDeal::Customer.new.list_name
12
12
  when 'create'
13
13
  if params['name']
14
- id = LucaDeal::Customer.new.generate!(params['name'])
14
+ id = LucaDeal::Customer.create(name: params['name'])
15
15
  puts "Successfully generated Customer #{id}" if id
16
16
  puts 'Edit customer detail.' if id
17
17
  else
@@ -28,7 +28,7 @@ def contract(args = nil, params = {})
28
28
  case params['mode']
29
29
  when 'create'
30
30
  if params['customer_id']
31
- id = LucaDeal::Contract.new.generate!(params['customer_id'])
31
+ id = LucaDeal::Contract.new.generate!(params['customer_id'], params['category'])
32
32
  puts "Successfully generated Contract #{id}" if id
33
33
  puts 'Conditions are tentative. Edit contract detail.' if id
34
34
  else
@@ -87,6 +87,9 @@ when 'contract'
87
87
  params['mode'] = 'create'
88
88
  params['customer_id'] = v
89
89
  end
90
+ opt.on('--salesfee', 'create contract as sales fee definition') do |_v|
91
+ params['category'] = 'sales_fee'
92
+ end
90
93
  args = opt.parse(ARGV)
91
94
  contract(args, params)
92
95
  end
@@ -6,6 +6,8 @@ require 'luca_deal/version'
6
6
  module LucaDeal
7
7
  autoload :Customer, 'luca_deal/customer'
8
8
  autoload :Contract, 'luca_deal/contract'
9
+ autoload :Fee, 'luca_deal/fee'
9
10
  autoload :Invoice, 'luca_deal/invoice'
11
+ autoload :Product, 'luca_deal/product'
10
12
  autoload :Setup, 'luca_deal/setup'
11
13
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'luca_deal/version'
2
4
 
3
5
  require 'yaml'
@@ -7,40 +9,85 @@ require 'luca_record'
7
9
  module LucaDeal
8
10
  class Contract < LucaRecord::Base
9
11
  @dirname = 'contracts'
12
+ @required = ['customer_id', 'terms']
10
13
 
11
14
  def initialize(date = nil)
12
15
  @date = date ? Date.parse(date) : Date.today
13
16
  @pjdir = Pathname(Dir.pwd)
14
17
  end
15
18
 
19
+ # returns active contracts on specified date.
20
+ #
21
+ def self.asof(year, month, day)
22
+ return enum_for(:asof, year, month, day) unless block_given?
23
+
24
+ new("#{year}-#{month}-#{day}").active do |contract|
25
+ yield contract
26
+ end
27
+ end
28
+
16
29
  #
17
30
  # collect active contracts
18
31
  #
19
32
  def active
33
+ return enum_for(:active) unless block_given?
34
+
20
35
  self.class.all do |data|
21
- contract = parse_current(data)
22
- next if !self.class.active_period?(contract)
36
+ next if !active_period?(data.dig('terms'))
23
37
 
24
- yield contract
38
+ contract = parse_current(data)
39
+ contract['items'] = contract['items']&.map { |item| parse_current(item) }
40
+ # TODO: handle sales_fee rate change
41
+ contract['rate'] = contract['rate']
42
+ yield contract.compact
25
43
  end
26
44
  end
27
45
 
28
- def generate!(customer_id)
46
+ def generate!(customer_id, mode = 'subscription')
29
47
  LucaDeal::Customer.find(customer_id) do |customer|
30
48
  current_customer = parse_current(customer)
31
- obj = { 'customer_id' => current_customer['id'], 'customer_name' => current_customer['name'] }
49
+ if mode == 'sales_fee'
50
+ obj = salesfee_template
51
+ else
52
+ obj = monthly_template
53
+ end
54
+ obj.merge!({ 'customer_id' => current_customer['id'], 'customer_name' => current_customer['name'] })
55
+ obj['terms'] ||= {}
56
+ obj['terms']['effective'] = @date
57
+ self.class.create(obj)
58
+ end
59
+ end
60
+
61
+ def active_period?(dat)
62
+ unless dat.dig('defunct').nil?
63
+ defunct = dat.dig('defunct').respond_to?(:year) ? dat.dig('defunct') : Date.parse(dat.dig('defunct'))
64
+ return false if @date > defunct
65
+ end
66
+ effective = dat.dig('effective').respond_to?(:year) ? dat.dig('effective') : Date.parse(dat.dig('effective'))
67
+ @date >= effective
68
+ end
69
+
70
+ private
71
+
72
+ def monthly_template
73
+ {}.tap do |obj|
32
74
  obj['items'] = [{
33
75
  'name' => '_ITEM_NAME_FOR_INVOICE_',
34
76
  'qty' => 1,
35
77
  'price' => 0
36
78
  }]
37
- obj['terms'] = { 'billing_cycle' => 'monthly', 'effective' => @date }
38
- self.class.create(obj)
79
+ obj['terms'] = { 'billing_cycle' => 'monthly' }
39
80
  end
40
81
  end
41
82
 
42
- def self.active_period?(dat)
43
- !dat.dig('terms').nil?
83
+ def salesfee_template
84
+ {}.tap do |obj|
85
+ obj['rate'] = {
86
+ 'default' => '0.2',
87
+ 'initial' => '0.2'
88
+ }
89
+ obj['terms'] = { 'category' => 'sales_fee' }
90
+ end
44
91
  end
45
92
  end
46
93
  end
@@ -10,6 +10,7 @@ require 'luca_record'
10
10
  module LucaDeal
11
11
  class Customer < LucaRecord::Base
12
12
  @dirname = 'customers'
13
+ @required = ['name']
13
14
 
14
15
  def initialize(pjdir = nil)
15
16
  @date = Date.today
@@ -21,17 +22,20 @@ module LucaDeal
21
22
  YAML.dump(list).tap { |l| puts l }
22
23
  end
23
24
 
24
- def generate!(name)
25
- contact = {
25
+ def self.create(obj)
26
+ raise ':name is required' if obj[:name].nil?
27
+
28
+ contacts = obj[:contact]&.map { |c| { 'mail' => c[:mail] } }&.compact
29
+ contacts ||= [{
26
30
  'mail' => '_MAIL_ADDRESS_FOR_CONTACT_'
31
+ }]
32
+ h = {
33
+ 'name' => obj[:name],
34
+ 'address' => obj[:address] || '_CUSTOMER_ADDRESS_FOR_INVOICE_',
35
+ 'address2' => obj[:address2] || '_CUSTOMER_ADDRESS_FOR_INVOICE_',
36
+ 'contacts' => contacts
27
37
  }
28
- obj = {
29
- 'name' => name,
30
- 'address' => '_CUSTOMER_ADDRESS_FOR_INVOICE_',
31
- 'address2' => '_CUSTOMER_ADDRESS_FOR_INVOICE_',
32
- 'contacts' => [contact]
33
- }
34
- self.class.create(obj)
38
+ super(h)
35
39
  end
36
40
  end
37
41
  end
@@ -0,0 +1,118 @@
1
+ require 'luca_deal/version'
2
+
3
+ require 'mail'
4
+ require 'yaml'
5
+ require 'pathname'
6
+ require 'bigdecimal'
7
+ require 'luca_support/config'
8
+ require 'luca_support/mail'
9
+ require 'luca_deal'
10
+
11
+ module LucaDeal
12
+ class Fee < LucaRecord::Base
13
+ @dirname = 'fees'
14
+
15
+ def initialize(date = nil)
16
+ @date = issue_date(date)
17
+ @config = load_config('config.yml')
18
+ end
19
+
20
+ # calculate fee, based on invoices
21
+ #
22
+ def monthly_fee
23
+ LucaDeal::Contract.asof(@date.year, @date.month, @date.day) do |contract|
24
+ next if contract.dig('terms', 'category') != 'sales_fee'
25
+
26
+ @rate = { 'default' => BigDecimal(contract.dig('rate', 'default')) }
27
+ @rate['initial'] = contract.dig('rate', 'initial') ? BigDecimal(contract.dig('rate', 'initial')) : @rate['default']
28
+
29
+ LucaDeal::Invoice.asof(@date.year, @date.month) do |invoice|
30
+ next if invoice.dig('sales_fee', 'id') != contract['id']
31
+ next if duplicated_contract? invoice['contract_id']
32
+
33
+ fee = invoice.dup
34
+ fee['invoice'] = {}.tap do |f_invoice|
35
+ %w[id contract_id issue_date due_date].each do |i|
36
+ f_invoice[i] = invoice[i]
37
+ fee.delete i
38
+ end
39
+ end
40
+ fee['id'] = issue_random_id
41
+ fee['customer'].delete('to')
42
+ fee['sales_fee'].merge! subtotal(fee['items'])
43
+ gen_fee!(fee)
44
+ end
45
+ end
46
+ end
47
+
48
+ def get_customer(id)
49
+ {}.tap do |res|
50
+ LucaDeal::Customer.find(id) do |dat|
51
+ customer = parse_current(dat)
52
+ res['id'] = customer['id']
53
+ res['name'] = customer.dig('name')
54
+ res['address'] = customer.dig('address')
55
+ res['address2'] = customer.dig('address2')
56
+ res['to'] = customer.dig('contacts').map { |h| take_current(h, 'mail') }.compact
57
+ end
58
+ end
59
+ end
60
+
61
+ def gen_fee!(fee)
62
+ id = fee.dig('invoice', 'contract_id')
63
+ self.class.create(fee, date: @date, codes: Array(id))
64
+ end
65
+
66
+ private
67
+
68
+ def lib_path
69
+ __dir__
70
+ end
71
+
72
+ # load user company profile from config.
73
+ #
74
+ def set_company
75
+ {}.tap do |h|
76
+ h['name'] = @config.dig('company', 'name')
77
+ h['address'] = @config.dig('company', 'address')
78
+ h['address2'] = @config.dig('company', 'address2')
79
+ end
80
+ end
81
+
82
+ # calc fee & tax amount by tax category
83
+ #
84
+ def subtotal(items)
85
+ {}.tap do |subtotal|
86
+ items.each do |i|
87
+ rate = i.dig('type') || 'default'
88
+ subtotal[rate] = { 'fee' => 0, 'tax' => 0 } if subtotal.dig(rate).nil?
89
+ subtotal[rate]['fee'] += i['qty'] * i['price'] * @rate[rate]
90
+ end
91
+ subtotal.each do |rate, amount|
92
+ amount['tax'] = (amount['fee'] * load_tax_rate(rate)).to_i
93
+ amount['fee'] = amount['fee'].to_i
94
+ end
95
+ end
96
+ end
97
+
98
+ def issue_date(date)
99
+ base = date.nil? ? Date.today : Date.parse(date)
100
+ Date.new(base.year, base.month, -1)
101
+ end
102
+
103
+ # load Tax Rate from config.
104
+ #
105
+ def load_tax_rate(name)
106
+ return 0 if @config.dig('tax_rate', name).nil?
107
+
108
+ BigDecimal(take_current(@config['tax_rate'], name).to_s)
109
+ end
110
+
111
+ def duplicated_contract?(id)
112
+ self.class.asof(@date.year, @date.month, @date.day) do |_f, path|
113
+ return true if path.include?(id)
114
+ end
115
+ false
116
+ end
117
+ end
118
+ end
@@ -12,6 +12,7 @@ require 'luca_record'
12
12
  module LucaDeal
13
13
  class Invoice < LucaRecord::Base
14
14
  @dirname = 'invoices'
15
+ @required = ['issue_date', 'customer', 'items', 'subtotal']
15
16
 
16
17
  def initialize(date = nil)
17
18
  @date = issue_date(date)
@@ -58,7 +59,6 @@ module LucaDeal
58
59
  mail
59
60
  end
60
61
 
61
- #
62
62
  # Output seriarized invoice data to stdout.
63
63
  # Returns previous N months on multiple count
64
64
  #
@@ -97,7 +97,7 @@ module LucaDeal
97
97
  collection << stat
98
98
  end
99
99
  end
100
- puts YAML.dump(collection)
100
+ puts YAML.dump(LucaSupport::Code.readable(collection))
101
101
  end
102
102
  end
103
103
 
@@ -113,12 +113,12 @@ module LucaDeal
113
113
  invoice['customer'] = get_customer(contract.dig('customer_id'))
114
114
  invoice['due_date'] = due_date(@date)
115
115
  invoice['issue_date'] = @date
116
- invoice['items'] = contract.dig('items').map do |item|
117
- {}.tap do |h|
118
- h['name'] = item.dig('name')
119
- h['price'] = item.dig('price')
120
- h['qty'] = item.dig('qty')
121
- end
116
+ invoice['sales_fee'] = contract['sales_fee'] if contract.dig('sales_fee')
117
+ invoice['items'] = get_products(contract['products'])
118
+ .concat(contract['items']&.map { |i| i['qty'] ||= 1; i } || [])
119
+ .compact
120
+ invoice['items'].reject! do |item|
121
+ item.dig('type') == 'initial' && subsequent_month?(contract.dig('terms', 'effective'))
122
122
  end
123
123
  invoice['subtotal'] = subtotal(invoice['items'])
124
124
  .map { |k, v| v.tap { |dat| dat['rate'] = k } }
@@ -126,7 +126,6 @@ module LucaDeal
126
126
  end
127
127
  end
128
128
 
129
- #
130
129
  # set variables for ERB template
131
130
  #
132
131
  def invoice_vars(invoice_dat)
@@ -151,9 +150,23 @@ module LucaDeal
151
150
  end
152
151
  end
153
152
 
153
+ def get_products(products)
154
+ return [] if products.nil?
155
+
156
+ [].tap do |res|
157
+ products.each do |product|
158
+ LucaDeal::Product.find(product['id'])['items'].each do |item|
159
+ item['product_id'] = product['id']
160
+ item['qty'] ||= 1
161
+ res << item
162
+ end
163
+ end
164
+ end
165
+ end
166
+
154
167
  def gen_invoice!(invoice)
155
168
  id = invoice.dig('contract_id')
156
- self.class.create_record!(invoice, @date, Array(id))
169
+ self.class.create(invoice, date: @date, codes: Array(id))
157
170
  end
158
171
 
159
172
  def issue_date(date)
@@ -172,7 +185,6 @@ module LucaDeal
172
185
  __dir__
173
186
  end
174
187
 
175
- #
176
188
  # load user company profile from config.
177
189
  #
178
190
  def set_company
@@ -183,23 +195,22 @@ module LucaDeal
183
195
  end
184
196
  end
185
197
 
186
- #
187
198
  # calc items & tax amount by tax category
188
199
  #
189
200
  def subtotal(items)
190
201
  {}.tap do |subtotal|
191
202
  items.each do |i|
192
203
  rate = i.dig('tax') || 'default'
204
+ qty = i['qty'] || BigDecimal('1')
193
205
  subtotal[rate] = { 'items' => 0, 'tax' => 0 } if subtotal.dig(rate).nil?
194
- subtotal[rate]['items'] += i['qty'] * i['price']
206
+ subtotal[rate]['items'] += i['price'] * qty
195
207
  end
196
208
  subtotal.each do |rate, amount|
197
- amount['tax'] = (amount['items'] * load_tax_rate(rate)).to_i
209
+ amount['tax'] = (amount['items'] * load_tax_rate(rate))
198
210
  end
199
211
  end
200
212
  end
201
213
 
202
- #
203
214
  # load Tax Rate from config.
204
215
  #
205
216
  def load_tax_rate(name)
@@ -218,5 +229,10 @@ module LucaDeal
218
229
  end
219
230
  false
220
231
  end
232
+
233
+ def subsequent_month?(effective_date)
234
+ effective_date = Date.parse(effective_date) unless effective_date.respond_to? :year
235
+ effective_date.year != @date.year || effective_date.month != @date.month
236
+ end
221
237
  end
222
238
  end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'luca_deal/version'
4
+
5
+ require 'date'
6
+ require 'yaml'
7
+ require 'pathname'
8
+ require 'luca_record'
9
+
10
+ module LucaDeal
11
+ class Product < LucaRecord::Base
12
+ @dirname = 'products'
13
+ @required = ['items']
14
+
15
+ def list_name
16
+ list = self.class.all.map { |dat| parse_current(dat) }
17
+ YAML.dump(list).tap { |l| puts l }
18
+ end
19
+
20
+ # Save data with hash in Product format. Simple format is also available as bellows:
21
+ # {
22
+ # name: 'item_name(required)', price: 'item_price', qty: 'item_qty',
23
+ # initial: { name: 'item_name', price: 'item_price', qty: 'item_qty' }
24
+ # }
25
+ def self.create(obj)
26
+ if obj[:name].nil?
27
+ h = obj
28
+ else
29
+ items = [{
30
+ 'name' => obj[:name],
31
+ 'price' => obj[:price] || 0,
32
+ 'qty' => obj[:qty] || 1
33
+ }]
34
+ if obj[:initial]
35
+ items << {
36
+ 'name' => obj.dig(:initial, :name),
37
+ 'price' => obj.dig(:initial, :price) || 0,
38
+ 'qty' => obj.dig(:initial, :qty) || 1,
39
+ 'type' => 'initial'
40
+ }
41
+ end
42
+ h = {
43
+ 'name' => obj[:name],
44
+ 'items' => items
45
+ }
46
+ end
47
+ super(h)
48
+ end
49
+ end
50
+ end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "luca_deal/version"
3
+ require 'luca_deal/version'
4
4
  require 'fileutils'
5
5
 
6
6
  module LucaDeal
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module LucaDeal
4
- VERSION = "0.2.7"
4
+ VERSION = '0.2.16'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lucadeal
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.7
4
+ version: 0.2.16
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chuma Takahiro
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-10-07 00:00:00.000000000 Z
11
+ date: 2020-11-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: lucarecord
@@ -28,14 +28,14 @@ dependencies:
28
28
  name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
33
  version: '1.17'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - "~>"
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '1.17'
41
41
  - !ruby/object:Gem::Dependency
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '5.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: 12.3.3
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: 12.3.3
55
69
  description: 'Deal with contracts
56
70
 
57
71
  '
@@ -69,7 +83,9 @@ files:
69
83
  - lib/luca_deal.rb
70
84
  - lib/luca_deal/contract.rb
71
85
  - lib/luca_deal/customer.rb
86
+ - lib/luca_deal/fee.rb
72
87
  - lib/luca_deal/invoice.rb
88
+ - lib/luca_deal/product.rb
73
89
  - lib/luca_deal/setup.rb
74
90
  - lib/luca_deal/templates/.keep
75
91
  - lib/luca_deal/templates/config.yml