sunat_books 0.0.1 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +3 -0
  3. data/.rubocop.yml +32 -0
  4. data/.rvmrc +1 -0
  5. data/.travis.yml +7 -0
  6. data/Gemfile +2 -4
  7. data/Makefile +2 -1
  8. data/README.mkd +18 -8
  9. data/lib/sunat_books.rb +9 -4
  10. data/lib/sunat_books/common_utils.rb +9 -0
  11. data/lib/sunat_books/csv/base.rb +63 -0
  12. data/lib/sunat_books/csv/option_error.rb +8 -0
  13. data/lib/sunat_books/errors/invalid_layout_error.rb +8 -0
  14. data/lib/sunat_books/pdf/base.rb +134 -0
  15. data/lib/sunat_books/pdf/buys.rb +36 -0
  16. data/lib/sunat_books/pdf/count_sum.rb +24 -0
  17. data/lib/sunat_books/pdf/diary_entries.rb +112 -0
  18. data/lib/{books → sunat_books/pdf}/layouts/buys.yml +0 -0
  19. data/lib/{books → sunat_books/pdf}/layouts/sales.yml +2 -2
  20. data/lib/sunat_books/pdf/locale.rb +6 -0
  21. data/lib/sunat_books/pdf/locales/es.yml +65 -0
  22. data/lib/sunat_books/pdf/page.rb +49 -0
  23. data/lib/sunat_books/pdf/pages_utils.rb +85 -0
  24. data/lib/sunat_books/pdf/sales.rb +35 -0
  25. data/lib/sunat_books/pdf/simplified_diary.rb +95 -0
  26. data/lib/sunat_books/pdf/trading_book.rb +50 -0
  27. data/lib/sunat_books/pdf/utils.rb +103 -0
  28. data/lib/sunat_books/ple/base.rb +49 -0
  29. data/lib/sunat_books/ple/buys.rb +14 -0
  30. data/lib/sunat_books/ple/layouts/buys-8.1.yml +41 -0
  31. data/lib/sunat_books/ple/layouts/buys-8.2.yml +36 -0
  32. data/lib/sunat_books/ple/layouts/buys-8.3.yml +31 -0
  33. data/lib/sunat_books/ple/layouts/sales-14.1.yml +34 -0
  34. data/lib/{ple_books/layouts/sales.yml → sunat_books/ple/layouts/sales-14.2.yml} +9 -9
  35. data/lib/sunat_books/ple/sales.rb +14 -0
  36. data/lib/sunat_books/ple/utils.rb +69 -0
  37. data/sunat_books.gemspec +16 -7
  38. data/test/csv/base_test.rb +46 -0
  39. data/test/fixtures/base.rb +11 -0
  40. data/test/fixtures/company.rb +5 -0
  41. data/test/fixtures/ticket.rb +4 -9
  42. data/test/helper.rb +11 -13
  43. data/test/pdf/base_test.rb +6 -0
  44. data/test/pdf/buys_test.rb +28 -0
  45. data/test/pdf/page_test.rb +24 -0
  46. data/test/pdf/pages_utils_test.rb +108 -0
  47. data/test/pdf/sales_test.rb +16 -0
  48. data/test/pdf/utils_test.rb +48 -0
  49. data/test/ple/base_test.rb +14 -0
  50. data/test/ple/buys_test.rb +59 -0
  51. data/test/ple/sales_test.rb +20 -0
  52. data/test/ple/utils_test.rb +54 -0
  53. metadata +135 -37
  54. data/Gemfile.lock +0 -48
  55. data/lib/books/base.rb +0 -212
  56. data/lib/books/buys.rb +0 -120
  57. data/lib/books/count_sum.rb +0 -20
  58. data/lib/books/sales.rb +0 -114
  59. data/lib/books/simplified_diary.rb +0 -149
  60. data/lib/ple_books/base.rb +0 -60
  61. data/lib/ple_books/book_codes.csv +0 -48
  62. data/lib/ple_books/buys.rb +0 -33
  63. data/lib/ple_books/layouts/buys.yml +0 -34
  64. data/lib/ple_books/sales.rb +0 -14
  65. data/test/ple_books_test.rb +0 -96
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: e08f90e66a2cb035ea0747c2e90ab0717fe01fdd
4
- data.tar.gz: 66e781fabac6fb3126919c07e661f17f4207b42b
2
+ SHA256:
3
+ metadata.gz: 1d074f71e576ad2528da91297e05d1acfd14480829e7b77db8c27b3a13bd3943
4
+ data.tar.gz: cdc8de2ebedfe75f5e9169d82530b4c7d1b50c37909c1e2c8aae2f1f4d29104d
5
5
  SHA512:
