sunat_books 0.0.2 → 0.0.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 +4 -4
- data/.gitignore +1 -0
- data/.rvmrc +1 -0
- data/.travis.yml +2 -5
- data/README.mkd +9 -3
- data/lib/sunat_books.rb +6 -6
- data/lib/sunat_books/common_utils.rb +9 -0
- data/lib/sunat_books/csv/base.rb +63 -0
- data/lib/sunat_books/csv/option_error.rb +8 -0
- data/lib/sunat_books/pdf/base.rb +134 -0
- data/lib/sunat_books/pdf/buys.rb +36 -0
- data/lib/sunat_books/pdf/count_sum.rb +24 -0
- data/lib/{books → sunat_books/pdf}/diary_entries.rb +1 -3
- data/lib/{books → sunat_books/pdf}/layouts/buys.yml +0 -0
- data/lib/{books → sunat_books/pdf}/layouts/sales.yml +0 -0
- data/lib/{books → sunat_books/pdf}/locale.rb +0 -0
- data/lib/{books → sunat_books/pdf}/locales/es.yml +0 -0
- data/lib/sunat_books/pdf/page.rb +48 -0
- data/lib/{books → sunat_books/pdf}/pages_utils.rb +1 -1
- data/lib/sunat_books/pdf/sales.rb +35 -0
- data/lib/sunat_books/pdf/simplified_diary.rb +94 -0
- data/lib/sunat_books/pdf/trading_book.rb +50 -0
- data/lib/{books → sunat_books/pdf}/utils.rb +4 -6
- data/lib/sunat_books/ple/base.rb +76 -0
- data/lib/sunat_books/ple/buys.rb +38 -0
- data/lib/{ple_books → sunat_books/ple}/layouts/buys.yml +0 -0
- data/lib/{ple_books → sunat_books/ple}/layouts/sales.yml +0 -0
- data/lib/sunat_books/ple/sales.rb +22 -0
- data/sunat_books.gemspec +2 -2
- data/test/{csv_books → csv}/base_test.rb +8 -5
- data/test/{books → pdf}/base_test.rb +0 -0
- data/test/pdf/buys_test.rb +27 -0
- data/test/{books → pdf}/page_test.rb +4 -4
- data/test/{books → pdf}/pages_utils_test.rb +32 -2
- data/test/pdf/sales_test.rb +15 -0
- data/test/{books → pdf}/utils_test.rb +7 -1
- data/test/{ple_books_test.rb → ple/base_test.rb} +2 -2
- data/test/{ple_books → ple}/buys_test.rb +8 -8
- metadata +38 -35
- data/Gemfile.lock +0 -83
- data/lib/books/base.rb +0 -132
- data/lib/books/buys.rb +0 -72
- data/lib/books/count_sum.rb +0 -22
- data/lib/books/page.rb +0 -44
- data/lib/books/sales.rb +0 -73
- data/lib/books/simplified_diary.rb +0 -97
- data/lib/csv_books/base.rb +0 -67
- data/lib/csv_books/option_error.rb +0 -6
- data/lib/ple_books/base.rb +0 -75
- data/lib/ple_books/buys.rb +0 -36
- data/lib/ple_books/sales.rb +0 -20
- data/test/books/buys_test.rb +0 -57
@@ -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
|
-
|
63
|
-
|
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
|
File without changes
|
File without changes
|
@@ -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
|
data/sunat_books.gemspec
CHANGED
@@ -2,8 +2,8 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = "sunat_books"
|
5
|
-
s.version = "0.0.
|
6
|
-
s.summary = "
|
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
|
-
|
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 =
|
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(
|
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
|
-
|
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 =
|
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
|