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