6
- metadata.gz: 51e5c9ae81c79fa4176aa6548f4dbbf11f4ee168e653685a79fb2d273436edf34fce09043265bdb2bb87f68bbb4083939777144f959a4b3b42c3e90356b2e544
7
- data.tar.gz: a5bdeb8c98c009f53f7beff00ed0f7b0228087c9437c438f1aba94e2dedd477a3b7a0f97e94540ba99f5e2853e957a0973597b4b00ef03c544694715fadfde44
6
+ metadata.gz: f5f87bcfa3ff0da39d7885cd8ddb5ab3d4aa2c607905e589f2c0ae2d9022f3dd7955deeae45bdc11c045f92719a3e4e4b17174059ee9d520916f6dbb6d6ccfeb
7
+ data.tar.gz: a42ef020ec2668c7bdbc674e44bbcc8bbfbed4240cfb27a591de483c61c2749d5cebbc26180d1dea3eb71de81a3bf0ca343982b1bfdecc06e89551ce995d8103
data/.gitignore CHANGED
@@ -1,2 +1,5 @@
1
1
  *.swp
2
2
  tmp
3
+ *.gem
4
+ *.csv
5
+ Gemfile.lock
@@ -0,0 +1,32 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.5
3
+ NewCops: enable
4
+ Style/Documentation:
5
+ Enabled: false
6
+ Style/StringLiterals:
7
+ EnforcedStyle: double_quotes
8
+ SupportedStyles:
9
+ - single_quotes
10
+ - double_quotes
11
+
12
+ Style/StringLiteralsInInterpolation:
13
+ EnforcedStyle: single_quotes
14
+ SupportedStyles:
15
+ - single_quotes
16
+ - double_quotes
17
+
18
+ Style/MixinUsage:
19
+ Exclude:
20
+ - 'test/**/*'
21
+
22
+ Style/HashEachMethods:
23
+ Enabled: true
24
+
25
+ Style/HashTransformKeys:
26
+ Enabled: true
27
+
28
+ Style/HashTransformValues:
29
+ Enabled: true
30
+
31
+ Lint/MissingSuper:
32
+ Enabled: false
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm --create use 2.6@sunat_books
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.5
4
+ - 2.6
5
+ - 2.7
6
+ script:
7
+ - make test
data/Gemfile CHANGED
@@ -1,7 +1,5 @@
1
- source 'https://rubygems.org'
1
+ # frozen_string_literal: true
2
2
 
3
- ruby '2.3.1'
3
+ source "https://rubygems.org"
4
4
 
5
5
  gemspec
6
-
7
- gem 'pry'
data/Makefile CHANGED
@@ -4,4 +4,5 @@ console:
4
4
  irb -Ilib -rsunat_books
5
5
 
6
6
  test:
7
- cutest test/*.rb
7
+ bundle exec cutest test/*.rb test/**/*.rb
8
+ bundle exec rubocop
data/README.mkd CHANGED
@@ -1,5 +1,9 @@
1
1
  # Sunat Books
