seshbot-packing 0.4.0 → 0.8.2

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: 876fae06d90ffcfa93a6ff2c543595b317be2c6c6f8cf3d311d6c2851206b793
4
- data.tar.gz: 3bbac8434283bc50469928db836a390cab625e77bdfccb12db6345593b53809a
3
+ metadata.gz: 7336679900a9b2f7950d7af79692a4a1fa42c89d88b7b3e7e9932a564a0913c8
4
+ data.tar.gz: 4e3ced53b824add98949a55290e80c411f929b1c9032948214de06bac6107ce1
5
5
  SHA512:
6
- metadata.gz: 0f219d09e54f88bea303576c363d1f62a4e9f00540c49ace087d5f4789ad7f200a915c2d3e36f19bd8aa5cd17a1d054629cba7cd41bd9631202c2656a013942b
7
- data.tar.gz: b33866fd2549c75554b34419df98969d1672287645384149a537869f884a76dd6634a143c20aadfa11689978c7be4e2e2973f38ab315d2cb87161546b2a56bd2
6
+ metadata.gz: 717491a4886d9ae93e5e68f9036aeeea39aa50752718e77e2a6bfdba81a794944fd3d48a5aa4f32dad48e8385bcaa327ee662026b3e241f6ed66fd12f6c59d41
7
+ data.tar.gz: ba70ad1c512f59b905b560414efe5ed7e31a8cc69ad971e7fa402cc4fb2c5b493da40044162eb5864f958ee205ea43ccf94dd94837591eb71a60d67d79c69098
data/.gitignore CHANGED
@@ -1,4 +1,5 @@
1
1
  .byebug_history
2
+ .ruby-version
2
3
  /.bundle/
3
4
  /.yardoc
4
5
  /_yardoc/
