sunat_books 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,149 +1,97 @@
1
- require 'books/base'
2
- require 'books/count_sum'
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"
3
7
 
4
8
  module Books
5
9
  class SimplifiedDiary < Base
6
- def initialize company, tickets, view, month, year
10
+ include DiaryEntries
11
+ include PagesUtils
12
+ def initialize(company, tickets, view, month, year)
7
13
  super(page_layout: :landscape, margin: [5], page_size: "A4")
8
14
  @view = view
9
15
  @company = company
10
16
  @tickets = tickets
11
- #@book_name = self.class.name.downcase.sub("books::", "")
12
- #dir = File.dirname(__FILE__)
13
- #@blayout = YAML.load_file("#{dir}/layouts/#{@book_name}.yml")
14
- counts = get_mother_counts @tickets
15
- total_sums = counts.map { |count| CountSum.new(count) }
17
+ # @book_name = self.class.name.downcase.sub("books::", "")
18
+ # dir = File.dirname(__FILE__)
19
+ # @blayout = YAML.load_file("#{dir}/layouts/#{@book_name}.yml")
20
+ @main_title = "LIBRO DIARIO - FORMATO SIMPLIFICADO"
21
+ @counts = get_mother_counts @tickets
22
+ @total_sums = @counts.map { |count| CountSum.new(count) }
23
+
24
+ prawn_book(month, year)
25
+ end
16
26
 
27
+ def prawn_book(month, year)
17
28
  (month.to_i..12).each do |m|
18
29
  start_new_page unless m == month.to_i
19
30
  period = get_period(m, year)
20
31
 
21
- bounding_box([bounds.left + 3, bounds.top - 10], width: 815, height: 510) do
22
- book_body m, year, total_sums, 20, period
32
+ x = bounds.left + 3
33
+ y = bounds.top - 10
34
+ bounding_box([x, y], width: 815, height: 510) do
35
+ book_body m, year, 20, period
23
36
  end
24
37
  end
25
38
  end
26
39
 
27
- def book_body month, year, total_sums, max_column=nil, period=nil
28
- data = []
40
+ def book_body(month, year, max_column = nil, period = nil)
29
41
  tickets = @tickets.where(period_month: month, period_year: year)
30
42
 
31
43
  # header
32
- # counts = get_counts @tickets
33
- counts = get_mother_counts @tickets
34
- data << ['FECHA', 'OPERACIÓN', counts].flatten
44
+ data = []
45
+ initial_day = get_date(year.to_i, month.to_i, 1)
46
+ draw_table_header(tickets, @counts, @total_sums, data, initial_day)
35
47
 
36
- # body
48
+ period_date = get_date(year, month, -1)
49
+ entries_data(tickets, @counts, @total_sums, data, period_date)
37
50
 
38
- # initial entry
39
- initial = tickets.where(operation_type: 'inicial')
40
- if initial.length > 0
41
- initial_data = get_row_sums(initial, counts, total_sums)
42
- else
43
- initial_data = []
44
- total_sums.map do |sum|
45
- initial_data << { content: formated_number(sum.total), align: :right }
46
- end
47
- end
48
- date = get_date(year.to_i, month.to_i, 1)
49
- data << [date, 'ASIENTO INICIAL DEL PERIODO', initial_data].flatten
50
-
51
- if tickets.length > 0
52
- sales = tickets.where(operation_type: 'ventas')
53
- if sales.count > 0
54
- # sales entry
55
- sales_row = get_row_sums(sales, counts, total_sums)
56
- data << [get_date(year.to_i, month.to_i, -1), 'VENTAS DEL PERIODO', sales_row].flatten
57
- end
51
+ book_header period, @company.ruc, @company.name, @main_title
52
+ draw_table_body(data, max_column, period)
53
+ end
58
54
 
59
- buys = tickets.where(operation_type: 'compras')
60
- if buys.count > 0
61
- # buys entry
62
- buys_row = get_row_sums(buys, counts, total_sums)
63
- data << [get_date(year.to_i, month.to_i, -1), 'COMPRAS DEL PERIODO', buys_row].flatten
64
- end
55
+ def not_moviment_data(data)
56
+ data << [{ content: "SIN MOVIMIENTO EN EL PERIODO", colspan: 5 }]
57
+ end
65
58
 
66
- # other entries
67
- others = tickets.where(operation_type: 'otros')
68
- # others_row = get_row_sums(others, counts, total_sums)
69
- others.each do |ticket|
70
- ticket_data = []
71
- counts.each_with_index do |count, i|
72
- if ticket.uniq_mother_counts.include? count
73
- value = get_value(ticket, count)
74
- else
75
- value = 0
76
- end
77
- total_sums[i].add value
78
- ticket_data << { content: formated_number(value), align: :right }
79
- end
80
- data << [parse_day(ticket.operation_date), ticket.reference, ticket_data].flatten
81
- end
59
+ def entries_data(tickets, counts, total_sums, data, period_date)
60
+ return not_moviment_data(data) if tickets.empty?
61
+ sales_entry(tickets, counts, total_sums, data, period_date)
62
+ buys_entry(tickets, counts, total_sums, data, period_date)
63
+ other_entry(tickets, counts, total_sums, data)
64
+ close_entry(tickets, counts, total_sums, data)
65
+ total_entry(total_sums, data)
66
+ end
82
67
 
83
- # cierre entry
84
- close = tickets.where(operation_type: 'cierre')
85
- close.each do |ticket|
86
- ticket_data = []
87
- counts.each_with_index do |count, i|
88
- if ticket.uniq_mother_counts.include? count
89
- value = get_value(ticket, count)
90
- else
91
- value = 0
92
- end
93
- total_sums[i].add value
94
- ticket_data << { content: formated_number(value), align: :right }
95
- end
96
- data << [parse_day(ticket.operation_date), ticket.reference, ticket_data].flatten
97
- end
98
- else
99
- data << [{content: 'SIN MOVIMIENTO EN EL PERIODO', colspan: 5}]
100
- end
68
+ def draw_table_header(tickets, counts, total_sums, data, date)
69
+ data << ["FECHA", "OPERACIÓN", counts].flatten
101
70
 
102
- # totals
103
- total_data = []
104
- total_sums.map do |sum|
105
- total_data << { content: formated_number(sum.total), align: :right }
106
- end
107
- data << [{content: 'TOTALES', colspan: 2}, total_data].flatten
108
-
109
- book_header period, @company.ruc, @company.name, "LIBRO DIARIO - FORMATO SIMPLIFICADO"
110
-
111
- if data.first.count > max_column
112
- tmp0 = []
113
- tmp1 = []
114
-
115
- data.each do |column|
116
- if column == data.last
117
- first_page = column.first(max_column - 1)
118
- tmp0 << first_page
119
- next_page = [ column.first ] + (column[(max_column -1)..column.length])
120
- tmp1 << next_page
121
- else
122
- if column.length < max_column
123
- tmp0 << column
124
- else
125
- first_page = column.first(max_column)
126
- tmp0 << first_page
127
-
128
- # TODO: make the same for more than 2 pages
129
- next_page = column.first(2) + (column[max_column..column.length])
130
- tmp1 << next_page
131
- end
132
- end
133
- end
71
+ # body
72
+ initial_data = initial_entry(tickets, counts, total_sums)
73
+ data << [date, "ASIENTO INICIAL DEL PERIODO", initial_data].flatten
74
+ end
134
75
 
135
- table(tmp0, header: true, cell_style: {borders: [], size: 6},
136
- column_widths: { 1 => 73 })
137
- start_new_page
138
- book_header period, @company.ruc, @company.name, "LIBRO DIARIO - FORMATO SIMPLIFICADO"
139
- table(tmp1, header: true, cell_style: {borders: [], size: 6},
140
- column_widths: { 1 => 73 })
76
+ def draw_table_body(data, max_column, period)
77
+ return render_prawn_table(data) unless data.first.count > max_column
141
78
 
142
- else
79
+ pages = split_data(data, max_column)
143
80
 
144
- table(data, header: true, cell_style: {borders: [], size: 6},
145
- column_widths: { 1 => 73 })
81
+ pages.each do |page|
82
+ prawn_new_page(period) unless page.page_number.zero?
83
+ render_prawn_table(page.data)
146
84
  end
147
85
  end
86
+
87
+ def prawn_new_page(period)
88
+ start_new_page
89
+ book_header period, @company.ruc, @company.name, @main_title
90
+ end
91
+
92
+ def render_prawn_table(data)
93
+ table(data, header: true, cell_style: { borders: [], size: 6 },
94
+ column_widths: { 1 => 73 })
95
+ end
148
96
  end
149
97
  end
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/all"
4
+ require_relative "count_sum"
5
+
6
+ module Utils
7
+ include ActiveSupport::NumberHelper
8
+
9
+ MONTHS = { 1 => "Enero", 2 => "Febrero", 3 => "marzo", 4 => "abril",
10
+ 5 => "mayo", 6 => "junio", 7 => "julio", 8 => "agosto",
11
+ 9 => "setiembre", 10 => "octubre", 11 => "noviembre",
12
+ 12 => "diciembre" }.freeze
13
+
14
+ def formated_number(float)
15
+ number_to_currency(float, unit: "")
16
+ end
17
+
18
+ def get_date(year, month, day)
19
+ parse_day(Date.new(year.to_i, month.to_i, day))
20
+ end
21
+
22
+ def get_period(month, year)
23
+ "#{MONTHS[month.to_i].upcase} #{year}"
24
+ end
25
+
26
+ def parse_day(day)
27
+ day.strftime("%d-%m").to_s
28
+ end
29
+
30
+ def add_align(aligns, options, key)
31
+ cell_style = options[:cell_style]
32
+ aligns.map do |a|
33
+ cell_style.merge!(align: a[key][0].to_sym) unless a[key].nil?
34
+ end
35
+ end
36
+
37
+ def add_widths(column_widths, options, width)
38
+ if column_widths.empty?
39
+ options[:cell_style][:width] = width
40
+ else
41
+ options.merge!(column_widths: column_widths)
42
+ end
43
+ end
44
+
45
+ def get_column_widths(widths, key)
46
+ obj = {}
47
+ widths&.each do |w|
48
+ obj = w[key].flatten unless w[key].nil?
49
+ end
50
+ obj
51
+ end
52
+
53
+ def txt(txt)
54
+ text txt, size: 8
55
+ end
56
+
57
+ def zero
58
+ formated_number(0)
59
+ end
60
+
61
+ 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
68
+ value
69
+ end
70
+
71
+ def sum_count(count_sums, count)
72
+ sum = nil
73
+ count_sums.each do |count_sum|
74
+ sum = count_sum if count_sum.count == count
75
+ end
76
+ sum
77
+ end
78
+
79
+ def order_data_row(counts, count_sums, total_sums)
80
+ data = []
81
+ counts.each_with_index do |count, i|
82
+ sum = sum_count(count_sums, count)
83
+ value = sum ? sum.total : 0
84
+ total_sums[i].add value
85
+ data << { content: formated_number(value), align: :right }
86
+ end
87
+ data
88
+ end
89
+
90
+ def get_row_sums(tickets, counts, total_sums)
91
+ # given an array of counts and tickets get sums by each count
92
+ row_counts = get_mother_counts tickets
93
+ count_sums = row_counts.map { |count| Books::CountSum.new(count) }
94
+
95
+ # get totals
96
+ tickets.each do |ticket|
97
+ count_sums.each do |count_sum|
98
+ count_sum.add get_value(ticket, count_sum.count)
99
+ end
100
+ end
101
+
102
+ # get ordered row
103
+ order_data_row(counts, count_sums, total_sums)
104
+ end
105
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "csv"
4
+ require_relative "option_error"
5
+
6
+ module CsvBooks
7
+ class Base
8
+ attr_accessor :file
9
+
10
+ def initialize(tickets, options = {})
11
+ # options
12
+ # - layout => Array of strings used to get data for csv
13
+ # - filename
14
+ if options[:layout].nil?
15
+ raise CsvBooks::OptionError.new(msg: "Layout option is required")
16
+ end
17
+ filename = options[:filename] || "#{tmp_path}book.csv"
18
+ fields = options[:layout]
19
+ get_file(filename, fields, tickets)
20
+ end
21
+
22
+ def get_file(filename, fields, tickets)
23
+ send("file=", filename)
24
+ File.exist?(filename) ? File.delete(filename) : nil
25
+ FileUtils.touch(filename)
26
+ append_headers(filename, fields)
27
+ append_data(tickets, filename, fields)
28
+ end
29
+
30
+ def tmp_path
31
+ dir = File.dirname(__FILE__)
32
+ tmp_path = "#{dir}/tmp/"
33
+ Dir.mkdir(tmp_path) unless Dir.exist?(tmp_path)
34
+ tmp_path
35
+ end
36
+
37
+ def append_headers(filename, fields)
38
+ append_to_csv(filename, fields, "w+")
39
+ end
40
+
41
+ def append_data(tickets, filename, fields)
42
+ tickets&.each do |ticket|
43
+ data = []
44
+ fields&.each do |field|
45
+ data << field_value(ticket, field)
46
+ end
47
+ append_to_csv(filename, data, "a+")
48
+ end
49
+ end
50
+
51
+ def field_value(ticket, field)
52
+ begin
53
+ value = ticket.send(field)
54
+ rescue
55
+ value = ""
56
+ end
57
+ value
58
+ end
59
+
60
+ def append_to_csv(filename, data, mode)
61
+ return if data.nil?
62
+ CSV.open(filename, mode) do |csv|
63
+ csv << data
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CsvBooks
4
+ class OptionError < StandardError
5
+ end
6
+ end
@@ -1,19 +1,22 @@
1
- require 'csv'
1
+ # frozen_string_literal: false
2
+
3
+ require "csv"
2
4
 
3
5
  module PleBooks
4
6
  class Base
5
7
  attr_accessor :file
6
8
 
7
- def ple_book_name uid, ruc, month, year, operations_state=nil, content=nil, currency=nil
9
+ def ple_book_name(uid, ruc, month, year, *args)
8
10
  code = book_code(uid)
9
- code_oportunity = '00' # TODO: case for 'inventarios y balances'
10
- operations_state ||= 1 # 0, 1, 2
11
- content ||= 1 # 1 ,0
12
- currency ||= 1 # 1, 2
13
- "LE#{ruc}#{year}#{month}00#{code}#{code_oportunity}#{operations_state}#{content}#{currency}1"
11
+ code_oportunity = "00" # TODO: case for 'inventarios y balances'
12
+ operations_state = args[0] || 1 # 0, 1, 2
13
+ content = args[1] || 1 # 1 ,0
14
+ currency = args[2] || 1 # 1, 2
15
+ name = "LE#{ruc}#{year}#{month}00#{code}#{code_oportunity}"
16
+ name << "#{operations_state}#{content}#{currency}1"
14
17
  end
15
18
 
16
- def book_code uid
19
+ def book_code(uid)
17
20
  dir = File.dirname(__FILE__)
18
21
  path = "#{dir}/book_codes.csv"
19
22
  code = ""
@@ -29,32 +32,44 @@ module PleBooks
29
32
  def path
30
33
  dir = File.dirname(__FILE__)
31
34
  tmp_path = "#{dir}/tmp/"
32
- Dir.mkdir(tmp_path) unless Dir.exists?(tmp_path)
35
+ Dir.mkdir(tmp_path) unless Dir.exist?(tmp_path)
33
36
  tmp_path
34
37
  end
35
38
 
36
39
  def get_file(tickets, fields, filename)
37
- FileUtils.touch("#{filename}")
40
+ FileUtils.touch(filename.to_s)
38
41
 
39
42
  send("file=", filename)
40
43
 
41
44
  tickets.each_with_index do |ticket, i|
42
- ticket_data = ""
43
-
44
- fields.each do |field|
45
- begin
46
- value = ticket.send(field)
47
- rescue
48
- value = ""
49
- end
50
- ticket_data << "#{value}|"
51
- end
45
+ ticket_data = get_value(fields, ticket)
52
46
 
53
- mode = (i == 0 ? "w+" : "a+")
54
- File.open("#{filename}", mode) do |txt|
47
+ mode = (i.zero? ? "w+" : "a+")
48
+ File.open(filename.to_s, mode) do |txt|
55
49
  txt.puts(ticket_data)
56
50
  end
57
51
  end
58
52
  end
53
+
54
+ def get_value(fields, ticket)
55
+ data = ""
56
+ fields.each do |field|
57
+ begin
58
+ value = ticket.send(field)
59
+ rescue
60
+ value = ""
61
+ end
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
59
74
  end
60
75
  end
@@ -1,8 +1,10 @@
1
- require 'ple_books/base'
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base"
2
4
 
3
5
  module PleBooks
4
6
  class Buys < Base
5
- def initialize(ruc, tickets, month, year, options={})
7
+ def initialize(ruc, tickets, month, year, options = {})
6
8
  # ruc => company's ruc in string format
7
9
  # tickets => an array of objects that respond to a layout's methods
8
10
  # month => a number that represent a month
@@ -15,19 +17,20 @@ module PleBooks
15
17
  dir = File.dirname(__FILE__)
16
18
  yml_path = options[:yml] || "#{dir}/layouts/#{book_name}.yml"
17
19
  fields = YAML.load_file(yml_path)
20
+ check_layout(options, fields)
21
+ content = !tickets.empty? ? 1 : 0
22
+ name = ple_book_name("8.1", ruc, month, year, nil, content)
18
23
 
19
- if options[:layout]
20
- options[:layout].each do |key, value|
21
- i = fields.index(key.to_s)
22
- fields.delete(key.to_s)
23
- fields.insert(i, value)
24
- end
25
- end
26
-
27
- content = tickets.length > 0 ? 1 : 0
28
-
29
- filename = "#{path}#{ple_book_name('8.1', ruc, month, year, nil, content)}.txt"
24
+ filename = "#{path}#{name}.txt"
30
25
  get_file(tickets, fields, filename)
31
26
  end
27
+
28
+ def insert_layout_fields(options, fields)
29
+ options[:layout].each do |key, value|
30
+ i = fields.index(key.to_s)
31
+ fields.delete(key.to_s)
32
+ fields.insert(i, value)
33
+ end
34
+ end
32
35
  end
33
36
  end
@@ -1,8 +1,8 @@
1
- - period # YYYYMM00
1
+ - period # YYYYMMDD
2
2
  - cuo # Código Unico de Operación, software (max 40)
3
3
  - correlative # 2 ..10
4
- - operation_date # day # 'dd/mm/yyyy'
5
- - pay_date # day # 'dd/mm/yyyy'
4
+ - operation_date # 'dd/mm/yyyy'
5
+ - pay_date # 'dd/mm/yyyy'
6
6
  - document_type # 2
7
7
  - document_serial # max 20
8
8
  - dua_year_emition
@@ -22,4 +22,4 @@
22
22
  - original_document_number
23
23
  - exchange_rate_error
24
24
  - payment_way
25
- - anotation_state
25
+ - anotation_oportunity
@@ -1,13 +1,19 @@
1
- require 'ple_books/base'
1
+ # frozen_string_literal: true
2
+
3
+ require "ple_books/base"
2
4
 
3
5
  module PleBooks
4
6
  class Sales < Base
5
- def initialize(ruc, tickets, month, year)
7
+ def initialize(ruc, tickets, month, year, options = {})
6
8
  book_name = self.class.name.downcase.sub("plebooks::", "")
7
9
  dir = File.dirname(__FILE__)
8
- fields = YAML.load_file("#{dir}/layouts/#{book_name}.yml")
10
+ yml_path = options[:yml] || "#{dir}/layouts/#{book_name}.yml"
11
+ fields = YAML.load_file(yml_path)
12
+ check_layout(options, fields)
13
+ content = !tickets.empty? ? 1 : 0
9
14
 
10
- @filename = "#{path}#{ple_book_name('14.2', ruc, month, year)}.txt"
15
+ name = ple_book_name("14.2", ruc, month, year, nil, content)
16
+ @filename = "#{path}#{name}.txt"
11
17
  get_file(tickets, fields, @filename)
12
18
  end
13
19
  end
data/lib/sunat_books.rb CHANGED
@@ -1,4 +1,8 @@
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_relative "books/buys"
4
+ require_relative "books/sales"
5
+ require_relative "books/simplified_diary"
6
+ require_relative "ple_books/buys"
7
+ require_relative "ple_books/sales"
8
+ require_relative "csv_books/base"
data/sunat_books.gemspec CHANGED
@@ -1,18 +1,25 @@
1
+ # frozen_string_literal: true
2
+
1
3
  Gem::Specification.new do |s|
2
4
  s.name = "sunat_books"
3
- s.version = "0.0.1"
5
+ s.version = "0.0.2"
4
6
  s.summary = "SUNAT books"
5
7
  s.description = s.summary
6
8
  s.authors = ["César Carruitero"]
7
9
  s.email = ["cesar@mozilla.pe"]
8
- s.homepage = "https://github.com/ccarruitero/books"
10
+ s.homepage = "https://github.com/ccarruitero/sunat_books"
9
11
  s.license = "MPL"
10
12
 
11
13
  s.files = `git ls-files`.split("\n")
12
14
 
13
- s.add_runtime_dependency "prawn"
14
- s.add_runtime_dependency "i18n"
15
- s.add_runtime_dependency "activesupport"
15
+ s.add_runtime_dependency("prawn", "~> 2.0")
16
+ s.add_runtime_dependency("prawn-table", "~> 0.2")
17
+ s.add_runtime_dependency("i18n", "~> 0.7")
18
+ s.add_runtime_dependency("activesupport", "> 4.1")
16
19
 
17
- s.add_development_dependency "cutest"
20
+ s.add_development_dependency("cutest", "~> 1.2")
21
+ s.add_development_dependency("pry", "~> 0.10")
22
+ s.add_development_dependency("rubocop", "~> 0.48")
23
+ s.add_development_dependency("pdf-inspector", "~> 1.2.0")
24
+ s.add_development_dependency("faker", "~> 1.7")
18
25
  end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../helper"
4
+
5
+ test "#sub_head_table return a table object" do
6
+ end