sunat_books 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rvmrc +1 -0
  4. data/.travis.yml +2 -5
  5. data/README.mkd +9 -3
  6. data/lib/sunat_books.rb +6 -6
  7. data/lib/sunat_books/common_utils.rb +9 -0
  8. data/lib/sunat_books/csv/base.rb +63 -0
  9. data/lib/sunat_books/csv/option_error.rb +8 -0
  10. data/lib/sunat_books/pdf/base.rb +134 -0
  11. data/lib/sunat_books/pdf/buys.rb +36 -0
  12. data/lib/sunat_books/pdf/count_sum.rb +24 -0
  13. data/lib/{books → sunat_books/pdf}/diary_entries.rb +1 -3
  14. data/lib/{books → sunat_books/pdf}/layouts/buys.yml +0 -0
  15. data/lib/{books → sunat_books/pdf}/layouts/sales.yml +0 -0
  16. data/lib/{books → sunat_books/pdf}/locale.rb +0 -0
  17. data/lib/{books → sunat_books/pdf}/locales/es.yml +0 -0
  18. data/lib/sunat_books/pdf/page.rb +48 -0
  19. data/lib/{books → sunat_books/pdf}/pages_utils.rb +1 -1
  20. data/lib/sunat_books/pdf/sales.rb +35 -0
  21. data/lib/sunat_books/pdf/simplified_diary.rb +94 -0
  22. data/lib/sunat_books/pdf/trading_book.rb +50 -0
  23. data/lib/{books → sunat_books/pdf}/utils.rb +4 -6
  24. data/lib/sunat_books/ple/base.rb +76 -0
  25. data/lib/sunat_books/ple/buys.rb +38 -0
  26. data/lib/{ple_books → sunat_books/ple}/layouts/buys.yml +0 -0
  27. data/lib/{ple_books → sunat_books/ple}/layouts/sales.yml +0 -0
  28. data/lib/sunat_books/ple/sales.rb +22 -0
  29. data/sunat_books.gemspec +2 -2
  30. data/test/{csv_books → csv}/base_test.rb +8 -5
  31. data/test/{books → pdf}/base_test.rb +0 -0
  32. data/test/pdf/buys_test.rb +27 -0
  33. data/test/{books → pdf}/page_test.rb +4 -4
  34. data/test/{books → pdf}/pages_utils_test.rb +32 -2
  35. data/test/pdf/sales_test.rb +15 -0
  36. data/test/{books → pdf}/utils_test.rb +7 -1
  37. data/test/{ple_books_test.rb → ple/base_test.rb} +2 -2
  38. data/test/{ple_books → ple}/buys_test.rb +8 -8
  39. metadata +38 -35
  40. data/Gemfile.lock +0 -83
  41. data/lib/books/base.rb +0 -132
  42. data/lib/books/buys.rb +0 -72
  43. data/lib/books/count_sum.rb +0 -22
  44. data/lib/books/page.rb +0 -44
  45. data/lib/books/sales.rb +0 -73
  46. data/lib/books/simplified_diary.rb +0 -97
  47. data/lib/csv_books/base.rb +0 -67
  48. data/lib/csv_books/option_error.rb +0 -6
  49. data/lib/ple_books/base.rb +0 -75
  50. data/lib/ple_books/buys.rb +0 -36
  51. data/lib/ple_books/sales.rb +0 -20
  52. data/test/books/buys_test.rb +0 -57
@@ -18,7 +18,7 @@ module PagesUtils
18
18
  pages_num = (length / page_max.to_f).ceil
19
19
  last_index = index.zero? ? pages_num - 1 : pages_num
20
20
  (index..last_index).each do |i|
21
- pages[i] = Books::Page.new(i, 0)
21
+ pages[i] = SunatBooks::Pdf::Page.new(i, 0)
22
22
  end
23
23
  end
24
24
 
@@ -0,0 +1,35 @@
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 Sales < TradingBook
9
+ include PagesUtils
10
+
11
+ def initialize(company, tickets, month, year)
12
+ super
13
+ prawn_book("REGISTRO DE VENTAS", 29)
14
+ end
15
+
16
+ def render_prawn_table(data)
17
+ widths_columns = { 0 => 22, 1 => 35, 2 => 30, 5 => 27, 6 => 37, 8 => 20,
18
+ 9 => 33, 10 => 27, 11 => 35, 12 => 29 }
19
+
20
+ table(data, header: true,
21
+ cell_style: { borders: [], size: 5, align: :right },
22
+ column_widths: widths_columns) do
23
+ row(0).borders = %i[bottom top]
24
+ end
25
+ end
26
+
27
+ def final_row(foot_line_text, page)
28
+ [{ content: foot_line_text, colspan: 5 }, zero,
29
+ formated_number(page.bi_sum), make_sub_table([zero, zero], 22), zero,
30
+ formated_number(page.igv_sum), zero,
31
+ formated_number(page.total_sum)]
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base"
4
+ require_relative "count_sum"
5
+ require_relative "diary_entries"
6
+ require_relative "pages_utils"
7
+
8
+ module SunatBooks
9
+ module Pdf
10
+ class SimplifiedDiary < Base
11
+ include DiaryEntries
12
+ include PagesUtils
13
+ def initialize(company, tickets, month, year)
14
+ super(page_layout: :landscape, margin: [5], page_size: "A4")
15
+ @company = company
16
+ @tickets = tickets
17
+ @main_title = "LIBRO DIARIO - FORMATO SIMPLIFICADO"
18
+ @counts = get_mother_counts @tickets
19
+ @total_sums = @counts.map { |count| CountSum.new(count) }
20
+
21
+ prawn_book(month, year)
22
+ end
23
+
24
+ def prawn_book(month, year)
25
+ (month.to_i..12).each do |m|
26
+ start_new_page unless m == month.to_i
27
+ period = get_period(m, year)
28
+
29
+ x = bounds.left + 3
30
+ y = bounds.top - 10
31
+ bounding_box([x, y], width: 815, height: 510) do
32
+ book_body m, year, 20, period
33
+ end
34
+ end
35
+ end
36
+
37
+ def book_body(month, year, max_column = nil, period = nil)
38
+ tickets = @tickets.where(period_month: month, period_year: year)
39
+
40
+ # header
41
+ data = []
42
+ initial_day = get_date(year.to_i, month.to_i, 1)
43
+ draw_table_header(tickets, @counts, @total_sums, data, initial_day)
44
+
45
+ period_date = get_date(year, month, -1)
46
+ entries_data(tickets, @counts, @total_sums, data, period_date)
47
+
48
+ book_header period, @company.ruc, @company.name, @main_title
49
+ draw_table_body(data, max_column, period)
50
+ end
51
+
52
+ def not_moviment_data(data)
53
+ data << [{ content: "SIN MOVIMIENTO EN EL PERIODO", colspan: 5 }]
54
+ end
55
+
56
+ def entries_data(tickets, counts, total_sums, data, period_date)
57
+ return not_moviment_data(data) if tickets.empty?
58
+ sales_entry(tickets, counts, total_sums, data, period_date)
59
+ buys_entry(tickets, counts, total_sums, data, period_date)
60
+ other_entry(tickets, counts, total_sums, data)
61
+ close_entry(tickets, counts, total_sums, data)
62
+ total_entry(total_sums, data)
63
+ end
64
+
65
+ def draw_table_header(tickets, counts, total_sums, data, date)
66
+ data << ["FECHA", "OPERACIÓN", counts].flatten
67
+
68
+ initial_data = initial_entry(tickets, counts, total_sums)
69
+ data << [date, "ASIENTO INICIAL DEL PERIODO", initial_data].flatten
70
+ end
71
+
72
+ def draw_table_body(data, max_column, period)
73
+ return render_prawn_table(data) unless data.first.count > max_column
74
+
75
+ pages = split_data(data, max_column)
76
+
77
+ pages.each do |page|
78
+ prawn_new_page(period) unless page.page_number.zero?
79
+ render_prawn_table(page.data)
80
+ end
81
+ end
82
+
83
+ def prawn_new_page(period)
84
+ start_new_page
85
+ book_header period, @company.ruc, @company.name, @main_title
86
+ end
87
+
88
+ def render_prawn_table(data)
89
+ table(data, header: true, cell_style: { borders: [], size: 6 },
90
+ column_widths: { 1 => 73 })
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base"
4
+
5
+ module SunatBooks
6
+ module Pdf
7
+ class TradingBook < Base
8
+ def initialize(company, tickets, month, year)
9
+ # company => an object that respond to ruc and name methods
10
+ # tickets => an array of objects that respond to a layout's methods
11
+ # month => a number that represent a month
12
+ # year => a number that represent a year
13
+ super(page_layout: :landscape, margin: [5], page_size: "A4")
14
+ @company = company
15
+ @period = get_period(month, year)
16
+ @tickets = tickets
17
+ @book_name = self.class.name.downcase.sub("sunatbooks::pdf::", "")
18
+ dir = File.dirname(__FILE__)
19
+ @blayout = YAML.load_file("#{dir}/layouts/#{@book_name}.yml")
20
+ end
21
+
22
+ def prawn_book(title, page_max)
23
+ prawn_header title, @period, @company
24
+ @pages = []
25
+ @page_max = page_max
26
+
27
+ bounding_box([bounds.left + 3, bounds.top - 45], width: 800,
28
+ height: 530) do
29
+ setup_pages(@pages, @tickets.length, @page_max)
30
+ move_down 5
31
+ book_body
32
+ end
33
+ end
34
+
35
+ def book_body
36
+ data = []
37
+ fields = @blayout["headers"]
38
+ data << table_head(fields, @book_name, @blayout)
39
+
40
+ if @tickets.length.positive?
41
+ row_data(data, @blayout["widths"], @blayout["align"], fields,
42
+ @book_name)
43
+ else
44
+ not_moviment_page(data)
45
+ end
46
+ render_prawn_table(data)
47
+ end
48
+ end
49
+ end
50
+ end
@@ -2,9 +2,11 @@
2
2
 
3
3
  require "active_support/all"
4
4
  require_relative "count_sum"
5
+ require_relative "../common_utils"
5
6
 
6
7
  module Utils
7
8
  include ActiveSupport::NumberHelper
9
+ include SunatBooks::CommonUtils
8
10
 
9
11
  MONTHS = { 1 => "Enero", 2 => "Febrero", 3 => "marzo", 4 => "abril",
10
12
  5 => "mayo", 6 => "junio", 7 => "julio", 8 => "agosto",
@@ -59,12 +61,8 @@ module Utils
59
61
  end
60
62
 
61
63
  def field_value(ticket, field)
62
- begin
63
- value = ticket.send(field)
64
- value = formated_number(value) if value.class == BigDecimal
65
- rescue
66
- value = ""
67
- end
64
+ value = available_value?(ticket, field)
65
+ value = formated_number(value) if value.class == BigDecimal
68
66
  value
69
67
  end
70
68
 
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: false
2
+
3
+ require "csv"
4
+ require_relative "../common_utils"
5
+
6
+ module SunatBooks
7
+ module Ple
8
+ class Base
9
+ include SunatBooks::CommonUtils
10
+
11
+ attr_accessor :file
12
+
13
+ def ple_book_name(uid, ruc, month, year, *args)
14
+ code = book_code(uid)
15
+ code_oportunity = "00" # TODO: case for 'inventarios y balances'
16
+ operations_state = args[0] || 1 # 0, 1, 2
17
+ content = args[1] || 1 # 1 ,0
18
+ currency = args[2] || 1 # 1, 2
19
+ name = "LE#{ruc}#{year}#{month}00#{code}#{code_oportunity}"
20
+ name << "#{operations_state}#{content}#{currency}1"
21
+ end
22
+
23
+ def book_code(uid)
24
+ dir = File.dirname(__FILE__)
25
+ path = "#{dir}/book_codes.csv"
26
+ code = ""
27
+ CSV.foreach(path) do |row|
28
+ if row[0] == uid
29
+ code = row[2]
30
+ break
31
+ end
32
+ end
33
+ code
34
+ end
35
+
36
+ def path
37
+ dir = File.dirname(__FILE__)
38
+ tmp_path = "#{dir}/tmp/"
39
+ Dir.mkdir(tmp_path) unless Dir.exist?(tmp_path)
40
+ tmp_path
41
+ end
42
+
43
+ def get_file(tickets, fields, filename)
44
+ FileUtils.touch(filename.to_s)
45
+
46
+ send("file=", filename)
47
+
48
+ tickets.each_with_index do |ticket, i|
49
+ ticket_data = get_value(fields, ticket)
50
+
51
+ mode = (i.zero? ? "w+" : "a+")
52
+ File.open(filename.to_s, mode) do |txt|
53
+ txt.puts(ticket_data)
54
+ end
55
+ end
56
+ end
57
+
58
+ def get_value(fields, ticket)
59
+ data = ""
60
+ fields.each do |field|
61
+ value = available_value?(ticket, field)
62
+ data << "#{value}|"
63
+ end
64
+ data
65
+ end
66
+
67
+ def check_layout(options, fields)
68
+ options[:layout]&.each do |key, value|
69
+ i = fields.index(key.to_s)
70
+ fields.delete(key.to_s)
71
+ fields.insert(i, value)
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "sunat_books/ple/base"
4
+
5
+ module SunatBooks
6
+ module Ple
7
+ class Buys < Base
8
+ def initialize(ruc, tickets, month, year, options = {})
9
+ # ruc => company's ruc in string format
10
+ # tickets => an array of objects that respond to a layout's methods
11
+ # month => a number that represent a month
12
+ # year => a number that represent a year
13
+ # options =>
14
+ # :yml => to define a custom layout file
15
+ # :layout => to define a custom name for a specific layout method
16
+
17
+ book_name = self.class.name.downcase.sub("sunatbooks::ple::", "")
18
+ dir = File.dirname(__FILE__)
19
+ yml_path = options[:yml] || "#{dir}/layouts/#{book_name}.yml"
20
+ fields = YAML.load_file(yml_path)
21
+ check_layout(options, fields)
22
+ content = !tickets.empty? ? 1 : 0
23
+ name = ple_book_name("8.1", ruc, month, year, nil, content)
24
+
25
+ filename = "#{path}#{name}.txt"
26
+ get_file(tickets, fields, filename)
27
+ end
28
+
29
+ def insert_layout_fields(options, fields)
30
+ options[:layout].each do |key, value|
31
+ i = fields.index(key.to_s)
32
+ fields.delete(key.to_s)
33
+ fields.insert(i, value)
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "sunat_books/ple/base"
4
+
5
+ module SunatBooks
6
+ module Ple
7
+ class Sales < Base
8
+ def initialize(ruc, tickets, month, year, options = {})
9
+ book_name = self.class.name.downcase.sub("sunatbooks::ple::", "")
10
+ dir = File.dirname(__FILE__)
11
+ yml_path = options[:yml] || "#{dir}/layouts/#{book_name}.yml"
12
+ fields = YAML.load_file(yml_path)
13
+ check_layout(options, fields)
14
+ content = !tickets.empty? ? 1 : 0
15
+
16
+ name = ple_book_name("14.2", ruc, month, year, nil, content)
17
+ @filename = "#{path}#{name}.txt"
18
+ get_file(tickets, fields, @filename)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -2,8 +2,8 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = "sunat_books"
5
- s.version = "0.0.2"
6
- s.summary = "SUNAT books"
5
+ s.version = "0.0.3"
6
+ s.summary = "A ruby gem to get accounting books for SUNAT"
7
7
  s.description = s.summary
8
8
  s.authors = ["César Carruitero"]
9
9
  s.email = ["cesar@mozilla.pe"]
@@ -1,18 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "../helper"
4
- require_relative "../../lib/csv_books/option_error"
4
+ require "sunat_books/csv/option_error"
5
5
 
6
6
  setup do
7
7
  @tickets = []
8
8
  5.times do
9
9
  @tickets << Ticket.new(field: SecureRandom.hex(10))
10
10
  end
11
- @base = CsvBooks::Base.new(@tickets, layout: ["field"])
11
+ @base = SunatBooks::Csv::Base.new(@tickets, layout: ["field"])
12
12
  end
13
13
 
14
14
  test "require a layout array" do
15
- assert_raise(CsvBooks::OptionError) { CsvBooks::Base.new(@tickets) }
15
+ assert_raise(SunatBooks::Csv::OptionError) do
16
+ SunatBooks::Csv::Base.new(@tickets)
17
+ end
16
18
  end
17
19
 
18
20
  test "should generate csv" do
@@ -20,13 +22,14 @@ test "should generate csv" do
20
22
  end
21
23
 
22
24
  test "should allow set custom filename" do
23
- custom_file = CsvBooks::Base.new(@tickets, layout: [], filename: "name.csv")
25
+ options = { layout: [], filename: "name.csv" }
26
+ custom_file = SunatBooks::Csv::Base.new(@tickets, options)
24
27
  assert custom_file.file.include?("name.csv")
25
28
  assert File.exist?(custom_file.file)
26
29
  end
27
30
 
28
31
  test "should handle undefined layout method" do
29
- undefined_method = CsvBooks::Base.new(@tickets, layout: %w[field bar])
32
+ undefined_method = SunatBooks::Csv::Base.new(@tickets, layout: %w[field bar])
30
33
  assert File.exist?(undefined_method.file)
31
34
  end
32
35
 
File without changes
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../helper"
4
+
5
+ setup do
6
+ @company = Company.new(ruc: Faker::Number.number(11), name: Faker::Name.name)
7
+ end
8
+
9
+ test "render pdf, have a parseable pdf" do
10
+ tickets = []
11
+ pdf = SunatBooks::Pdf::Buys.new(@company, tickets, 2, 3015)
12
+ page_counter = PDF::Inspector::Page.analyze(pdf.render)
13
+ assert pdf.page_count == 1
14
+ assert page_counter.pages.size == 1
15
+ end
16
+
17
+ test "@book_name instance variable is correct" do
18
+ book = SunatBooks::Pdf::Buys.new(@company, [], 2, 3015)
19
+ assert_equal book.instance_variable_get("@book_name"), "buys"
20
+ end
21
+
22
+ test "have correct text in header" do
23
+ tickets = []
24
+ pdf = SunatBooks::Pdf::Buys.new(@company, tickets, 2, 3015)
25
+ reader = PDF::Reader.new(StringIO.new(pdf.render))
26
+ assert reader.pages.first.text.include?("REGISTRO DE COMPRAS")
27
+ end