colppy 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,152 @@
1
+ module Colppy
2
+ class Product < Resource
3
+ attr_reader :id, :name, :sku, :data
4
+
5
+ class << self
6
+ def all(client, company)
7
+ list(client, company)
8
+ end
9
+
10
+ def list(client, company, parameters = {})
11
+ call_parameters = base_params.merge(parameters)
12
+ response = client.call(
13
+ :product,
14
+ :list,
15
+ extended_parameters(client, company, call_parameters)
16
+ )
17
+ if response[:success]
18
+ results = response[:data].map do |params|
19
+ new(params.merge(client: client, company: company))
20
+ end
21
+ parse_list_response(results, response[:total].to_i, call_parameters)
22
+ else
23
+ response
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def extended_parameters(client, company, parameters)
30
+ [ client.session_params,
31
+ company.params,
32
+ parameters
33
+ ].inject(&:merge)
34
+ end
35
+
36
+ def base_params
37
+ {
38
+ filter:[],
39
+ order: [{
40
+ field: "codigo",
41
+ dir: "asc"
42
+ }]
43
+ }
44
+ end
45
+
46
+ end
47
+
48
+ def initialize(client: nil, company: nil, **params)
49
+ @client = client if client && client.is_a?(Colppy::Client)
50
+ @company = company if company && company.is_a?(Colppy::Company)
51
+ @id = params.delete(:idItem)
52
+ @name = params.delete(:descripcion)
53
+ @sku = params.delete(:codigo)
54
+ @data = params
55
+ end
56
+
57
+ def new?
58
+ id.nil? || id.empty?
59
+ end
60
+
61
+ def save
62
+ ensure_client_valid! && ensure_company_valid!
63
+
64
+ response = @client.call(
65
+ :product,
66
+ operation,
67
+ save_parameters
68
+ )
69
+ if response[:success]
70
+ response_data = response[:data]
71
+ case operation
72
+ when :create
73
+ @id = response_data[:idItem]
74
+ end
75
+ self
76
+ else
77
+ false
78
+ end
79
+ end
80
+
81
+ def name=(new_name)
82
+ @name = new_name
83
+ end
84
+
85
+ def sku=(new_sku)
86
+ @sku = new_sku
87
+ end
88
+
89
+ def []=(key, value)
90
+ key_sym = key.to_sym
91
+ if protected_data_keys.include?(key_sym)
92
+ raise ResourceError.new("You cannot change any of this values: #{protected_data_keys.join(", ")} manually")
93
+ end
94
+ @data[key_sym] = value
95
+ end
96
+
97
+ def params_for_invoice
98
+ {
99
+ idItem: id || "",
100
+ minimo: @data[:minimo] || "0",
101
+ codigo: sku || "",
102
+ tipoItem: @data[:tipoItem] || "P",
103
+ unidadMedida: @data[:unidadMedida] || "u",
104
+ Descripcion: name || "",
105
+ ImporteUnitario: @data[:precioVenta],
106
+ IVA: @data[:iva] || "21",
107
+ Comentario: @data[:detalle] || "",
108
+ idPlanCuenta: @data[:ctaIngresoVentas]
109
+ }
110
+ end
111
+
112
+ private
113
+
114
+ def attr_inspect
115
+ [:id, :sku, :name]
116
+ end
117
+
118
+ def protected_data_keys
119
+ [:idItem, :idEmpresa]
120
+ end
121
+
122
+ def operation
123
+ new? ? :create : :update
124
+ end
125
+
126
+ def save_parameters
127
+ [
128
+ @client.session_params,
129
+ params
130
+ ].inject(&:merge)
131
+ end
132
+
133
+ def params
134
+ {
135
+ idItem: id || "",
136
+ idEmpresa: @company.id,
137
+ codigo: sku || "",
138
+ descripcion: name || "",
139
+ detalle: @data[:detalle] || "",
140
+ precioVenta: @data[:precioVenta] || "0",
141
+ ultimoPrecioCompra: @data[:minimo] || "0",
142
+ ctaInventario: @data[:ctaInventario] || "",
143
+ ctaCostoVentas: @data[:ctaCostoVentas] || "",
144
+ ctaIngresoVentas: @data[:ctaIngresoVentas] || "",
145
+ iva: @data[:iva] || "21",
146
+ tipoItem: @data[:tipoItem] || "P",
147
+ unidadMedida: @data[:unidadMedida] || "u",
148
+ minimo: @data[:minimo] || "0"
149
+ }
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,343 @@
1
+ module Colppy
2
+ class SellInvoice < Resource
3
+ attr_reader :id, :number, :cae, :items, :data, :url
4
+ attr_accessor :payments
5
+
6
+ VALID_RECEIPT_TYPES = %w(4 6 8 NCV)
7
+
8
+ class << self
9
+ def all(client, company)
10
+ list(client, company)
11
+ end
12
+
13
+ def list(client, company, parameters = {})
14
+ call_parameters = base_params.merge(parameters)
15
+ response = client.call(
16
+ :sell_invoice,
17
+ :list,
18
+ extended_parameters(client, company, call_parameters)
19
+ )
20
+ if response[:success]
21
+ results = response[:data].map do |params|
22
+ new(params.merge(client: client, company: company))
23
+ end
24
+ parse_list_response(results, response[:total].to_i, call_parameters)
25
+ else
26
+ response
27
+ end
28
+ end
29
+
30
+ def get(client, company, id)
31
+ response = client.call(
32
+ :sell_invoice,
33
+ :read,
34
+ extended_parameters(client, company, { idFactura: id })
35
+ )
36
+ if response[:success]
37
+ new(extended_response(response, client, company))
38
+ else
39
+ response
40
+ end
41
+ end
42
+
43
+ private
44
+
45
+ def extended_parameters(client, company, parameters)
46
+ [ client.session_params,
47
+ company.params,
48
+ parameters
49
+ ].inject(&:merge)
50
+ end
51
+
52
+ def base_params
53
+ {
54
+ filter:[],
55
+ order: {
56
+ field: ["nroFactura"],
57
+ order: "desc"
58
+ }
59
+ }
60
+ end
61
+
62
+ def extended_response(response, client, company)
63
+ [ response[:infofactura],
64
+ items: response[:itemsFactura],
65
+ url: response[:UrlFacturaPdf],
66
+ client: client,
67
+ company: company
68
+ ].inject(&:merge)
69
+ end
70
+
71
+ end
72
+
73
+ def initialize(client: nil, company: nil, customer: nil, **params)
74
+ @client = client if client && client.is_a?(Colppy::Client)
75
+ @company = company if company && company.is_a?(Colppy::Company)
76
+ @customer = customer if customer && customer.is_a?(Colppy::Customer)
77
+
78
+ @id = params.delete(:idFactura)
79
+ @number = params.delete(:nroFactura)
80
+ @cae = params.delete(:cae)
81
+
82
+ @items = parse_items(params.delete(:items))
83
+ @payments = params.delete(:ItemsCobro) || []
84
+
85
+ @data = params
86
+ @url = build_pdf_url
87
+ end
88
+
89
+ def new?
90
+ id.nil? || id.empty?
91
+ end
92
+
93
+ def editable?
94
+ cae.nil? || cae.empty?
95
+ end
96
+
97
+ def save
98
+ ensure_editability! && ensure_client_valid! && ensure_company_valid! && ensure_customer_valid!
99
+
100
+ response = @client.call(
101
+ :sell_invoice,
102
+ :create,
103
+ save_parameters
104
+ )
105
+ binding.pry
106
+ if response[:success]
107
+ @id = response[:data][:idFactura]
108
+ self
109
+ else
110
+ false
111
+ end
112
+ end
113
+
114
+ def []=(key, value)
115
+ ensure_editability!
116
+
117
+ key_sym = key.to_sym
118
+ if protected_data_keys.include?(key_sym)
119
+ raise ResourceError.new("You cannot change any of this values: #{protected_data_keys.join(", ")} manually")
120
+ end
121
+ @data[key_sym] = value
122
+ end
123
+
124
+ def items=(new_items)
125
+ @charged_details = nil
126
+ @items = parse_items(new_items)
127
+ end
128
+
129
+ private
130
+
131
+ def attr_inspect
132
+ [:id, :number, :cae]
133
+ end
134
+
135
+ def protected_data_keys
136
+ [:idFactura, :idEmpresa, :nroFactura]
137
+ end
138
+
139
+ def parse_items(new_items)
140
+ return [] if new_items.nil? || new_items.empty?
141
+
142
+ new_items.map do |item|
143
+ item.tap do |hash|
144
+ if item[:idItem] && (item[:product].nil? || item[:product].empty?)
145
+ item[:product] = @company.product(item[:idItem])
146
+ end
147
+ end
148
+ end
149
+ end
150
+
151
+ def build_pdf_url
152
+ return if data[:url].nil? || data[:url].empty?
153
+
154
+ data[:url] = "#{data[:url]}?usuario=#{@client.username}&claveSesion=#{@client.session_key}&idEmpresa=#{@company.id}&idFactura=#{id}&idCliente=#{data[:idCliente]}"
155
+ end
156
+
157
+ def save_parameters
158
+ [
159
+ @client.session_params,
160
+ general_params,
161
+ invoice_payments_params,
162
+ itemsFactura: invoice_items_params,
163
+ totalesiva: total_taxes_params
164
+ ].inject(&:merge)
165
+ end
166
+
167
+ def general_params
168
+ {
169
+ idCliente: @customer.id || @data[:idCliente] || "",
170
+ idEmpresa: @company.id,
171
+ descripcion: @data[:descripcion] || "",
172
+ fechaFactura: valid_date(@data[:fechaFactura]),
173
+ idCondicionPago: @data[:idCondicionPago] || "Contado",
174
+ fechaPago: valid_date(@data[:fechaPago]),
175
+ idEstadoAnterior: @data[:idEstadoAnterior] || "",
176
+ idEstadoFactura: @data[:idEstadoFactura] || "Cobrada",
177
+ idFactura: id || "",
178
+ idMedioCobro: @data[:idMedioCobro] || "Efectivo",
179
+ idMoneda: @data[:idMoneda] || "1",
180
+ idTipoComprobante: receipt_type,
181
+ idTipoFactura: invoice_type,
182
+ netoGravado: (charged_details[:total_taxed] || 0.00).to_s,
183
+ netoNoGravado: (charged_details[:total_non_taxed] || 0.00).to_s,
184
+ nroFactura1: @data[:nroFactura1] || "0001",
185
+ nroFactura2: @data[:nroFactura2] || "",
186
+ percepcionIVA: (@data[:percepcionIVA] || 0.00).to_s,
187
+ percepcionIIBB: (@data[:percepcionIIBB] || 0.00).to_s,
188
+ totalFactura: (charged_details[:total] || 0.00).to_s,
189
+ totalIVA: (charged_details[:tax_total] || 0.00).to_s,
190
+ valorCambio: @data[:valorCambio] || "1",
191
+ }.tap do |params|
192
+ params[:labelfe] = "Factura Electrónica" if @data[:labelfe]
193
+ end
194
+ end
195
+
196
+ def invoice_items_params
197
+ items.map do |item|
198
+ if item[:product] && item[:product].is_a?(Colppy::Product)
199
+ product = item[:product]
200
+ [
201
+ product.params_for_invoice,
202
+ item_params(item, false)
203
+ ].inject(&:merge)
204
+ elsif item.is_a?(Hash)
205
+ item_params(item)
206
+ end
207
+ end
208
+ end
209
+
210
+ def item_params(item, fill_empty = true)
211
+ {
212
+ Cantidad: item[:Cantidad],
213
+ porcDesc: item[:porcDesc] || "0.00"
214
+ }.tap do |params|
215
+ if value = item[:ImporteUnitario] || fill_empty
216
+ params[:ImporteUnitario] = value || ""
217
+ end
218
+ if value = item[:idPlanCuenta] || fill_empty
219
+ params[:idPlanCuenta] = value || ""
220
+ end
221
+ if value = item[:IVA] || fill_empty
222
+ params[:IVA] = value || "21"
223
+ end
224
+ if value = item[:Comentario] || fill_empty
225
+ params[:Comentario] = value || ""
226
+ end
227
+ end
228
+ end
229
+
230
+ def invoice_payments_params
231
+ return {} unless payments
232
+ payment_items = payments.map do |payment|
233
+ {
234
+ idMedioCobro: payment[:idMedioCobro] || "Efectivo",
235
+ idPlanCuenta: payment[:idPlanCuenta] || "Caja en pesos",
236
+ Banco: payment[:Banco] || "",
237
+ nroCheque: payment[:nroCheque] || "",
238
+ fechaValidez: payment[:fechaValidez] || "",
239
+ importe: payment[:importe] || "0",
240
+ VAD: payment[:VAD] || "S"
241
+ }
242
+ end
243
+ { ItemsCobro: payment_items }
244
+ end
245
+
246
+ def total_taxes_params
247
+ charged_details[:tax_details].map do |tax, values|
248
+ {
249
+ alicuotaIva: tax.to_s,
250
+ baseImpIva: values[:total_taxed].to_s,
251
+ importeIva: values[:tax_total].to_s
252
+ }
253
+ end
254
+ end
255
+
256
+ def charged_details
257
+ return @charged_details unless @charged_details.nil? || @charged_details.empty?
258
+
259
+ if items.nil? || items.empty?
260
+ raise DataError.new("In order to save an SellInvoice you should at least have one item in it")
261
+ else
262
+ default_object = {
263
+ total_taxed: 0.00,
264
+ total_nontaxed: 0.00,
265
+ total: 0.00,
266
+ tax_total: 0.00,
267
+ tax_details:{}
268
+ }
269
+ @charged_details = items.each_with_object(default_object) do |item, result|
270
+ product_data = (item[:product] && item[:product].data) || {}
271
+ iva = (item[:IVA] || product_data[:iva] || 0).to_f
272
+ charged = (item[:ImporteUnitario] || product_data[:precioVenta] || 0).to_f
273
+ quantity = (item[:Cantidad] || 0).to_i
274
+ total = ( charged * quantity ).round(2)
275
+ if iva > 0
276
+ tax = (( total * iva ) / 100).round(2)
277
+ result[:tax_total] += tax
278
+ result[:total_taxed] += total - tax
279
+ result[:tax_details][iva] = {
280
+ tax_total: result[:tax_total],
281
+ total_taxed: result[:total_taxed]
282
+ }
283
+ else
284
+ result[:total_nontaxed] += total
285
+ end
286
+ result[:total] += total
287
+ end
288
+ end
289
+ end
290
+
291
+ def ensure_editability!
292
+ unless editable?
293
+ raise ResourceError.new("You cannot change any value of this invoice, because it's already processed by AFIP. You should create a new one instead")
294
+ end
295
+ end
296
+
297
+ def invoice_status
298
+ type = @data[:idEstadoFactura]
299
+ if type && VALID_INVOICE_STATUS.include?(type)
300
+ type
301
+ else
302
+ raise DataError.new("The value of idEstadoFactura:#{type} is invalid. The value should be any of this ones: #{VALID_INVOICE_STATUS.join(", ")}")
303
+ end
304
+ end
305
+
306
+ def invoice_type
307
+ type = @data[:idTipoFactura]
308
+ if type && VALID_INVOICE_TYPES.include?(type)
309
+ type
310
+ else
311
+ raise DataError.new("The idTipoFactura:#{type} is invalid. The value should be any of this ones: #{VALID_INVOICE_TYPES.join(", ")}")
312
+ end
313
+ end
314
+
315
+ def receipt_type
316
+ type = @data[:idTipoComprobante]
317
+ if type && VALID_RECEIPT_TYPES.include?(type)
318
+ type
319
+ else
320
+ raise DataError.new("The idTipoComprobante:#{type} is invalid. The value should be any of this ones: #{VALID_RECEIPT_TYPES.join(", ")}")
321
+ ""
322
+ end
323
+ end
324
+ end
325
+ end
326
+
327
+ # "totalesiva":[
328
+ # {
329
+ # alicuotaIva: "0",
330
+ # baseImpIva: "0.00",
331
+ # importeIva: "0.00"
332
+ # },
333
+ # {
334
+ # alicuotaIva: "21",
335
+ # baseImpIva: "101.65",
336
+ # importeIva: "21.35"
337
+ # },
338
+ # {
339
+ # alicuotaIva: "27",
340
+ # baseImpIva: "0.00",
341
+ # importeIva: "0.00"
342
+ # }
343
+ # ]