magelex 0.1.2 → 0.1.3

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
  SHA1:
3
- metadata.gz: 032119d67096a3213c374635a710796c3732ea30
4
- data.tar.gz: 9451eb9eba3fddd6bf7d6a12d0af3e554dc53222
3
+ metadata.gz: de06fc18a480aa62a7229eb17dd1039513345fd3
4
+ data.tar.gz: 2c47cad6e86d40495ddd67c32a6533ab5e2d4e77
5
5
  SHA512:
6
- metadata.gz: a4e4e62c48ffa8fca02d241e5a953b191cf8fd3e6a7ad29769140aef3a0eae03b681822fa23a251b1aa33aac76afc108a798f97865d44eed6a606fce294e1a0d
7
- data.tar.gz: cf520013296e47b60ac59790e43b677337673b7bb65bd18c292a3af5b9e95fddf60a0411492af011d0cc4cbaf8587e09a4c53961955cba9e46cc431ad694bbd3
6
+ metadata.gz: 07b259c2f497d0345b89ec13819b6717e2c64293bc212d7fe1a805214270aacf77f80fec7d0478462c2002cfd2a55b81741d6f171a971933aa1e924846873026
7
+ data.tar.gz: 7efba0085ec0ecfe48693fd2b0e71af54f7f4351769d943338d9bd8e39a2a339af03c9ab7e199b12f5ddd748318300778f5b8ccca1e43a543c78e71be301baea
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  *This README reflects (more or less) the current development state, for documentation of a given version, see the README shipped with that gem (or respective tag on github).*
4
4
 
5
- Magelex takes Magento data and presents it in a format that Lexware can read.
5
+ Magelex takes Magento online shop order data and presents it in a format that Lexware can read to model open positions.
6
6
 
7
7
  Aim is to manage the cash flow in Lexware.
8
8
 
@@ -16,7 +16,7 @@ Install it yourself as:
16
16
 
17
17
  ## Assumptions
18
18
 
19
- Customer accounts are hard coded. Database access necessary for date corrections.
19
+ Customer accounts are hard coded. Database access is necessary for date corrections (but can be skipped).
20
20
 
21
21
  ## Usage
22
22
 
@@ -34,9 +34,9 @@ Call `magelex --help` to get a basic idea:
34
34
  --version Show version and exit.
35
35
 
36
36
 
37
- By default, `magelex` will log to `STDERR`, put you can pass the path to a log file.
37
+ By default, `magelex` will log to `STDERR`, but you can pass the path to a log file.
38
38
 
39
- It consumes a single file (given as argument, as in `magelex magento_orders.csv`). `magelex` will create a file with same filename in the path `lexware` (can be changed with the `--out-dir` switch).
39
+ It consumes a single file (given as argument, as in `magelex magento_orders.csv`) or a directory of files. `magelex` will create a file with same filename in the path `lexware` (can be changed with the `--out-dir` option).
40
40
 
41
41
  ### Configuration
42
42
 
@@ -48,9 +48,14 @@ If no database queries should be done, invoke with `--skip-db`.
48
48
 
49
49
  Call `magelex --help` to get a basic idea.
50
50
 
51
- ## TODOs
51
+ ## Documentation of process
52
52
 
53
- - Respect Discounts (item and totals).
53
+ `bin/magelex` will read in a CSV file with orders exported by magento (`Magelex::MagentoCSV`). In this file, one row accounts for one 'order item'. Items are added up to form a `Magelex::LexwareBill`. Adding Items to a `LexWareBill` collects the brutto values separated by tax. For this, the tax category (0%, 7% or 19%) has to be guessed (`Magelex::TaxGuesser`).
54
+
55
+ Result of this processing are a number of `LexwareBill`s.
56
+ Swiss orders require some special attention, so steps are undertaken to adjust these to reality. Afterwards, the shipping costs can be included.
57
+
58
+ Finally the `LexwareBill`s that conform to the rules (`LexwareBill#check`) can be exported to be imported to Lexware (`Magelex::LexwareCSV`).
54
59
 
55
60
  ## Development
56
61
 
data/bin/magelex CHANGED
@@ -37,7 +37,7 @@ optparse = OptionParser.new do |opts|
37
37
  end.parse!
38
38
 
39
39
  if ARGV.length != 1
40
- STDERR.puts "Need an argument (directory or file)"
40
+ STDERR.puts "Need a single argument (directory or file)"
41
41
  exit 1
42
42
  end
43
43
 
@@ -50,7 +50,7 @@ Magelex.logger.formatter = proc { |severity, datetime, progname, msg|
50
50
  }