2
- A ruby gem for get accounting books for [SUNAT](https://www.sunat.gob.pe)
2
+ [![Gem Version][rubygems-image]][rubygems-url]
3
+ [![Build Status][travis-image]][travis-url]
4
+ [![Code Climate][code-climate-image]][code-climate-url]
5
+
6
+ > A ruby gem to get accounting books for [SUNAT](https://www.sunat.gob.pe)
3
7
 
4
8
  ## Install
5
9
  You can install via
@@ -20,18 +24,24 @@ require 'sunat_books'
20
24
  to get a pdf format
21
25
 
22
26
  ```ruby
23
- pdf = Books::Buys.new(company, tickets, view_context, month, year)
27
+ pdf = SunatBooks::Pdf::Buys.new(company, tickets, month, year)
24
28
  pdf.render
25
29
  ```
26
30
 
27
31
  to get the txt file for electronic books
28
32
 
29
33
  ```ruby
30
- ple = PleBooks::Buys.new(ruc, tickets, month, year)
34
+ ple = SunatBooks::Ple::Buys.new(ruc, tickets, month, year, options)
31
35
  ```
32
36
 
33
-
34
- https://github.com/rails/rails/blob/master/activesupport/lib/active_support/core_ext/numeric/conversions.rb
35
-
36
- get support for otf font in prawn
37
- https://github.com/prawnpdf/prawn/issues/893
37
+ Currently allowed options are:
38
+ - `yml`: path location to a custom layout file
39
+ - `layout`: a hash to define method names to replace in layout
40
+ - `book_format`: book format to use
41
+
42
+ [rubygems-image]: https://badge.fury.io/rb/sunat_books.svg
43
+ [rubygems-url]: https://badge.fury.io/rb/sunat_books
44
+ [travis-image]: https://travis-ci.org/ccarruitero/sunat_books.svg?branch=master
45
+ [travis-url]: https://travis-ci.org/ccarruitero/sunat_books
46
+ [code-climate-image]: https://codeclimate.com/github/ccarruitero/sunat_books/badges/gpa.svg
47
+ [code-climate-url]: https://codeclimate.com/github/ccarruitero/sunat_books
@@ -1,4 +1,9 @@
1
- require 'books/buys'
2
- require 'books/sales'
3
- require 'books/simplified_diary'
4
- require 'ple_books/buys'
1
+ # frozen_string_literal: false
2
+
3
+ require "sunat_books/pdf/buys"
4
+ require "sunat_books/pdf/sales"
5
+ require "sunat_books/pdf/simplified_diary"
6
+ require "sunat_books/ple/buys"
7
+ require "sunat_books/ple/sales"
8
+ require "sunat_books/csv/base"
9
+ require "sunat_books/errors/invalid_layout_error"
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SunatBooks
4
+ module CommonUtils
5
+ def available_value?(source, attribute)
6
+ source.respond_to?(attribute) ? source.send(attribute) : ""
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "csv"
4
+ require_relative "option_error"
5
+ require_relative "../common_utils"
6
+
7
+ module SunatBooks
8
+ module Csv
9
+ class Base
10
+ include SunatBooks::CommonUtils
11
+
12
+ attr_accessor :file
13
+
14
+ def initialize(tickets, options = {})
15
+ # options
16
+ # - layout => Array of strings used to get data for csv
17
+ # - filename
18
+ raise SunatBooks::Csv::OptionError, "Layout option is required" if options[:layout].nil?
19
+
20
+ filename = options[:filename] || "#{tmp_path}book.csv"
21
+ fields = options[:layout]
22
+ get_file(filename, fields, tickets)
23
+ end
24
+
25
+ def get_file(filename, fields, tickets)
26
+ send("file=", filename)
27
+ File.exist?(filename) ? File.delete(filename) : nil
28
+ FileUtils.touch(filename)
29
+ append_headers(filename, fields)
30
+ append_data(tickets, filename, fields)
31
+ end
32
+
33
+ def tmp_path
34
+ dir = File.dirname(__FILE__)
35
+ tmp_path = "#{dir}/tmp/"
36
+ Dir.mkdir(tmp_path) unless Dir.exist?(tmp_path)
37
+ tmp_path
38
+ end
39
+
40
+ def append_headers(filename, fields)
41
+ append_to_csv(filename, fields, "w+")
42
+ end
43
+
44
+ def append_data(tickets, filename, fields)
45
+ tickets&.each do |ticket|
46
+ data = []
47
+ fields&.each do |field|
48
+ data << available_value?(ticket, field)
49
+ end
50
+ append_to_csv(filename, data, "a+")
51
+ end
52
+ end
53
+
54
+ def append_to_csv(filename, data, mode)
55
+ return if data.nil?
56
+
57
+ CSV.open(filename, mode) do |csv|
58
+ csv << data
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SunatBooks
4
+ module Csv
5
+ class OptionError < StandardError
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SunatBooks
4
+ module Errors
5
+ class InvalidLayoutError < StandardError
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,134 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "prawn"
4
+ require "prawn/table"
5
+ require "yaml"
6
+ require_relative "utils"
7
+ require_relative "locale"
8
+
9
+ module SunatBooks
10
+ module Pdf
11
+ class Base < Prawn::Document
12
+ include Utils
13
+ include Prawn::Table::Interface
14
+
15
+ def sub_head(hash, book_name, blayout)
16
+ arr, current_key = nil
17
+ hash.each do |key, value|
18
+ k = I18n.t("books.#{book_name}.#{key}").mb_chars.upcase.to_s
19
+ v = value.collect do |s|
20
+ I18n.t("books.#{book_name}.#{s}").mb_chars.upcase.to_s
21
+ end
22
+ arr = [[{ content: k, colspan: value.length }], v]
23
+ current_key = key
24
+ end
25
+
26
+ sub_head_table(blayout["widths"], arr, current_key)
27
+ end
28
+
29
+ def sub_head_table(widths, arr, key)
30
+ column_widths = get_column_widths(widths, key)
31
+ options = sub_head_options(column_widths)
32
+ make_table(arr, options)
33
+ end
34
+
35
+ def sub_head_options(column_widths)
36
+ options = { cell_style: {
37
+ borders: [], size: 5, align: :center, padding: 1
38
+ } }
39
+ add_widths(column_widths, options, 22)
40
+ options
41
+ end
42
+
43
+ def book_title(title)
44
+ text title, align: :center, size: 8
45
+ end
46
+
47
+ def book_header(period, ruc, name, title = nil)
48
+ move_down 5
49
+ txt name.to_s.upcase
50
+ txt "RUC: #{ruc}"
51
+ book_title("#{title} - #{period}")
52
+ move_down 5
53
+ end
54
+
55
+ def prawn_header(title, period, company)
56
+ repeat(:all) do
57
+ canvas do
58
+ bounding_box([bounds.left + 10, bounds.top - 10], width: 800) do
59
+ book_header period, company.ruc, company.name, title
60
+ end
61
+ end
62
+ end
63
+ end
64
+
65
+ def table_head(fields, book_name, layout)
66
+ thead = []
67
+ fields.each do |h|
68
+ if h.instance_of? Hash
69
+ r = sub_head(h, book_name, layout)
70
+ thead << r
71
+ else
72
+ thead << I18n.t("books.#{book_name}.#{h}").mb_chars.upcase.to_s
73
+ end
74
+ end
75
+ thead
76
+ end
77
+
78
+ def table_body(fields, ticket, widths, aligns)
79
+ tbody = []
80
+ fields.each do |f|
81
+ if f.is_a? Hash
82
+ table_hash(f, ticket, tbody, widths, aligns)
83
+ else
84
+ tbody << field_value(ticket, f)
85
+ end
86
+ end
87
+ tbody
88
+ end
89
+
90
+ def table_hash(field, ticket, tbody, widths, aligns)
91
+ options = { cell_style: { borders: [], size: 5 } }
92
+
93
+ field.each do |key, value|
94
+ v = value.collect do |s|
95
+ value = field_value(ticket, s)
96
+ end
97
+
98
+ column_widths = get_column_widths(widths, key)
99
+ add_widths(column_widths, options, 28)
100
+ add_align(aligns, options, key) unless aligns.nil?
101
+ tbody << make_table([v], options)
102
+ end
103
+ end
104
+
105
+ # diary
106
+ def get_counts(tickets)
107
+ tickets.map(&:uniq_counts).flatten.uniq.sort
108
+ end
109
+
110
+ def get_mother_counts(tickets)
111
+ tickets.map(&:uniq_mother_counts).flatten.uniq.sort
112
+ end
113
+
114
+ def get_value(ticket, count)
115
+ # active_amount = ticket.get_amount_by_position(count)
116
+ # pasive_amount = ticket.get_amount_by_position(count, false)
117
+ active_amount = ticket.get_amount_by_mother_count(count)
118
+ pasive_amount = ticket.get_amount_by_mother_count(count, false)
119
+ # if count === '401' && ticket.operation_type == 'compras'
120
+ # amount = amount * (-1)
121
+ # end
122
+ active_amount - pasive_amount
123
+ end
124
+
125
+ def make_sub_table(content, width = nil)
126
+ options = { cell_style: { width: width, size: 5, borders: [],
127
+ align: :right } }
128
+ content_row = []
129
+ content.each { |c| content_row << formated_number(c) }
130
+ make_table([content_row], options)
131
+ end
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "trading_book"
4
+ require_relative "pages_utils"
5
+
6
+ module SunatBooks
7
+ module Pdf
8
+ class Buys < TradingBook
9
+ include PagesUtils
10
+
11
+ def initialize(company, tickets, month, year)
12
+ super
13
+ prawn_book("REGISTRO DE COMPRAS", 27)
14
+ end
15
+
16
+ def final_row(foot_line_text, page)
17
+ [{ content: foot_line_text, colspan: 5 },
18
+ make_sub_table([page.bi_sum, page.igv_sum], 32),
19
+ make_sub_table([zero, zero], 25),
20
+ make_sub_table([zero, zero], 25),
21
+ formated_number(page.non_taxable),
22
+ zero, zero,
23
+ formated_number(page.total_sum)]
24
+ end
25
+
26
+ def render_prawn_table(data)
27
+ table(data, header: true, cell_style: { borders: [], size: 5,
28
+ align: :right },
29
+ column_widths: { 0 => 22, 1 => 35, 2 => 30, 8 => 30,
30
+ 10 => 30, 9 => 22, 11 => 33, 12 => 33 }) do
31
+ row(0).borders = %i[bottom top]
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SunatBooks
4
+ module Pdf
5
+ class CountSum
6
+ def initialize(count_number, initial_value = BigDecimal(0))
7
+ @sum = initial_value
8
+ @count_number = count_number
9
+ end
10
+
11
+ def add(value)
12
+ @sum += value
13
+ end
14
+
15
+ def count
16
+ @count_number
17
+ end
18
+
19
+ def total
20
+ @sum
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DiaryEntries
4
+ def initial_entry(tickets, counts, total_sums)
5
+ initial = tickets.where(operation_type: "inicial")
6
+ if !initial.empty?
7
+ initial_data = get_row_sums(initial, counts, total_sums)
8
+ else
9
+ initial_data = []
10
+ total_sums.map do |sum|
11
+ initial_data << { content: formated_number(sum.total), align: :right }
12
+ end
13
+ end
14
+ initial_data
15
+ end
16
+
17
+ def buys_entry(tickets, counts, total_sums, data, period_date)
18
+ buys = tickets.where(operation_type: "compras")
19
+ title = "COMPRAS DEL PERIODO"
20
+ return unless buys.count.positive?
21
+
22
+ buys_sum = get_row_sums(buys, counts, total_sums)
23
+ data << [period_date, title, buys_sum].flatten
24
+ end
25
+
26
+ def sales_entry(tickets, counts, total_sums, data, period_date)
27
+ sales = tickets.where(operation_type: "ventas")
28
+ title = "VENTAS DEL PERIODO"
29
+ return unless sales.count.positive?
30
+
31
+ sales_sum = get_row_sums(sales, counts, total_sums)
32
+ data << [period_date, title, sales_sum].flatten
33
+ end
34
+
35
+ def other_entry(tickets, counts, total_sums, data)
36
+ others = tickets.where(operation_type: "otros")
37
+ # others_row = get_row_sums(others, counts, total_sums)
38
+ others&.each do |ticket|
39
+ ticket_data = []
40
+ counts.each_with_index do |count, i|
41
+ value = mother_count?(count, ticket) ? get_value(ticket, count) : 0
42
+ increase_value(ticket_data, total_sums, i, value)
43
+ end
44
+ ref = ticket.reference
45
+ data << [parse_day(ticket.operation_date), ref, ticket_data].flatten
46
+ end
47
+ end
48
+
49
+ def close_entry(tickets, counts, total_sums, data)
50
+ close = tickets.where(operation_type: "cierre")
51
+ close.each do |ticket|
52
+ ticket_data = []
53
+ counts.each_with_index do |count, i|
54
+ value = mother_count?(count, ticket) ? get_value(ticket, count) : 0
55
+ increase_value(ticket_data, total_sums, i, value)
56
+ end
57
+ ref = ticket.reference
58
+ data << [parse_day(ticket.operation_date), ref, ticket_data].flatten
59
+ end
60
+ end
61
+
62
+ def total_entry(total_sums, data)
63
+ # totals
64
+ total_data = []
65
+ total_sums.map do |sum|
66
+ total_data << { content: formated_number(sum.total), align: :right }
67
+ end
68
+ data << [{ content: "TOTALES", colspan: 2 }, total_data].flatten
69
+ end
70
+
71
+ def mother_count?(count, ticket)
72
+ ticket.uniq_mother_counts.include?(count)
73
+ end
74
+
75
+ def calculate_totals(tickets, count_sums)
76
+ # get totals
77
+ tickets.each do |ticket|
78
+ count_sums.each do |count_sum|
79
+ count_sum.add get_value(ticket, count_sum.count)
80
+ end
81
+ end
82
+ end
83
+
84
+ def current_sum_count(count_sums, count)
85
+ sum_count = nil
86
+ count_sums.each do |count_sum|
87
+ sum_count = count_sum if count_sum.count == count
88
+ end
89
+ sum_count
90
+ end
91
+
92
+ def get_row_sums(tickets, counts, total_sums)
93
+ # given an array of counts and tickets get sums by each count
94
+ row_counts = get_mother_counts tickets
95
+ count_sums = row_counts.map { |count| SunatBooks::Pdf::CountSum.new(count) }
96
+
97
+ calculate_totals(tickets, count_sums)
98
+ # get ordered row
99
+ row_data = []
100
+ counts.each_with_index do |count, i|
101
+ sum_count = current_sum_count(count_sums, count)
102
+ value = sum_count ? sum_count.total : 0
103
+ increase_value(row_data, total_sums, i, value)
104
+ end
105
+ row_data
106
+ end
107
+
108
+ def increase_value(row_data, total_sums, index, value)
109
+ total_sums[index].add value
110
+ row_data << { content: formated_number(value), align: :right }
111
+ end
112
+ end