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.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/.rubocop.yml +15 -0
- data/.travis.yml +9 -0
- data/Gemfile +2 -4
- data/Gemfile.lock +51 -16
- data/Makefile +2 -1
- data/README.mkd +8 -6
- data/lib/books/base.rb +70 -150
- data/lib/books/buys.rb +34 -82
- data/lib/books/count_sum.rb +4 -2
- data/lib/books/diary_entries.rb +112 -0
- data/lib/books/layouts/sales.yml +2 -2
- data/lib/books/locale.rb +6 -0
- data/lib/books/locales/es.yml +65 -0
- data/lib/books/page.rb +44 -0
- data/lib/books/pages_utils.rb +85 -0
- data/lib/books/sales.rb +40 -81
- data/lib/books/simplified_diary.rb +65 -117
- data/lib/books/utils.rb +105 -0
- data/lib/csv_books/base.rb +67 -0
- data/lib/csv_books/option_error.rb +6 -0
- data/lib/ple_books/base.rb +37 -22
- data/lib/ple_books/buys.rb +16 -13
- data/lib/ple_books/layouts/buys.yml +3 -3
- data/lib/ple_books/layouts/sales.yml +1 -1
- data/lib/ple_books/sales.rb +10 -4
- data/lib/sunat_books.rb +8 -4
- data/sunat_books.gemspec +13 -6
- data/test/books/base_test.rb +6 -0
- data/test/books/buys_test.rb +57 -0
- data/test/books/page_test.rb +24 -0
- data/test/books/pages_utils_test.rb +78 -0
- data/test/books/utils_test.rb +42 -0
- data/test/csv_books/base_test.rb +43 -0
- data/test/fixtures/base.rb +11 -0
- data/test/fixtures/company.rb +5 -0
- data/test/fixtures/ticket.rb +4 -9
- data/test/helper.rb +11 -13
- data/test/ple_books/buys_test.rb +49 -0
- data/test/ple_books_test.rb +34 -83
- metadata +109 -20
@@ -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
|
+
# buys entry
|
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
|
+
sales_sum = get_row_sums(sales, counts, total_sums)
|
31
|
+
data << [period_date, title, sales_sum].flatten
|
32
|
+
end
|
33
|
+
|
34
|
+
def other_entry(tickets, counts, total_sums, data)
|
35
|
+
# other entries
|
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| Books::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, i, value)
|
109
|
+
total_sums[i].add value
|
110
|
+
row_data << { content: formated_number(value), align: :right }
|
111
|
+
end
|
112
|
+
end
|
data/lib/books/layouts/sales.yml
CHANGED
@@ -11,7 +11,7 @@ headers:
|
|
11
11
|
- client_document_number
|
12
12
|
- client_name
|
13
13
|
- export_value
|
14
|
-
- taxable_bi
|
14
|
+
- taxable_bi
|
15
15
|
- sale_non_taxable:
|
16
16
|
- exo
|
17
17
|
- inaf
|
@@ -21,7 +21,7 @@ headers:
|
|
21
21
|
- total_operation_sales
|
22
22
|
- exchange_rate
|
23
23
|
- credit_origina_document:
|
24
|
-
- credit_original_document_date
|
24
|
+
- credit_original_document_date
|
25
25
|
- credit_original_document_type
|
26
26
|
- credit_original_document_serial
|
27
27
|
- credit_original_document_number
|
data/lib/books/locale.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
es:
|
2
|
+
books:
|
3
|
+
buys:
|
4
|
+
correlative: "Nro"
|
5
|
+
operation_date: "Fecha de Emisión"
|
6
|
+
pay_date: "Fecha de Venc"
|
7
|
+
document: "Documento"
|
8
|
+
doc_type: "T"
|
9
|
+
document_serial: "Serie"
|
10
|
+
year_emition: "AÑO DUA DSI"
|
11
|
+
document_number: "N° documen"
|
12
|
+
supplier: "Proveedor"
|
13
|
+
supplier_document_type: "t"
|
14
|
+
supplier_document_number: "N° documen"
|
15
|
+
supplier_name: "Nombre o razón social"
|
16
|
+
taxable_to_taxable_export: "adquisiciones grav. dest. a oper. gravadas y/o de exportac."
|
17
|
+
taxable_to_taxable_export_bi: "bi"
|
18
|
+
taxable_to_taxable_export_igv: "igv"
|
19
|
+
taxable_to_taxable_export_non_taxable: "adquisiciones gravadas dest. a oper. grav. y/o exportac. y a oper. no gravadas"
|
20
|
+
taxable_to_taxable_export_non_taxable_bi: "bi"
|
21
|
+
taxable_to_taxable_export_non_taxable_igv: "igv"
|
22
|
+
taxable_to_non_taxable: "adquisiciones gravadas destinadas a operaciones no gravadas"
|
23
|
+
taxable_to_non_taxable_bi: "bi"
|
24
|
+
taxable_to_non_taxable_igv: "igv"
|
25
|
+
non_taxable: "adquisiciones no gravadas"
|
26
|
+
isc: "isc"
|
27
|
+
other_taxs: "otros tributos y cargos"
|
28
|
+
total_operation_buys: "importe total"
|
29
|
+
foreign_pay_document_number: "n° comprobante pago emitido por no domiciliado"
|
30
|
+
detraction: "detracción"
|
31
|
+
detraction_number: "número"
|
32
|
+
detraction_date: "fech emis"
|
33
|
+
exchange_rate: "tipo de cambio"
|
34
|
+
debit_original_document: "referencia del comprobante de pago que se modifica"
|
35
|
+
debit_original_document_date: "Fecha"
|
36
|
+
debit_original_document_type: "t"
|
37
|
+
debit_original_document_serial: "serie"
|
38
|
+
debit_original_document_number: "n° doc"
|
39
|
+
sales:
|
40
|
+
correlative: "nro"
|
41
|
+
operation_date: "Fecha de Emisión"
|
42
|
+
pay_date: "Fecha de Venc"
|
43
|
+
document: "Documento"
|
44
|
+
doc_type: "T"
|
45
|
+
document_serial: "Serie"
|
46
|
+
document_number: "N° documen"
|
47
|
+
client: "Cliente"
|
48
|
+
client_document_type: "t"
|
49
|
+
client_document_number: "N° documen"
|
50
|
+
client_name: "Nombre o razón social"
|
51
|
+
export_value: "Valor exportac"
|
52
|
+
taxable_bi: "BI operac gravadas"
|
53
|
+
sale_non_taxable: "operacion. exoneradas o inafectas"
|
54
|
+
exo: "Exo"
|
55
|
+
inaf: "Inaf"
|
56
|
+
isc: "ISC"
|
57
|
+
igv: "IGV"
|
58
|
+
other_taxs: "Otros tributos"
|
59
|
+
total_operation_sales: "importe total"
|
60
|
+
exchange_rate: "tipo de cambio"
|
61
|
+
credit_origina_document: "referencia de documento original que se modifica"
|
62
|
+
credit_original_document_date: "Fec"
|
63
|
+
credit_original_document_type: "t"
|
64
|
+
credit_original_document_serial: "serie"
|
65
|
+
credit_original_document_number: "n° doc"
|
data/lib/books/page.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "base"
|
4
|
+
|
5
|
+
module Books
|
6
|
+
class Page < Base
|
7
|
+
attr_accessor :length, :bi_sum, :igv_sum, :total_sum, :non_taxable, :data
|
8
|
+
|
9
|
+
def initialize(page_number, length)
|
10
|
+
@page_number = page_number
|
11
|
+
@length = length
|
12
|
+
@bi_sum = BigDecimal(0)
|
13
|
+
@igv_sum = BigDecimal(0)
|
14
|
+
@total_sum = BigDecimal(0)
|
15
|
+
@non_taxable = BigDecimal(0)
|
16
|
+
@data = []
|
17
|
+
end
|
18
|
+
|
19
|
+
def update_data_buys(ticket)
|
20
|
+
@bi_sum += ticket.taxable_to_taxable_export_bi.round(2)
|
21
|
+
@igv_sum += ticket.taxable_to_taxable_export_igv.round(2)
|
22
|
+
@total_sum += ticket.total_operation_buys.round(2)
|
23
|
+
@non_taxable += ticket.non_taxable unless ticket.non_taxable.nil?
|
24
|
+
end
|
25
|
+
|
26
|
+
def update_data_sales(ticket)
|
27
|
+
@bi_sum += ticket.taxable_bi.round(2)
|
28
|
+
@igv_sum += ticket.igv.round(2)
|
29
|
+
@total_sum += ticket.total_operation_sales.round(2)
|
30
|
+
end
|
31
|
+
|
32
|
+
def update_fields(fields = nil, source = nil)
|
33
|
+
# update fields from a given source
|
34
|
+
return if source.nil?
|
35
|
+
fields&.each do |field|
|
36
|
+
begin
|
37
|
+
send("#{field}=", source.send(field))
|
38
|
+
rescue
|
39
|
+
return nil
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "page"
|
4
|
+
|
5
|
+
module PagesUtils
|
6
|
+
def page_not_full(last_page, pages, page_max, &block)
|
7
|
+
if last_page.length < page_max
|
8
|
+
last_page.length += 1
|
9
|
+
last_page
|
10
|
+
else
|
11
|
+
yield if block
|
12
|
+
new_page = setup_new_page(pages, last_page, 2)
|
13
|
+
new_page
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def setup_pages(pages, length, page_max, index = 1)
|
18
|
+
pages_num = (length / page_max.to_f).ceil
|
19
|
+
last_index = index.zero? ? pages_num - 1 : pages_num
|
20
|
+
(index..last_index).each do |i|
|
21
|
+
pages[i] = Books::Page.new(i, 0)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def setup_new_page(pages, last_page, length)
|
26
|
+
return if last_page.page_number == pages.count
|
27
|
+
new_page = pages[last_page.page_number + 1]
|
28
|
+
fields = %w[bi_sum igv_sum total_sum non_taxable]
|
29
|
+
new_page.update_fields(fields, last_page)
|
30
|
+
new_page.length += length
|
31
|
+
new_page
|
32
|
+
end
|
33
|
+
|
34
|
+
def page_index(i, page_max)
|
35
|
+
i.zero? ? 1 : (i / page_max.to_f).ceil
|
36
|
+
end
|
37
|
+
|
38
|
+
def not_moviment_page(data)
|
39
|
+
data << [content: "SIN MOVIMIENTO EN EL PERIODO", colspan: 5]
|
40
|
+
end
|
41
|
+
|
42
|
+
def setup_final_row_data(page, ticket, data)
|
43
|
+
if page.length == @page_max && @tickets.last != ticket
|
44
|
+
data << final_row("VAN", page)
|
45
|
+
elsif @tickets.last == ticket
|
46
|
+
data << final_row("TOTAL", page)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def row_data(data, widths, aligns, fields, operation)
|
51
|
+
@tickets.each_with_index do |ticket, i|
|
52
|
+
last_page = @pages[page_index(i, @page_max)]
|
53
|
+
page = page_not_full(last_page, @pages, @page_max) do
|
54
|
+
data << final_row("VIENEN", last_page)
|
55
|
+
end
|
56
|
+
data << table_body(fields, ticket, widths, aligns)
|
57
|
+
page.send("update_data_#{operation}", ticket)
|
58
|
+
setup_final_row_data(page, ticket, data)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# for diary
|
63
|
+
def split_data(data, max_column)
|
64
|
+
# split rows data in pages according max_column
|
65
|
+
pages = []
|
66
|
+
setup_pages(pages, data.first.count, max_column, 0)
|
67
|
+
data&.each_with_index do |row, i|
|
68
|
+
if i == data.length - 1
|
69
|
+
setup_row_pages(pages, row, max_column - 1, 1)
|
70
|
+
else
|
71
|
+
setup_row_pages(pages, row, max_column)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
pages
|
75
|
+
end
|
76
|
+
|
77
|
+
def setup_row_pages(pages, row, max_column, first_rows = 2)
|
78
|
+
initial_rows = row.first(first_rows)
|
79
|
+
batches_length = max_column - first_rows
|
80
|
+
left_data = row[first_rows..row.length].in_groups_of(batches_length, false)
|
81
|
+
pages.each_with_index do |page, i|
|
82
|
+
page.data << (initial_rows + left_data[i])
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
data/lib/books/sales.rb
CHANGED
@@ -1,114 +1,73 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "base"
|
4
|
+
require_relative "pages_utils"
|
2
5
|
|
3
6
|
module Books
|
4
7
|
class Sales < Base
|
8
|
+
include PagesUtils
|
9
|
+
|
5
10
|
def initialize(company, tickets, view, month, year)
|
11
|
+
# company => an object that respond to ruc and name methods
|
12
|
+
# tickets => an array of objects that respond to a layout's methods
|
13
|
+
# view => a view context
|
14
|
+
# month => a number that represent a month
|
15
|
+
# year => a number that represent a year
|
6
16
|
super(page_layout: :landscape, margin: [5], page_size: "A4")
|
7
17
|
@view = view
|
8
18
|
@company = company
|
9
19
|
@period = get_period(month, year)
|
10
|
-
@pages = {}
|
11
20
|
@tickets = tickets
|
12
21
|
@book_name = self.class.name.downcase.sub("books::", "")
|
13
22
|
dir = File.dirname(__FILE__)
|
14
23
|
@blayout = YAML.load_file("#{dir}/layouts/#{@book_name}.yml")
|
15
24
|
@page_max = 29
|
16
25
|
|
17
|
-
|
18
|
-
|
19
|
-
bounding_box([bounds.left + 10, bounds.top - 10], width: 800) do
|
20
|
-
# book_title "REGISTRO DE VENTAS"
|
21
|
-
book_header @period, @company.ruc, @company.name, "REGISTRO DE VENTAS"
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
26
|
+
prawn_book
|
27
|
+
end
|
25
28
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
+
def prawn_book
|
30
|
+
prawn_header "REGISTRO DE VENTAS", @period, @company
|
31
|
+
@pages = []
|
29
32
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
end
|
35
|
-
end
|
33
|
+
bounding_box([bounds.left + 3, bounds.top - 45], width: 800,
|
34
|
+
height: 530) do
|
35
|
+
setup_pages(@pages, @tickets.length, @page_max)
|
36
|
+
book_body
|
36
37
|
end
|
37
38
|
end
|
38
39
|
|
39
40
|
def book_body
|
40
|
-
length = @tickets.length
|
41
|
-
page_num = (length / 29.0).ceil
|
42
|
-
page_num.times do |i|
|
43
|
-
@pages[i + 1] = {
|
44
|
-
page_number: i + 1,
|
45
|
-
length: 0,
|
46
|
-
bi_sum: BigDecimal(0)
|
47
|
-
}
|
48
|
-
end
|
49
|
-
|
50
41
|
move_down 5
|
51
|
-
fields = @blayout["headers"]
|
52
|
-
widths = @blayout["widths"]
|
53
|
-
aligns = @blayout["align"]
|
54
42
|
data = []
|
43
|
+
fields = @blayout["headers"]
|
55
44
|
data << table_head(fields, @book_name, @blayout)
|
56
45
|
|
57
|
-
|
58
|
-
|
59
|
-
igv_sum = BigDecimal(0)
|
60
|
-
total_sum = BigDecimal(0)
|
61
|
-
non_taxable = BigDecimal(0)
|
62
|
-
|
63
|
-
if length > 0
|
64
|
-
@tickets.each do |ticket|
|
65
|
-
|
66
|
-
if @pages[n][:length] < @page_max
|
67
|
-
page = @pages[n]
|
68
|
-
page[:length] += 1
|
69
|
-
else
|
70
|
-
data << final_row('VIENEN', @pages[n])
|
71
|
-
page = @pages[n + 1]
|
72
|
-
n += 1
|
73
|
-
page[:length] += 2
|
74
|
-
end
|
75
|
-
|
76
|
-
data << table_body(fields, ticket, widths, aligns)
|
77
|
-
|
78
|
-
bi_sum += ticket.taxable_bi
|
79
|
-
igv_sum += ticket.igv
|
80
|
-
total_sum += ticket.total_operation_sales
|
81
|
-
page[:bi_sum] = bi_sum.round(2)
|
82
|
-
page[:igv_sum] = igv_sum.round(2)
|
83
|
-
page[:total_sum] = total_sum.round(2)
|
84
|
-
if page[:length] == @page_max && @tickets.last != ticket
|
85
|
-
data << final_row('VAN', page)
|
86
|
-
elsif @tickets.last == ticket
|
87
|
-
data << final_row('TOTAL', page)
|
88
|
-
end
|
89
|
-
end
|
46
|
+
if @tickets.length.positive?
|
47
|
+
row_data(data, @blayout["widths"], @blayout["align"], fields, "sales")
|
90
48
|
else
|
91
|
-
data
|
92
|
-
@pages[n] = {}
|
93
|
-
page = @pages[n]
|
94
|
-
page[:bi_sum] = zero
|
95
|
-
page[:igv_sum] = zero
|
96
|
-
page[:total_sum] = zero
|
49
|
+
not_moviment_page(data)
|
97
50
|
end
|
98
51
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
52
|
+
render_prawn_table(data)
|
53
|
+
end
|
54
|
+
|
55
|
+
def render_prawn_table(data)
|
56
|
+
widths_columns = { 0 => 22, 1 => 35, 2 => 30, 5 => 27, 6 => 37, 8 => 20,
|
57
|
+
9 => 33, 10 => 27, 11 => 35, 12 => 29 }
|
58
|
+
|
59
|
+
table(data, header: true,
|
60
|
+
cell_style: { borders: [], size: 5, align: :right },
|
61
|
+
column_widths: widths_columns) do
|
62
|
+
row(0).borders = %i[bottom top]
|
104
63
|
end
|
105
64
|
end
|
106
65
|
|
107
|
-
def final_row
|
108
|
-
[
|
109
|
-
|
110
|
-
|
111
|
-
|
66
|
+
def final_row(foot_line_text, page)
|
67
|
+
[{ content: foot_line_text, colspan: 5 }, zero,
|
68
|
+
formated_number(page.bi_sum), make_sub_table([zero, zero], 22), zero,
|
69
|
+
formated_number(page.igv_sum), zero,
|
70
|
+
formated_number(page.total_sum)]
|
112
71
|
end
|
113
72
|
end
|
114
73
|
end
|