ydim 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +6 -0
- data/LICENSE.txt +339 -0
- data/Manifest.txt +43 -0
- data/README.txt +21 -0
- data/Rakefile +28 -0
- data/bin/ydim-edit +55 -0
- data/bin/ydim-inject +33 -0
- data/bin/ydimd +55 -0
- data/install.rb +1098 -0
- data/lib/ydim/autoinvoicer.rb +41 -0
- data/lib/ydim/client.rb +29 -0
- data/lib/ydim/config.rb +32 -0
- data/lib/ydim/currency_converter.rb +28 -0
- data/lib/ydim/currency_updater.rb +39 -0
- data/lib/ydim/debitor.rb +86 -0
- data/lib/ydim/factory.rb +58 -0
- data/lib/ydim/invoice.rb +146 -0
- data/lib/ydim/item.rb +36 -0
- data/lib/ydim/mail.rb +95 -0
- data/lib/ydim/odba.rb +39 -0
- data/lib/ydim/root_session.rb +143 -0
- data/lib/ydim/root_user.rb +14 -0
- data/lib/ydim/server.rb +165 -0
- data/lib/ydim/smtp_tls.rb +58 -0
- data/lib/ydim/util.rb +16 -0
- data/test/data/search.html +58 -0
- data/test/stub/odba.rb +21 -0
- data/test/suite.rb +10 -0
- data/test/test_autoinvoicer.rb +51 -0
- data/test/test_currency_converter.rb +37 -0
- data/test/test_currency_updater.rb +45 -0
- data/test/test_debitor.rb +96 -0
- data/test/test_factory.rb +99 -0
- data/test/test_invoice.rb +131 -0
- data/test/test_item.rb +41 -0
- data/test/test_mail.rb +94 -0
- data/test/test_root_session.rb +347 -0
- data/test/test_root_user.rb +20 -0
- data/test/test_server.rb +142 -0
- metadata +137 -0
@@ -0,0 +1,41 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# AutoInvoicer -- ydim -- 13.01.2006 -- hwyss@ywesee.com
|
3
|
+
|
4
|
+
require 'date'
|
5
|
+
require 'pdfinvoice/config'
|
6
|
+
require 'pdfinvoice/invoice'
|
7
|
+
require 'odba/odba'
|
8
|
+
require 'ydim/debitor'
|
9
|
+
require 'ydim/mail'
|
10
|
+
|
11
|
+
module YDIM
|
12
|
+
class AutoInvoicer
|
13
|
+
def initialize(serv)
|
14
|
+
@serv = serv
|
15
|
+
end
|
16
|
+
def run
|
17
|
+
Debitor.odba_extent { |debitor|
|
18
|
+
autoinvoice(debitor)
|
19
|
+
}
|
20
|
+
end
|
21
|
+
def autoinvoice(debitor)
|
22
|
+
today = Date.today
|
23
|
+
next_month = today >> 1
|
24
|
+
debitor.autoinvoices.each { |auto|
|
25
|
+
if(auto.total_netto > 0)
|
26
|
+
case auto.date
|
27
|
+
when today
|
28
|
+
Mail.send_invoice(@serv.config, generate(auto))
|
29
|
+
when next_month
|
30
|
+
Mail.send_reminder(@serv.config, auto)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
}
|
34
|
+
end
|
35
|
+
def generate(autoinvoice)
|
36
|
+
ODBA.transaction {
|
37
|
+
@serv.factory.generate_invoice(autoinvoice)
|
38
|
+
}
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/lib/ydim/client.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# Client -- ydim -- 12.01.2006 -- hwyss@ywesee.com
|
3
|
+
|
4
|
+
require 'drb'
|
5
|
+
|
6
|
+
module YDIM
|
7
|
+
class Client
|
8
|
+
include DRb::DRbUndumped
|
9
|
+
attr_reader :session
|
10
|
+
def initialize(config)
|
11
|
+
@config = config
|
12
|
+
DRb.start_service(config.client_url)
|
13
|
+
end
|
14
|
+
def login(server, private_key)
|
15
|
+
@server = server
|
16
|
+
@session = @server.login(self, @config.user) { |challenge|
|
17
|
+
if(private_key.respond_to?(:syssign))
|
18
|
+
private_key.syssign(challenge)
|
19
|
+
end
|
20
|
+
}
|
21
|
+
end
|
22
|
+
def logout
|
23
|
+
@server.logout(@session) if(@server)
|
24
|
+
end
|
25
|
+
def method_missing(meth, *args, &block)
|
26
|
+
@session.send(meth, *args, &block)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/ydim/config.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# CONFIG -- ydim -- 12.01.2006 -- hwyss@ywesee.com
|
3
|
+
|
4
|
+
require 'rclconf'
|
5
|
+
require 'fileutils'
|
6
|
+
|
7
|
+
module YDIM
|
8
|
+
VERSION = '1.0.0'
|
9
|
+
class Client
|
10
|
+
home_dir = ENV['HOME'] || '/tmp'
|
11
|
+
ydim_default_dir = File.join(home_dir, '.ydim')
|
12
|
+
default_config_files = [
|
13
|
+
File.join(ydim_default_dir, 'ydim.yml'),
|
14
|
+
'/etc/ydim/ydim.yml',
|
15
|
+
]
|
16
|
+
defaults = {
|
17
|
+
'client_url' => 'druby://localhost:0',
|
18
|
+
'config' => default_config_files,
|
19
|
+
'private_key' => File.join(home_dir, '.ssh', 'id_dsa'),
|
20
|
+
'user' => nil,
|
21
|
+
'server_url' => 'druby://localhost:12375',
|
22
|
+
'currency' => 'EUR',
|
23
|
+
'invoice_description' => 'Auto-Invoice',
|
24
|
+
'mail_charset' => 'iso-8859-1',
|
25
|
+
'payment_period' => 30,
|
26
|
+
}
|
27
|
+
config = RCLConf::RCLConf.new(ARGV, defaults)
|
28
|
+
config.load(config.config)
|
29
|
+
|
30
|
+
CONFIG = config
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# CurrencyConverter -- ydim -- 01.02.2006 -- hwyss@ywesee.com
|
3
|
+
|
4
|
+
module YDIM
|
5
|
+
class MobileCurrencyConverter
|
6
|
+
def initialize(conversions={})
|
7
|
+
@conversions = conversions
|
8
|
+
end
|
9
|
+
def convert(amount, origin, target)
|
10
|
+
return amount if(origin == target)
|
11
|
+
amount.to_f * @conversions.fetch([origin, target]) {
|
12
|
+
raise "Unknown Conversion '#{origin}' -> '#{target}'"
|
13
|
+
}
|
14
|
+
end
|
15
|
+
end
|
16
|
+
class CurrencyConverter < MobileCurrencyConverter
|
17
|
+
def drb_dup
|
18
|
+
MobileCurrencyConverter.new(@conversions)
|
19
|
+
end
|
20
|
+
def known_currencies
|
21
|
+
@conversions.keys.collect { |origin, target| origin }.uniq.size
|
22
|
+
end
|
23
|
+
def store(origin, target, rate)
|
24
|
+
@conversions.store([target, origin], 1.0/rate)
|
25
|
+
@conversions.store([origin, target], rate)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# CurrencyUpdater -- ydim -- 01.02.2006 -- hwyss@ywesee.com
|
3
|
+
|
4
|
+
require 'net/http'
|
5
|
+
|
6
|
+
module YDIM
|
7
|
+
class CurrencyUpdater
|
8
|
+
def initialize(serv)
|
9
|
+
@serv = serv
|
10
|
+
end
|
11
|
+
def run
|
12
|
+
curr = @serv.config.currencies.dup
|
13
|
+
while(origin = curr.shift)
|
14
|
+
curr.each { |target|
|
15
|
+
update_conversion(origin, target)
|
16
|
+
}
|
17
|
+
end
|
18
|
+
@serv.currency_converter.odba_store
|
19
|
+
end
|
20
|
+
def extract_conversion(html)
|
21
|
+
if(match = /1\s+[^<>=]+=\s+(\d+\.\d+)/.match(html))
|
22
|
+
match[1]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
def get_conversion(origin, target)
|
26
|
+
extract_conversion(get_html(origin, target)).to_f
|
27
|
+
end
|
28
|
+
def get_html(origin, target)
|
29
|
+
## not in test-suite, test manually when modified
|
30
|
+
Net::HTTP.start('www.google.com') { |session|
|
31
|
+
session.get("/search?q=1+#{origin.upcase}+in+#{target.upcase}").body
|
32
|
+
}
|
33
|
+
end
|
34
|
+
def update_conversion(origin, target)
|
35
|
+
@serv.currency_converter.store(origin, target,
|
36
|
+
get_conversion(origin, target))
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/ydim/debitor.rb
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# Debitor -- ydim -- 10.01.2006 -- hwyss@ywesee.com
|
3
|
+
|
4
|
+
require 'ydim/item'
|
5
|
+
|
6
|
+
module YDIM
|
7
|
+
class Debitor
|
8
|
+
attr_reader :autoinvoices, :unique_id, :invoices
|
9
|
+
attr_accessor :address_lines, :contact, :contact_firstname,
|
10
|
+
:contact_title, :country, :debitor_type, :email, :location,
|
11
|
+
:name, :salutation, :phone
|
12
|
+
def initialize(unique_id)
|
13
|
+
@unique_id = unique_id
|
14
|
+
@address_lines = []
|
15
|
+
@invoices = []
|
16
|
+
@autoinvoices = []
|
17
|
+
end
|
18
|
+
def autoinvoice(unique_id)
|
19
|
+
@autoinvoices.find { |invoice| invoice.unique_id == unique_id }
|
20
|
+
end
|
21
|
+
def autoinvoice_infos
|
22
|
+
@autoinvoices.collect { |inv| inv.info }
|
23
|
+
end
|
24
|
+
def add_autoinvoice(invoice)
|
25
|
+
@autoinvoices.push(invoice)
|
26
|
+
invoice
|
27
|
+
end
|
28
|
+
def add_invoice(invoice)
|
29
|
+
@invoices.push(invoice)
|
30
|
+
invoice
|
31
|
+
end
|
32
|
+
def address
|
33
|
+
lns = [@name]
|
34
|
+
lns.push(["z.H.", @salutation,
|
35
|
+
@contact_firstname, @contact].compact.join(' '))
|
36
|
+
lns.push(@contact_title)
|
37
|
+
lns.concat(@address_lines)
|
38
|
+
lns.push(@location, @country)
|
39
|
+
if @email
|
40
|
+
lns.push "To: #@email"
|
41
|
+
unless emails_cc.empty?
|
42
|
+
lns.push "Cc: #{emails_cc.join(', ')}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
lns.compact!
|
46
|
+
lns
|
47
|
+
end
|
48
|
+
def delete_autoinvoice(invoice)
|
49
|
+
@autoinvoices.delete(invoice)
|
50
|
+
end
|
51
|
+
def delete_invoice(invoice)
|
52
|
+
@invoices.delete(invoice)
|
53
|
+
end
|
54
|
+
def emails_cc
|
55
|
+
@emails_cc ||= []
|
56
|
+
end
|
57
|
+
def emails_cc=(emails)
|
58
|
+
@emails_cc = emails
|
59
|
+
end
|
60
|
+
def emails
|
61
|
+
[ @email ].concat(emails_cc).compact
|
62
|
+
end
|
63
|
+
def emails=(emails)
|
64
|
+
@email = emails.shift
|
65
|
+
@emails_cc = emails
|
66
|
+
emails
|
67
|
+
end
|
68
|
+
def foreign?
|
69
|
+
@country && !@country.to_s.strip.empty? \
|
70
|
+
&& @country != Server.config.home_country
|
71
|
+
end
|
72
|
+
def invoice(unique_id)
|
73
|
+
@invoices.find { |invoice| invoice.unique_id == unique_id }
|
74
|
+
end
|
75
|
+
def invoice_infos(status)
|
76
|
+
@invoices.select { |inv|
|
77
|
+
inv.status == status
|
78
|
+
}.collect { |inv| inv.info }
|
79
|
+
end
|
80
|
+
def next_invoice_date
|
81
|
+
@autoinvoices.collect { |inv| inv.date }.compact.min
|
82
|
+
end
|
83
|
+
private
|
84
|
+
include ItemId
|
85
|
+
end
|
86
|
+
end
|
data/lib/ydim/factory.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# Factory -- ydim -- 16.01.2006 -- hwyss@ywesee.com
|
3
|
+
|
4
|
+
require 'ydim/debitor'
|
5
|
+
require 'ydim/invoice'
|
6
|
+
|
7
|
+
module YDIM
|
8
|
+
class Factory
|
9
|
+
def initialize(serv)
|
10
|
+
@serv = serv
|
11
|
+
end
|
12
|
+
def create_autoinvoice(debitor)
|
13
|
+
id = @serv.id_server.next_id(:autoinvoice,
|
14
|
+
@serv.config.invoice_number_start)
|
15
|
+
invoice = AutoInvoice.new(id)
|
16
|
+
yield(invoice) if(block_given?)
|
17
|
+
if debitor.foreign?
|
18
|
+
invoice.suppress_vat = true
|
19
|
+
end
|
20
|
+
invoice.debitor = debitor
|
21
|
+
debitor.autoinvoices.odba_store
|
22
|
+
invoice
|
23
|
+
end
|
24
|
+
def create_invoice(debitor)
|
25
|
+
id = @serv.id_server.next_id(:invoice, @serv.config.invoice_number_start)
|
26
|
+
invoice = Invoice.new(id)
|
27
|
+
yield(invoice) if(block_given?)
|
28
|
+
invoice.debitor = debitor
|
29
|
+
if debitor.foreign?
|
30
|
+
invoice.suppress_vat = true
|
31
|
+
end
|
32
|
+
debitor.invoices.odba_store
|
33
|
+
invoice
|
34
|
+
end
|
35
|
+
def generate_invoice(autoinvoice)
|
36
|
+
create_invoice(autoinvoice.debitor) { |inv|
|
37
|
+
date = autoinvoice.date || Date.today
|
38
|
+
nextdate = autoinvoice.advance(date)
|
39
|
+
inv.date = date
|
40
|
+
inv.currency = autoinvoice.currency
|
41
|
+
inv.description = sprintf("%s %s-%s", autoinvoice.description,
|
42
|
+
date.strftime("%d.%m.%Y"),
|
43
|
+
(nextdate - 1).strftime("%d.%m.%Y"))
|
44
|
+
inv.precision = autoinvoice.precision
|
45
|
+
inv.payment_period = autoinvoice.payment_period
|
46
|
+
autoinvoice.items.each { |item|
|
47
|
+
nitem = item.dup
|
48
|
+
nitem.time = Time.now
|
49
|
+
nitem.expiry_time = Time.local(nextdate.year, nextdate.month,
|
50
|
+
nextdate.day)
|
51
|
+
nitem.vat_rate = @serv.config.vat_rate
|
52
|
+
inv.add_item(nitem)
|
53
|
+
}
|
54
|
+
autoinvoice.odba_store
|
55
|
+
}
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
data/lib/ydim/invoice.rb
ADDED
@@ -0,0 +1,146 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# Invoice -- ydim -- 11.01.2006 -- hwyss@ywesee.com
|
3
|
+
|
4
|
+
require 'pdfinvoice/config'
|
5
|
+
require 'pdfinvoice/invoice'
|
6
|
+
require 'ydim/item'
|
7
|
+
require 'ydim/server'
|
8
|
+
|
9
|
+
module YDIM
|
10
|
+
class Invoice
|
11
|
+
class Info
|
12
|
+
KEYS = [:unique_id, :date, :description, :payment_received, :currency,
|
13
|
+
:status, :debitor_name, :debitor_email, :debitor_id, :due_date,
|
14
|
+
:total_netto, :total_brutto, :deleted, :suppress_vat ]
|
15
|
+
attr_accessor *KEYS
|
16
|
+
def initialize(invoice)
|
17
|
+
KEYS.each { |key|
|
18
|
+
instance_variable_set("@#{key}", invoice.send(key))
|
19
|
+
}
|
20
|
+
end
|
21
|
+
end
|
22
|
+
attr_reader :unique_id, :debitor, :items, :suppress_vat
|
23
|
+
attr_accessor :precision, :date, :description, :payment_period,
|
24
|
+
:payment_received, :currency, :deleted
|
25
|
+
def Invoice.sum(key)
|
26
|
+
define_method(key) {
|
27
|
+
@items.inject(0.0) { |value, item| value + item.send(key) }
|
28
|
+
}
|
29
|
+
end
|
30
|
+
def initialize(unique_id)
|
31
|
+
@unique_id = unique_id
|
32
|
+
@items = []
|
33
|
+
@precision = 2
|
34
|
+
@payment_period = 10
|
35
|
+
end
|
36
|
+
def invoice_key
|
37
|
+
:invoice
|
38
|
+
end
|
39
|
+
def add_item(item)
|
40
|
+
item.index = next_item_id
|
41
|
+
@items.push(item)
|
42
|
+
item
|
43
|
+
end
|
44
|
+
def debitor=(debitor)
|
45
|
+
if(@debitor)
|
46
|
+
@debitor.send("delete_#{invoice_key}", self)
|
47
|
+
end
|
48
|
+
if(debitor)
|
49
|
+
debitor.send("add_#{invoice_key}", self)
|
50
|
+
end
|
51
|
+
@debitor = debitor
|
52
|
+
end
|
53
|
+
def debitor_email
|
54
|
+
@debitor.email if(@debitor)
|
55
|
+
end
|
56
|
+
def debitor_id
|
57
|
+
@debitor.unique_id if(@debitor)
|
58
|
+
end
|
59
|
+
def debitor_name
|
60
|
+
@debitor.name if(@debitor)
|
61
|
+
end
|
62
|
+
def due_date
|
63
|
+
if(@date && !@payment_received)
|
64
|
+
@date + @payment_period.to_i
|
65
|
+
end
|
66
|
+
end
|
67
|
+
def empty?
|
68
|
+
@items.empty?
|
69
|
+
end
|
70
|
+
def info
|
71
|
+
Info.new(self)
|
72
|
+
end
|
73
|
+
def item(index)
|
74
|
+
@items.find { |item| item.index == index }
|
75
|
+
end
|
76
|
+
def status
|
77
|
+
if(@deleted)
|
78
|
+
'is_trash'
|
79
|
+
elsif(@payment_received)
|
80
|
+
'is_paid'
|
81
|
+
elsif(@date && @payment_period \
|
82
|
+
&& ((@date + @payment_period) < Date.today))
|
83
|
+
'is_due'
|
84
|
+
else
|
85
|
+
'is_open'
|
86
|
+
end
|
87
|
+
end
|
88
|
+
def pdf_invoice(sort_args={})
|
89
|
+
config = PdfInvoice.config.dup
|
90
|
+
config.formats['quantity'] = "%1.#{@precision}f"
|
91
|
+
config.formats['total'] = "#{@currency} %1.2f"
|
92
|
+
invoice = PdfInvoice::Invoice.new(config)
|
93
|
+
invoice.date = @date
|
94
|
+
invoice.invoice_number = @unique_id
|
95
|
+
invoice.description = @description
|
96
|
+
invoice.debitor_address = @debitor.address
|
97
|
+
items = @items
|
98
|
+
if sort_by = sort_args[:sortby]
|
99
|
+
begin
|
100
|
+
items = items.sort_by do |item|
|
101
|
+
item.send(sort_by)
|
102
|
+
end
|
103
|
+
if sort_args[:sort_reverse]
|
104
|
+
items.reverse!
|
105
|
+
end
|
106
|
+
rescue StandardError
|
107
|
+
end
|
108
|
+
end
|
109
|
+
invoice.items = items.collect { |item|
|
110
|
+
[ item.time, item.text, item.unit, item.quantity.to_f,
|
111
|
+
item.price.to_f, item.vat.to_f ]
|
112
|
+
}
|
113
|
+
invoice
|
114
|
+
end
|
115
|
+
def suppress_vat= bool
|
116
|
+
rate = bool ? 0 : YDIM::Server.config.vat_rate
|
117
|
+
@items.each do |item| item.vat_rate = rate end
|
118
|
+
@suppress_vat = bool
|
119
|
+
end
|
120
|
+
def to_pdf(sort_args={})
|
121
|
+
pdf_invoice(sort_args).to_pdf
|
122
|
+
end
|
123
|
+
sum :total_brutto
|
124
|
+
sum :total_netto
|
125
|
+
sum :vat
|
126
|
+
private
|
127
|
+
include ItemId
|
128
|
+
end
|
129
|
+
class AutoInvoice < Invoice
|
130
|
+
@@year_ptrn = %r{<year>([^<])*</year>}
|
131
|
+
attr_accessor :invoice_interval, :reminder_body, :reminder_subject
|
132
|
+
def invoice_key
|
133
|
+
:autoinvoice
|
134
|
+
end
|
135
|
+
def advance(date)
|
136
|
+
months = @invoice_interval.to_s[/\d+/].to_i
|
137
|
+
if @reminder_subject
|
138
|
+
@reminder_subject.gsub!(@@year_ptrn) do |match|
|
139
|
+
years = months / 12
|
140
|
+
match.gsub(%r{\d+}) do |year| (year.to_i + years).to_s end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
@date = date >> months
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
data/lib/ydim/item.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# Item -- ydim -- 11.01.2006 -- hwyss@ywesee.com
|
3
|
+
|
4
|
+
module YDIM
|
5
|
+
class Item
|
6
|
+
DATA_KEYS = [ :data, :expiry_time, :item_type, :price, :quantity, :text,
|
7
|
+
:time, :unit, :vat_rate ]
|
8
|
+
attr_accessor :index, *DATA_KEYS
|
9
|
+
def initialize(data={})
|
10
|
+
update(data)
|
11
|
+
end
|
12
|
+
def total_brutto
|
13
|
+
total_netto + vat
|
14
|
+
end
|
15
|
+
def total_netto
|
16
|
+
@quantity.to_f * @price.to_f
|
17
|
+
end
|
18
|
+
def update(data)
|
19
|
+
data.each { |key, val|
|
20
|
+
if(DATA_KEYS.include?(key.to_sym))
|
21
|
+
instance_variable_set("@#{key}", val)
|
22
|
+
end
|
23
|
+
}
|
24
|
+
end
|
25
|
+
def vat
|
26
|
+
total_netto * (@vat_rate.to_f / 100.0)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
module ItemId
|
30
|
+
def next_item_id
|
31
|
+
id = @next_item_id.to_i
|
32
|
+
@next_item_id = id.next
|
33
|
+
id
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/ydim/mail.rb
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# Mail -- ydim -- 18.01.2006 -- hwyss@ywesee.com
|
3
|
+
|
4
|
+
require 'net/smtp'
|
5
|
+
require 'rmail'
|
6
|
+
require 'ydim/smtp_tls'
|
7
|
+
|
8
|
+
module YDIM
|
9
|
+
module Mail
|
10
|
+
def Mail.body(config, debitor, invoice)
|
11
|
+
salutation = config.salutation[debitor.salutation.to_s]
|
12
|
+
sprintf(config.mail_body, salutation, debitor.contact, invoice.description)
|
13
|
+
end
|
14
|
+
def Mail.send_invoice(config, invoice, sort_args={})
|
15
|
+
debitor = invoice.debitor
|
16
|
+
to = debitor.email
|
17
|
+
subject = sprintf('Rechnung %s #%i, %s', debitor.name,
|
18
|
+
invoice.unique_id, invoice.description)
|
19
|
+
invoice_name = sprintf("%s.pdf", subject.tr(' /', '_-'))
|
20
|
+
mpart = RMail::Message.new
|
21
|
+
header = mpart.header
|
22
|
+
header.to = to
|
23
|
+
cc = header.cc = debitor.emails_cc
|
24
|
+
header.from = config.mail_from
|
25
|
+
header.subject = encode_subject config, subject
|
26
|
+
header.date = Time.now
|
27
|
+
tpart = RMail::Message.new
|
28
|
+
mpart.add_part(tpart)
|
29
|
+
tpart.header.add('Content-Type', 'text/plain', nil,
|
30
|
+
'charset' => config.mail_charset)
|
31
|
+
tpart.body = body(config, debitor, invoice)
|
32
|
+
fpart = RMail::Message.new
|
33
|
+
mpart.add_part(fpart)
|
34
|
+
header = fpart.header
|
35
|
+
header.add('Content-Type', 'application/pdf')
|
36
|
+
header.add('Content-Disposition', 'attachment', nil,
|
37
|
+
{'filename' => invoice_name })
|
38
|
+
header.add('Content-Transfer-Encoding', 'base64')
|
39
|
+
fpart.body = [invoice.to_pdf(sort_args)].pack('m')
|
40
|
+
recipients = config.mail_recipients.dup.push(to).concat(cc).uniq
|
41
|
+
Net::SMTP.start(config.smtp_server, config.smtp_port,
|
42
|
+
config.smtp_domain, config.smtp_user, config.smtp_pass,
|
43
|
+
config.smtp_authtype) { |smtp|
|
44
|
+
recipients.each { |recipient|
|
45
|
+
smtp.sendmail(mpart.to_s, config.smtp_user, recipient)
|
46
|
+
}
|
47
|
+
}
|
48
|
+
recipients
|
49
|
+
rescue Timeout::Error
|
50
|
+
retries ||= 3
|
51
|
+
if retries > 0
|
52
|
+
sleep 3 - retries
|
53
|
+
retries -= 1
|
54
|
+
retry
|
55
|
+
else
|
56
|
+
raise
|
57
|
+
end
|
58
|
+
end
|
59
|
+
def Mail.send_reminder(config, autoinvoice)
|
60
|
+
subject = autoinvoice.reminder_subject.to_s.strip
|
61
|
+
subject.gsub! %r{<year>\s*}, ''
|
62
|
+
subject.gsub! %r{\s*</year>}, ''
|
63
|
+
body = autoinvoice.reminder_body.to_s.strip
|
64
|
+
body.gsub! %r{<invoice>\s*}, ''
|
65
|
+
body.gsub! %r{\s*</invoice>}, ''
|
66
|
+
unless(subject.empty? || body.empty?)
|
67
|
+
debitor = autoinvoice.debitor
|
68
|
+
to = debitor.email
|
69
|
+
mpart = RMail::Message.new
|
70
|
+
header = mpart.header
|
71
|
+
header.to = to
|
72
|
+
cc = header.cc = debitor.emails_cc
|
73
|
+
header.from = config.mail_from
|
74
|
+
header.subject = encode_subject config, subject
|
75
|
+
header.date = Time.now
|
76
|
+
header.add('Content-Type', 'text/plain', nil,
|
77
|
+
'charset' => config.mail_charset)
|
78
|
+
mpart.body = body
|
79
|
+
recipients = config.mail_recipients.dup.push(to).concat(cc).uniq
|
80
|
+
Net::SMTP.start(config.smtp_server, config.smtp_port,
|
81
|
+
config.smtp_domain, config.smtp_user, config.smtp_pass,
|
82
|
+
config.smtp_authtype) { |smtp|
|
83
|
+
recipients.each { |recipient|
|
84
|
+
smtp.sendmail(mpart.to_s, config.smtp_user, recipient)
|
85
|
+
}
|
86
|
+
}
|
87
|
+
recipients
|
88
|
+
end
|
89
|
+
end
|
90
|
+
def Mail.encode_subject(config, subject)
|
91
|
+
encoded = [subject].pack('M').gsub("=\n", '').gsub(' ', '=20')
|
92
|
+
sprintf("=?%s?q?%s?=", config.mail_charset, encoded)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
data/lib/ydim/odba.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# ODBA -- ydim -- 10.01.2006 -- hwyss@ywesee.com
|
3
|
+
|
4
|
+
require 'odba'
|
5
|
+
require 'ydim/currency_converter'
|
6
|
+
require 'ydim/debitor'
|
7
|
+
require 'ydim/invoice'
|
8
|
+
|
9
|
+
module ODBA
|
10
|
+
module Persistable
|
11
|
+
alias :save :odba_store
|
12
|
+
end
|
13
|
+
end
|
14
|
+
module YDIM
|
15
|
+
class CurrencyConverter
|
16
|
+
include ODBA::Persistable
|
17
|
+
ODBA_SERIALIZABLE = ['@conversions']
|
18
|
+
end
|
19
|
+
class Debitor
|
20
|
+
include ODBA::Persistable
|
21
|
+
ODBA_SERIALIZABLE = ['@address_lines', '@emails_cc', '@hosting_items']
|
22
|
+
odba_index :email
|
23
|
+
odba_index :name
|
24
|
+
odba_index :unique_id
|
25
|
+
end
|
26
|
+
class Invoice
|
27
|
+
include ODBA::Persistable
|
28
|
+
class << self
|
29
|
+
alias :all :odba_extent
|
30
|
+
end
|
31
|
+
alias :save :odba_store
|
32
|
+
ODBA_SERIALIZABLE = ['@items']
|
33
|
+
odba_index :status
|
34
|
+
odba_index :unique_id, :class_filter => :instance_of?
|
35
|
+
end
|
36
|
+
class AutoInvoice
|
37
|
+
odba_index :unique_id, :class_filter => :instance_of?
|
38
|
+
end
|
39
|
+
end
|