51
51
 
52
52
  def main options
53
- Magelex.logger.info("Started")
53
+ Magelex.logger.info("Started (version #{Magelex::VERSION})")
54
54
 
55
55
  Dir.mkdir options[:out_dir] rescue {}
56
56
  outdir = Pathname.new(options[:out_dir]).realpath
data/bin/magelex_debug ADDED
@@ -0,0 +1,75 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "magelex"
4
+
5
+ require 'yaml'
6
+ require 'optparse'
7
+ require 'terminal-table'
8
+
9
+ options = {}
10
+
11
+ optparse = OptionParser.new do |opts|
12
+ opts.banner = "Usage: #{$PROGRAM_NAME} DIR_OR_FILE"
13
+ opts.separator ""
14
+ opts.separator "Debug magelex im- and export"
15
+ opts.separator ""
16
+
17
+ opts.on('-v', '--verbose', 'Run verbosely') do |o|
18
+ options[:verbose] = o
19
+ end
20
+ opts.on('-s', '--skip-db', 'Do not update dates from mysql database.') do |o|
21
+ options[:skipdb] = o
22
+ end
23
+ opts.on_tail('--version', 'Show version and exit.') do
24
+ puts "Magelex #{Magelex::VERSION}"
25
+ exit 0
26
+ end
27
+ opts.on('-h', '--help', 'Show this help and exit.') do
28
+ puts opts
29
+ exit 0
30
+ end
31
+ end.parse!
32
+
33
+ if ARGV.length != 1
34
+ STDERR.puts "Need an argument (directory or file)"
35
+ exit 1
36
+ end
37
+
38
+ Magelex.logger = Logger.new(options[:log_file] ? options[:log_file] : STDERR)
39
+
40
+ Magelex.logger.level = options[:verbose] ? Logger::DEBUG : Logger::INFO
41
+ Magelex.logger.datetime_format = "%Y-%m-%d %H:%M:%S"
42
+ Magelex.logger.formatter = proc { |severity, datetime, progname, msg|
43
+ "#{severity} - #{datetime} - #{msg}\n"
44
+ }
45
+
46
+ def main options
47
+ Magelex.logger.info("Started")
48
+
49
+ # Import/Read file.
50
+ bills = Magelex::MagentoCSV.read ARGV[0]
51
+ bills.each(&:swissify)
52
+ bills.each(&:process_shipping_costs)
53
+ bill_rows = bills.map do |bill|
54
+ [
55
+ bill.order_nr,
56
+ bill.swiss? ? 'Y' : 'N',
57
+ bill.total,
58
+ "%.3f" %bill.total_0, "%.3f" % bill.total_7, "%.3f" % bill.total_19,
59
+ "%.3f" % bill.tax_7, "%.3f" % bill.tax_19,
60
+ "%.3f" % bill.shipping_cost, "%.3f" % bill.check_diff, bill.check ? "Y" : "N"
61
+ ]
62
+ end
63
+
64
+ t = Terminal::Table.new(headings: ["nr", "S?","total_b",
65
+ "total0", "total7", "total19",
66
+ "tax7", "tax19",
67
+ "ship", "diff", "C"],
68
+ rows: bill_rows)
69
+
70
+ puts t
71
+ puts Magelex::LexwareCSV::render bills
72
+ Magelex.logger.info("Finished")
73
+ end
74
+
75
+ main options
@@ -0,0 +1,38 @@
1
+ module Magelex
2
+ module BillModifier
3
+ def self.process bill
4
+ # 'Trick' around with bill
5
+ swissify bill
6
+ process_shipping_costs bill
7
+ adjust_order_number bill
8
+ end
9
+
10
+ def self.process_shipping_costs bill
11
+ if bill.swiss?
12
+ bill.total_0 += LexwareBill.floor2(bill.shipping_cost)
13
+ else
14
+ bill.tax_19 += bill.shipping_cost * 0.19
15
+ bill.total_19 += LexwareBill.floor2(bill.shipping_cost * 1.19)
16
+ end
17
+ end
18
+
19
+ def self.adjust_order_number bill
20
+ bill.order_nr.to_s.gsub!(/^e-/, '')
21
+ end
22
+
23
+ # total0 consumes total and resets others, if check passes
24
+ # shipping costs should be consumed before
25
+ # this has to be layed out in a graph or documented properly
26
+ # (what happens when)
27
+ def self.swissify bill
28
+ return if !bill.swiss?
29
+
30
+ bill.incorrect_tax += (bill.total_19 - bill.total_19 / 1.19)
31
+ bill.incorrect_tax += (bill.total_7 - bill.total_7 / 1.07)
32
+ bill.total_0 += (bill.total_19 / 1.19)
33
+ bill.total_19 = 0
34
+ bill.total_0 += (bill.total_7 / 1.07)
35
+ bill.total_7 = 0
36
+ end
37
+ end
38
+ end
@@ -12,7 +12,8 @@ module Magelex
12
12
  10000 + ord * 100