data/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ ## Version 0.8.0
2
+
3
+ * -C324 are calculated and separated from bundling
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- seshbot-packing (0.3.0)
4
+ seshbot-packing (0.8.1)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -1,9 +1,5 @@
1
1
  # Seshbot::Packing
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/seshbot/packing`. To experiment with that code, run `bin/console` for an interactive prompt.
4
-
5
- TODO: Delete this and the text above, and describe your gem
6
-
7
3
  ## Installation
8
4
 
9
5
  Add this line to your application's Gemfile:
@@ -20,9 +16,43 @@ Or install it yourself as:
20
16
 
21
17
  $ gem install seshbot-packing
22
18
 
23
- ## Usage
19
+ ## Methods
20
+
21
+ ```ruby
22
+ # Bundle Items
23
+ ## this is the main method, it calls most other methods to get its result
24
+ items = [{ 'sku' => 'B-B306', 'quantity' => 2, 'price' => 1 }]
25
+ Seshbot::Packing.bundle_items(items)
26
+ #=> [{ 'sku' => 'BUND-B312', 'quantity' => 1, 'price' => 0 }]
27
+
28
+ # Count Product Types
29
+ ## groups together everythign after the `-` and counts them
30
+ items = [{"sku" => "A-B306", "quantity" => 2}, {"sku" => "B-B306", "quantity" => 1}]
31
+ Seshbot::Packing.count_product_types(items)
32
+ #=> {"B306"=>3}
33
+
34
+ # Separate SKU
35
+ ## returns the hashes where the sku value matches the given string
36
+ items = [{ 'sku' => 'A-C306', 'quantity' => 4, 'price' => 1 },
37
+ { 'sku' => 'A-B306', 'quantity' => 4, 'price' => 1 }]
38
+ Seshbot::Packing.filter_by_sku(items, '-C')
39
+ #=> [{ 'sku' => 'A-C306', 'quantity' => 4, 'price' => 1 }]
40
+ ## also takes a named argument
41
+ Seshbot::Packing.filter_by_sku(items, '-C', inverse: true)
42
+ #=> [{ 'sku' => 'A-B306', 'quantity' => 4, 'price' => 1 }]
43
+
44
+ # Substitute SKU
45
+ ## replaces an sku in a hash
46
+ Seshbot::Packing.substitute_sku(items, '-B', '-C')
47
+ #=> items = [{"sku" => "A-C306", "quantity" => 2}, {"sku" => "B-C306", "quantity" => 1}]
48
+ ```
49
+
24
50
 
25
- TODO: Write usage instructions here
51
+ ## C324s
52
+ As of v0.8.0, C324s will be treated differently. If there are any C324s that can exist, they will stay as C324s, and not converted to bottles.
53
+ eg.
54
+ In: 1x C312, 1x C312, 1x C306, 1x B306
55
+ Out: 1x C324, 1x B312
26
56
 
27
57
  ## Development
28
58
 
@@ -1,13 +1,30 @@
1
- require "date"
1
+ require 'logger'
2
+ require 'date'
3
+
2
4
  module Seshbot
3
5
  module Packing
4
6
  class Error < StandardError; end
5
-
6
7
  class << self
8
+ def logger
9
+ Logger.new(STDERR)
10
+ end
11
+
12
+ def filter_by_sku(items, sku_fragment, inverse: false)
13
+ items.select do |i|
14
+ matches = i['sku'].include?(sku_fragment)
15
+ inverse ? !matches : matches
16
+ end
17
+ end
18
+
19
+ def substitute_sku(items, from, to)
20
+ items.each { |i| i['sku'].gsub!(from, to) }
21
+ items
22
+ end
23
+
7
24
  def count_product_types(items)
8
25
  result = Hash.new(0)
9
26
  # create a hash were the keys are sku_fragment, and the values are the summed quantities
10
- items.each do |item|
27
+ items.each do |item|
11
28
  size = packaged_size(item["sku"])
12
29
  result[size] += item["quantity"]
13
30
  end
@@ -15,42 +32,85 @@ module Seshbot
15
32
  end
16
33
 
17
34
  def packaged_size(sku)
18
- # Takes an sku and returns the size
19
- result = sku[/(L\d{2}|U\d{2}|B\d{3}|C\d{3}|M\d{3}|P\d{2})/]
35
+ size_regex = /-([LUP]\d{2}|[BCM]\d{3})/
36
+ match = sku.match(size_regex)
37
+ raise "Cannot determine packaged size for SKU: #{sku}" if match.nil? || match.captures.empty?
38
+
39
+ # this is everything inside the (..) in the regex (everything except the leading '-')
40
+ result = match.captures[0]
41
+
20
42
  if result == "B502"
21
43
  result = "B306"
22
44
  end
23
45
  if result == "B504"
24
46
  result = "B312"
25
47
  end
26
- result.gsub!(/^C/, 'B')
48
+ raise "Cannot determine packaged size for SKU: #{sku}" if result.nil?
49
+
27
50
  result
28
51
  end
29
52
 
30
53
  def bundle_items(items, fulfilled_at: nil)
31
- if fulfilled_at
32
- bundling_start_date = DateTime.new(2020, 1, 14, 8, 0, 0, Time.new.zone)
33
- result = items.map { |item| { "sku" => item.sku, "quantity" => item.quantity, "price" => item.price } }
34
- return result if fulfilled_at < bundling_start_date # TODO remove bundling start date method
54
+ logger.debug "***** BUNDLING"
55
+ logger.debug "Bundling Items: #{items}. Fulfilled at: #{fulfilled_at || '(no fulfillment date)'}"
56
+ if !fulfilled_at.nil? && fulfilled_at < DateTime.new(2020, 1, 14, 8, 0, 0, Time.new.zone)
57
+ return items.map { |item| { 'sku' => item.sku, 'quantity' => item.quantity, 'price' => item.price } }
35
58
  end
36
59
 
37
- # get a hash of {pack_6: 5, pack_12: 1}
38
- product_type_counts = count_product_types(items)
60
+ # get the cans first, and separate them out
61
+ cans = filter_by_sku(items, '-C')
62
+ unless cans.empty?
63
+ product_type_counts = count_product_types(cans)
64
+ product_type_counts = unpack(product_type_counts)
65
+ product_type_counts = pack(product_type_counts)
66
+ new_can_items = product_type_counts.map { |k, v| { 'sku' => "BUND-#{k}", 'quantity' => v, 'price' => 0 } }
67
+ # separate out all the C324s
68
+ separated_c324s = filter_by_sku(new_can_items, '-C324')
69
+ logger.debug "Cans: #{cans}"
70
+ logger.debug " - Removed C324s: #{separated_c324s}"
71
+
72
+ remaining_cans = filter_by_sku(new_can_items, '-C324', inverse: true)
73
+ logger.debug " - Remaining skus: #{remaining_cans}"
74
+ end
75
+
76
+ # merge the remaining with the original leftover
77
+ non_cans = filter_by_sku(items, '-C', inverse: true)
78
+ remaining_cans ||= []
79
+ remaining_items = non_cans + remaining_cans
80
+ logger.debug "Substituting:"
81
+ logger.debug " - remaining items: #{remaining_items}"
39
82
 
83
+ # substitute C's for B's
84
+ substitute_sku(remaining_items, '-C', '-B')
85
+ logger.debug " - skus updated to: #{remaining_items}"
86
+
87
+ # get a hash of {pack_6: 5, pack_12: 1}
88
+ product_type_counts = count_product_types(remaining_items)
40
89
  # dismantle packages into individual units (e.g., {pack_6: 7})
41
90
  product_type_counts = unpack(product_type_counts)
42
-
43
91
  # repackage into the 'best' packaging we can figure out (e.g., {pack_12: 2})
44
92
  product_type_counts = pack(product_type_counts)
93
+ new_remaining_items = product_type_counts.map { |k,v| { "sku" => "BUND-#{k}", "quantity" => v, "price" => 0} }
45
94
 
46
- new_line_items = product_type_counts.map { |k,v| { "sku" => "BUND-#{k}", "quantity" => v, "price" => 0} }
47
- new_line_items
95
+ separated_c324s ||= []
96
+ merged_items = new_remaining_items + separated_c324s
97
+ logger.debug "Bundled result:"
98
+ logger.debug " - IN: #{items}"
99
+ logger.debug " - OUT: #{merged_items}"
100
+ merged_items
48
101
  end
49
102
 
50
103
 
51
104
  def unpack(product_type_counts)
52
- result = pack_single_step(product_type_counts, unpacking: true)
53
- result
105
+ final_result = product_type_counts
106
+
107
+ while true
108
+ new_result = pack_single_step(final_result, unpacking: true)
109
+ break if new_result == final_result
110
+
111
+ final_result = new_result
112
+ end
113
+ final_result
54
114
  end
55
115
 
56
116
  def pack_single_step(product_type_counts, unpacking:)
@@ -4,23 +4,53 @@ module Seshbot
4
4
  class << self
5
5
  def get_recipes
6
6
  {
7
- "twenty_four_pack_4"=>{
7
+ "bottles_twenty_four_pack_4"=>{
8
8
  "input_fragment"=>"B306",
9
9
  "input_quantity"=>4,
10
10
  "output_fragment"=>"B324",
11
11
  "output_quantity"=>1
12
12
  },
13
- "twenty_four_pack_3"=>{
13
+ "bottles_twenty_four_pack_3"=>{
14
14
  "input_fragment"=>"B306",
15
15
  "input_quantity"=>3,
16
16
  "output_fragment"=>"B318",
17
17
  "output_quantity"=>1
18
18
  },
19
- "twelve_pack"=>{
19
+ "bottles_twelve_pack"=>{
20
20
  "input_fragment"=>"B306",
21
21
  "input_quantity"=>2,
22
22
  "output_fragment"=>"B312",
23
23
  "output_quantity"=>1
24
+ },
25
+ "bottles_six_pack"=>{
26
+ "input_fragment"=>"B301",
27
+ "input_quantity"=>6,
28
+ "output_fragment"=>"B306",
29
+ "output_quantity"=>1
30
+ },
31
+ "cans_twenty_four_pack_4"=>{
32
+ "input_fragment"=>"C306",
33
+ "input_quantity"=>4,
34
+ "output_fragment"=>"C324",
35
+ "output_quantity"=>1
36
+ },
37
+ "cans_twenty_four_pack_3"=>{
38
+ "input_fragment"=>"C306",
39
+ "input_quantity"=>3,
40
+ "output_fragment"=>"C318",
41
+ "output_quantity"=>1
42
+ },
43
+ "cans_twelve_pack"=>{
44
+ "input_fragment"=>"C306",
45
+ "input_quantity"=>2,
46
+ "output_fragment"=>"C312",
47
+ "output_quantity"=>1
48
+ },
49
+ "cans_six_pack"=>{
50
+ "input_fragment"=>"C301",
51
+ "input_quantity"=>6,
52
+ "output_fragment"=>"C306",
53
+ "output_quantity"=>1
24
54
  }
25
55
  }
26
56
  end
@@ -1,5 +1,5 @@
1
1
  module Seshbot
2
2
  module Packing
3
- VERSION = "0.4.0"
3
+ VERSION = '0.8.2'
4
4
  end
5
5
  end
@@ -1,20 +1,20 @@
1
1
  require_relative 'lib/seshbot/packing/version'
2
2
 
3
3
  Gem::Specification.new do |spec|
4
- spec.name = "seshbot-packing"
4
+ spec.name = 'seshbot-packing'
5
5
  spec.version = Seshbot::Packing::VERSION
6
- spec.authors = ["Shaun"]
7
- spec.email = ["shaun@brewkeeper.com.au"]
8
-
6
+ spec.authors = ['Shaun']
7
+ spec.email = ['shaun@brewkeeper.com.au']
8
+ spec.license = 'MIT'
9
9
  spec.summary = %q{Receives, calculates, returns skus with new quantities}
10
- spec.homepage = "https://gitlab.com/brewkeeper/seshbot-packing"
10
+ spec.homepage = 'https://gitlab.com/brewkeeper/seshbot-packing'
11
11
  spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
12
12
 
13
13
  # spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
14
14
 
15
15
  spec.metadata["homepage_uri"] = spec.homepage
16
16
  spec.metadata["source_code_uri"] = "https://gitlab.com/brewkeeper/seshbot-packing"
17
- spec.metadata["changelog_uri"] = "https://gitlab.com/brewkeeper/seshbot-packing/~blob/master/CHANGELOG.md"
17
+ spec.metadata["changelog_uri"] = "https://gitlab.com/brewkeeper/seshbot-packing/-/blob/master/CHANGELOG.md"
18
18
 
19
19
  # Specify which files should be added to the gem when it is released.
20
20
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: seshbot-packing
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.8.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shaun
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-11-15 00:00:00.000000000 Z
11
+ date: 2021-03-16 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email:
@@ -19,6 +19,7 @@ extra_rdoc_files: []
19
19
  files:
20
20
  - ".gitignore"
21
21
  - ".travis.yml"
22
+ - CHANGELOG.md
22
23
  - CODE_OF_CONDUCT.md
23
24
  - Gemfile
24
25
  - Gemfile.lock
@@ -32,11 +33,12 @@ files:
32
33
  - lib/seshbot/packing/version.rb
33
34
  - seshbot-packing.gemspec
34
35
  homepage: https://gitlab.com/brewkeeper/seshbot-packing
35
- licenses: []
36
+ licenses:
37
+ - MIT
36
38
  metadata:
37
39
  homepage_uri: https://gitlab.com/brewkeeper/seshbot-packing
38
40
  source_code_uri: https://gitlab.com/brewkeeper/seshbot-packing
39
- changelog_uri: https://gitlab.com/brewkeeper/seshbot-packing/~blob/master/CHANGELOG.md
41
+ changelog_uri: https://gitlab.com/brewkeeper/seshbot-packing/-/blob/master/CHANGELOG.md
40
42
  post_install_message:
41
43
  rdoc_options: []
42
44
  require_paths: