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.
- 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
|