13
13
  end
14
14
 
15
- # get tax for :total_0, :total_7 or :total_19
15
+ # Get account number for
16
+ # :incorrect_tax, :total_0, :total_7 or :total_19
16
17
  def self.for(bill, tax_kind)
17
18
  if tax_kind == :total_0
18
19
  return for_0 bill
@@ -20,6 +21,8 @@ module Magelex
20
21
  return for_7 bill
21
22
  elsif tax_kind == :total_19
22
23
  return for_19 bill
24
+ elsif tax_kind == :incorrect_tax
25
+ return for_incorrect_tax bill
23
26
  else
24
27
  raise "unknown tax_kind (#{tax_kind})"
25
28
  end
@@ -36,5 +39,9 @@ module Magelex
36
39
  def self.for_0 bill
37
40
  '8120'
38
41
  end
42
+
43
+ def self.for_incorrect_tax bill
44
+ '1783'
45
+ end
39
46
  end
40
47
  end
@@ -7,20 +7,23 @@ module Magelex
7
7
  'NL','AT','PL','PT','RO','SI','SK','FI','SE','UK']
8
8
 
9
9
  attr_accessor :order_nr, :customer_name, :country_code,
10
- :date, :status, :shipping_cost, :total, :total_0, :total_7, :total_19, :has_problems
10
+ :date, :status, :shipping_cost, :total, :total_0, :total_7, :total_19, :has_problems, :tax_7, :tax_19, :incorrect_tax
11
11
 
12
12
  def initialize values={}
13
13
  @total_0, @total_7, @total_19, @total = 0, 0, 0, 0
14
14
  @customer_name = values.delete(:customer_name) || ""
15
15
  @order_nr = values.delete(:order_nr) || nil
16
- @date = values.delete(:date) || nil
16
+ @date = values.delete(:date) || nil
17
17
  @total = values.delete(:total) || 0
18
- @total_0 = values.delete(:total_0) || 0
19
- @total_7 = values.delete(:total_7) || 0
18
+ @total_0 = values.delete(:total_0) || 0
19
+ @total_7 = values.delete(:total_7) || 0
20
20
  @total_19 = values.delete(:total_19) || 0
21
+ @tax_7 = values.delete(:tax_7) || 0
22
+ @tax_19 = values.delete(:tax_19) || 0
23
+ @incorrect_tax = values.delete(:incorrect_tax) || 0
21
24
  @status = values.delete(:status) || nil
22
25
  @shipping_cost = values.delete(:shipping_cost) || nil
23
- @country_code = values.delete(:country_code) || nil
26
+ @country_code = values.delete(:country_code) || nil
24
27
  @has_problems = false
25
28
  if !values.empty?
26
29
  raise "Unknown values for bill: #{values.inspect}"
@@ -31,17 +34,35 @@ module Magelex
31
34
  @country_code == 'CH'
32
35
  end
33
36
 
37
+ # Add item values to corresponding total_ and tax_ attributes
38
+ # depending on discount, include or exclude taxes.
34
39
  def add_item amount, tax, name, discount=0
35
40
  begin
36
41
  case TaxGuess.guess(amount, tax)
37
42
  when :tax0
38
- @total_0 += amount.round(2)# - discount.round(0)
43
+ @total_0 += amount.round(2)
39
44
  when :tax7
40
- @total_7 += amount.round(2)# - discount.round(0)
45
+ if discount != 0
46
+ @total_7 += (amount.round(2) * 1.07)
47
+ else
48
+ @total_7 += amount.round(2)
49
+ end
50
+ @tax_7 += tax
41
51
  when :tax19
42
- @total_19 += amount.round(2)# - discount.round(0)
52
+ if discount != 0
53
+ @total_19 += (amount.round(2) * 1.18)
54
+ else
55
+ @total_19 += amount.round(2)
56
+ end
57
+ if swiss?
58
+ Magelex::logger.info("19% Tax Item in swiss order: #{@order_nr}: #{name}")
59
+ end
60
+ @tax_19 += tax
61
+ when :empty_item
62
+ Magelex::logger.debug("Empty item: '#{name}' #{amount}, tax: #{tax}")
43
63
  end
44
- rescue
64
+ rescue RuntimeError
65
+ Magelex::logger.warn("Unguessable tax (#{@order_nr}: #{name} #{amount}/#{tax})")
45
66
  @has_problems = true
46
67
  end
47
68
  end
@@ -54,33 +75,21 @@ module Magelex
54
75
  @@EU_CODES.include? @country_code
55
76
  end
56
77
 
78
+ def check_diff
79
+ @total.round(2) - (@total_0.round(2) + @total_7.round(2) + @total_19.round(2) + @incorrect_tax.round(2)).round(2)
80
+ end
81
+
57
82
  def check
58
- @has_problems == false && @total > 0 && @total.round(2) == (@total_0.round(2) + @total_7.round(2) + @total_19.round(2)).round(2)
83
+ @has_problems == false && @total > 0 && check_diff == 0
59
84
  end
60
85
 
61
86
  def self.floor2 value
62
87
  (value * 100).to_i / 100.0
63
88
  end
64
-
65
- def consume_shipping_cost
66
- if swiss?
67
- @total_0 += LexwareBill.floor2(@shipping_cost)
68
- else
69
- @total_19 += LexwareBill.floor2(@shipping_cost * 1.19)
70
- end
71
- @shipping_cost = 0
72
89
  end
73
90
 
74
91
  def complete?
75
92
  @status == "complete"
76
93
  end
77
-
78
- def swissify
79
- return if !swiss?
80
- @total_0 += @total_19
81
- @total_19 = 0
82
- @total_0 += @total_7
83
- @total_7 = 0
84
- end
85
94
  end
86
95
  end
@@ -16,7 +16,7 @@ module Magelex
16
16
  Magelex::AccountNumber.for_customer(bill),
17
17
  0]
18
18
  # subs
19
- [:total_0, :total_7, :total_19].each do |part|
19
+ [:total_0, :total_7, :total_19, :incorrect_tax].each do |part|
20
20
  if (amount = bill.send(part)) != 0
21
21
  rows << [
22
22
  bill.date.strftime("%d.%m.%Y"),
@@ -3,7 +3,7 @@ require 'csv'
3
3
  module Magelex
4
4
  module MagentoCSV
5
5
  MONEY_FIELDS = ['Order Shipping', 'Order Grand Total',
6
- 'Item Total', 'Item Tax']
6
+ 'Item Total', 'Item Tax', 'Item Discount']
7
7
 
8
8
  CSV::Converters[:german_money_amount] = lambda do |value, info|
9
9
  if MONEY_FIELDS.include? info[:header]
@@ -39,6 +39,13 @@ module Magelex
39
39
  bill.status = row['Order Status']
40
40
 
41
41
  bill.shipping_cost = row['Order Shipping']
42
+ if bill.shipping_cost == 12.6
43
+ Magelex::logger.info "Correcting shipping cost of #{bill.order_nr} (12.6 -> 15 / 1.19 €)"
44
+ bill.shipping_cost = 15 / 1.19
45
+ elsif bill.shipping_cost == 4.15
46
+ Magelex::logger.info "Correcting shipping cost of #{bill.order_nr} (4.15 -> 4.95 / 1.19 €)"
47
+ bill.shipping_cost = 4.95 / 1.19
48
+ end
42
49
  bill.total = row['Order Grand Total']
43
50
  bill
44
51
  end
@@ -57,7 +64,8 @@ module Magelex
57
64
 
58
65
  current_bill.add_item(row['Item Total'],
59
66
  row['Item Tax'],
60
- row['Item Name'])
67
+ row['Item Name'],
68
+ row['Item Discount'])
61
69
 
62
70
  if !bills.include? (current_bill)
63
71
  bills << current_bill
@@ -1,16 +1,34 @@
1
1
  module Magelex
2
2
  module TaxGuess
3
+ # Guesses the tax category.
4
+ # Question is: how many percent of total is tax_amount.
3
5
  def self.guess(total, tax_amount)
4
- if tax_amount == 0
5
- :tax0
6
- elsif (total - total/1.06) <= tax_amount && (total - total/1.08) >= tax_amount
7
- :tax7
8
- elsif (total - total/1.18) <= tax_amount && (total - total/1.20) >= tax_amount
9
- :tax19
10
- else
6
+ if total == 0 && tax_amount == 0
7
+ return :empty_item
8
+ end
9
+ if total == 0
11
10
  raise "TaxGuess: Cannot guess tax of "\
12
11
  "#{total}/#{tax_amount} (#{total - total/tax_amount})"
13
12
  end
13
+
14
+ # net: netto, gro: gross/brutto
15
+ # gro_price = net_price + taxes ## (1: net_price = gro_price - taxes)
16
+ # gro_price = net_price + net_price * tax_perce
17
+ # tax_perce = (gro_price - net_price) / net_price
18
+ # tax_perce = gro_price / net_price - 1 ## (see 1)
19
+ percentage = total.to_f / (total - tax_amount) - 1
20
+
21
+ case percentage
22
+ when -0.01..0.01
23
+ :tax0
24
+ when 0.06..0.09
25
+ :tax7
26
+ when 0.16..0.20
27
+ :tax19
28
+ else
29
+ raise "TaxGuess: Cannot guess tax of "\
30
+ "#{total}/#{tax_amount} (#{total - total/tax_amount}) - #{tax_amount/total if total != 0}"
31
+ end
14
32
  end
15
33
  end
16
34
  end
@@ -1,3 +1,3 @@
1
1
  module Magelex
2
- VERSION = "0.1.2".freeze
2
+ VERSION = "0.1.3".freeze
3
3
  end
data/lib/magelex.rb CHANGED
@@ -5,12 +5,13 @@ require 'magelex/lexware_csv'
5
5
  require 'magelex/tax_guess'
6
6
  require 'magelex/lexware_account'
7
7
  require 'magelex/magento_mysql'
8
+ require 'magelex/bill_modifier'
8
9
 
9
10
  require 'logger'
10
11
 
11
12
  module Magelex
12
13
  def self.logger
13
- @logger ||= Logger.new
14
+ @logger ||= Logger.new STDERR
14
15
  end
15
16
  def self.logger= logger
16
17
  @logger = logger
@@ -24,15 +25,17 @@ module Magelex
24
25
  if !bill.complete?
25
26
  Magelex.logger.info("Skip order #{bill.order_nr} (incomplete: #{bill.status})")
26
27
  else # complete!
27
- bill.consume_shipping_cost
28
- bill.swissify
28
+ Magelex::BillModifier.process bill
29
29
  if !bill.check
30
- Magelex.logger.info("Skip order #{bill.order_nr}#{bill.swiss? ? ' (swiss)' : ''}")
30
+ Magelex.logger.info("Skip order #{bill.order_nr}#{bill.swiss? ? ' (swiss)' : ''} #{bill.has_problems ? ' (broken item)': '' }")
31
31
  Magelex.logger.info(" (totals do not match #{bill.total} != "\
32
32
  "(0: #{bill.total_0} + 7: #{bill.total_7} "\
33
33
  "+ 19: #{bill.total_19} "\
34
34
  "= #{bill.total_0 + bill.total_7 + bill.total_19})")
35
35
  else
36
+ if bill.swiss?
37
+ Magelex.logger.info("#{bill.order_nr}: swiss")
38
+ end
36
39
  Magelex.logger.debug("Handle #{bill.order_nr}")
37
40
  bills_export << bill
38
41
  end
data/magelex.gemspec CHANGED
@@ -19,6 +19,7 @@ Gem::Specification.new do |spec|
19
19
  spec.require_paths = ["lib"]
20
20
 
21
21
  spec.add_dependency 'mysql2', '~> 0.4'
22
+ spec.add_dependency 'terminal-table', '~> 1.5'
22
23
 
23
24
  spec.add_development_dependency "bundler", "~> 1.11"
24
25
  spec.add_development_dependency "rake", "~> 10.0"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: magelex
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Felix Wolfsteller
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-02-10 00:00:00.000000000 Z
11
+ date: 2016-02-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: mysql2
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0.4'
27
+ - !ruby/object:Gem::Dependency
28
+ name: terminal-table
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.5'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.5'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: bundler
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -71,6 +85,7 @@ email:
71
85
  - felix.wolfsteller@gmail.com
72
86
  executables:
73
87
  - magelex
88
+ - magelex_debug
74
89
  extensions: []
75
90
  extra_rdoc_files: []
76
91
  files:
@@ -81,7 +96,9 @@ files:
81
96
  - README.md
82
97
  - Rakefile
83
98
  - bin/magelex
99
+ - bin/magelex_debug
84
100
  - lib/magelex.rb
101
+ - lib/magelex/bill_modifier.rb
85
102
  - lib/magelex/lexware_account.rb
86
103
  - lib/magelex/lexware_bill.rb
87
104
  - lib/magelex/lexware_csv.rb