gnucash-invoice 0.0.1

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.
@@ -0,0 +1,44 @@
1
+ # GnuCash::Invoice
2
+
3
+ GnuCash invoice printer for human beings.
4
+
5
+
6
+ ## Installation
7
+
8
+ $ gem install gnucash-invoice
9
+
10
+
11
+ ## Usage
12
+
13
+ GnuCash invoice printer currently supports SQLite and the usage is dead simple.
14
+ List all invoices that can be printed:
15
+
16
+ $ gnucash-invoice --dbpath /path/to/db.sqlite3
17
+
18
+ #00004 Customer A (2012-11-24)
19
+ #00005 Customer B (2012-11-24)
20
+ ...
21
+
22
+ Print out invoice by it's number:
23
+
24
+ $ gnucash-invoice --dbpath /path/to/db.sqlite3 00004 > invoice-00004.html
25
+
26
+
27
+ ## TODO
28
+
29
+ * TESTS!!!
30
+ * Fix models backend to allow specify db host:port
31
+ * ??? Add backend for old-school XML format
32
+ * Use currency from an account book instead of hardcoded
33
+ * ??? Export into FreshBooks
34
+ * Built-in several templates
35
+ * Allow specify foot notice (using markdown)
36
+
37
+
38
+ ## Contributing
39
+
40
+ 1. Fork it
41
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
42
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
43
+ 4. Push to the branch (`git push origin my-new-feature`)
44
+ 5. Create new Pull Request
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+
4
+ $:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
5
+
6
+
7
+ require 'gnucash/invoice'
8
+ GnuCash::Invoice::Runner.run ARGV
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/gnucash/invoice/version', __FILE__)
3
+
4
+
5
+ Gem::Specification.new do |gem|
6
+ gem.name = "gnucash-invoice"
7
+ gem.version = GnuCash::Invoice::VERSION
8
+ gem.homepage = "http://ixti.github.com/gnucash-invoice"
9
+ gem.authors = %w{Aleksey V Zapparov}
10
+ gem.email = %w{ixti@member.fsf.org}
11
+ gem.summary = "gnucash-invoice-#{GnuCash::Invoice::VERSION}"
12
+ gem.description = %q{GnuCash invoice printer for human beings.}
13
+
14
+ gem.add_dependency "sequel", "~> 3.41"
15
+ gem.add_dependency "slim", "~> 1.3"
16
+ gem.add_dependency 'sass', '~> 3.2'
17
+ gem.add_dependency "sprockets", "~> 2.8"
18
+
19
+ gem.add_development_dependency "sqlite3"
20
+ gem.add_development_dependency "rake"
21
+
22
+ gem.files = `git ls-files`.split($\)
23
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
24
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
25
+
26
+ gem.require_paths = ["lib"]
27
+ end
@@ -0,0 +1,26 @@
1
+ # 3rd-party
2
+ require 'sequel'
3
+
4
+
5
+ # internal
6
+ require 'gnucash/timestamps'
7
+
8
+
9
+ module GnuCash
10
+ class NoDatabaseConnection < StandardError; end
11
+
12
+
13
+ def self.connection
14
+ @connection or raise NoDatabaseConnection
15
+ end
16
+
17
+
18
+ def self.connect! db
19
+ @connection = Sequel.connect "sqlite://#{db}"
20
+ end
21
+
22
+
23
+ def self.root
24
+ @root ||= Pathname.new File.realpath(File.join(__FILE__, '../..'))
25
+ end
26
+ end
@@ -0,0 +1,85 @@
1
+ # internal
2
+ require 'gnucash'
3
+ require 'gnucash/invoice/version'
4
+ require 'gnucash/invoice/entry'
5
+ require 'gnucash/invoice/customer'
6
+ require 'gnucash/invoice/supplier'
7
+ require 'gnucash/invoice/printer'
8
+ require 'gnucash/invoice/runner'
9
+
10
+
11
+ module GnuCash
12
+ class Invoice
13
+ class InvoiceNotFound < StandardError; end
14
+
15
+
16
+ include Timestamps
17
+
18
+
19
+ attr_reader :raw, :id, :opened_at, :posted_at, :notes
20
+
21
+
22
+ def initialize data
23
+ @raw = data
24
+
25
+ @id = data[:id]
26
+ @opened_at = from_timestamp data[:date_opened]
27
+ @posted_at = from_timestamp data[:date_posted]
28
+ @notes = data[:notes]
29
+ end
30
+
31
+
32
+ def posted?
33
+ posted_at.is_a? DateTime
34
+ end
35
+
36
+
37
+ def customer
38
+ @customer ||= Customer.find @raw[:owner_guid]
39
+ end
40
+
41
+
42
+ def entries
43
+ @entries ||= Entry.find @raw[:guid]
44
+ end
45
+
46
+
47
+ def total
48
+ entries.inject(0){ |memo,entry| memo + entry.total }
49
+ end
50
+
51
+
52
+ def to_s
53
+ open_date = "(#{opened_at.strftime '%Y/%m/%d'})"
54
+ post_date = ""
55
+
56
+ if posted?
57
+ post_date = "[#{posted_at.strftime '%Y/%m/%d'}]"
58
+ end
59
+
60
+ "%-16s %-32s %s %s" % [ id, customer.name, open_date, post_date ]
61
+ end
62
+
63
+
64
+ def self.all
65
+ dataset.map{ |data| new(data) }
66
+ end
67
+
68
+
69
+ def self.find id
70
+ unless data = dataset.where(:id => id).first
71
+ raise InvoiceNotFound, "ID: #{id}"
72
+ end
73
+
74
+ new(data)
75
+ end
76
+
77
+
78
+ private
79
+
80
+
81
+ def self.dataset
82
+ GnuCash.connection[:invoices].where(:owner_type => 2)
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,51 @@
1
+ module GnuCash
2
+ class Invoice
3
+ class Customer
4
+ class CustomerNotFound < StandardError; end
5
+
6
+
7
+ include Timestamps
8
+
9
+
10
+ attr_reader :id, :name
11
+
12
+
13
+ def initialize data
14
+ @raw = data
15
+
16
+ @id = data[:id]
17
+ @name = data[:name]
18
+ end
19
+
20
+
21
+ def address_lines
22
+ @address_lines ||= [].tap do |lines|
23
+ (1..4).each do |i|
24
+ id = :"addr_addr#{i}"
25
+ lines << @raw[id] if @raw[id]
26
+ end
27
+ end
28
+ end
29
+
30
+
31
+ alias :to_s :name
32
+
33
+
34
+ def self.find guid
35
+ unless data = dataset.where(:guid => guid).first
36
+ raise CustomerNotFound, "GUID: #{guid}"
37
+ end
38
+
39
+ new(data)
40
+ end
41
+
42
+
43
+ private
44
+
45
+
46
+ def self.dataset
47
+ GnuCash.connection[:customers]
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,39 @@
1
+ module GnuCash
2
+ class Invoice
3
+ class Entry
4
+ include Timestamps
5
+
6
+
7
+ attr_reader :raw, :date, :description, :action, :quantity, :price
8
+
9
+
10
+ def initialize data
11
+ @raw = data
12
+
13
+ @date = from_timestamp(data[:date]).to_date
14
+ @description = data[:description]
15
+ @action = data[:action]
16
+ @quantity = data[:quantity_num].to_f / data[:quantity_denom]
17
+ @price = data[:i_price_num].to_f / data[:i_price_denom]
18
+ end
19
+
20
+
21
+ def total
22
+ price * quantity
23
+ end
24
+
25
+
26
+ def self.find invoice_guid
27
+ dataset.where(:invoice => invoice_guid).map{ |data| new(data) }
28
+ end
29
+
30
+
31
+ private
32
+
33
+
34
+ def self.dataset
35
+ GnuCash.connection[:entries]
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,46 @@
1
+ require 'slim'
2
+ require 'sass'
3
+ require 'sprockets'
4
+
5
+
6
+ module GnuCash
7
+ class Invoice
8
+ class Printer
9
+ def initialize invoice_id
10
+ @invoice = Invoice.find(invoice_id)
11
+ end
12
+
13
+
14
+ def embedded_asset pathname
15
+ environment[pathname].to_s
16
+ end
17
+
18
+
19
+ def environment
20
+ @environment ||= Sprockets::Environment.new(template_dir).tap do |env|
21
+ env.append_path 'assets/stylesheets'
22
+ env.css_compressor = :sass
23
+ end
24
+ end
25
+
26
+
27
+ def render
28
+ template = Slim::Template.new template_dir('invoice.slim').to_s
29
+ template.render(self, {
30
+ :invoice => @invoice,
31
+ :entries => @invoice.entries,
32
+ :customer => @invoice.customer
33
+ })
34
+ end
35
+
36
+
37
+ protected
38
+
39
+
40
+ def template_dir *args
41
+ args.unshift 'templates'
42
+ GnuCash.root.join(*args)
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,52 @@
1
+ # stdlib
2
+ require 'ostruct'
3
+ require 'optparse'
4
+ require 'pathname'
5
+
6
+
7
+ module GnuCash
8
+ class Invoice
9
+ module Runner
10
+ class InvalidArguments < StandardError; end
11
+
12
+
13
+ def self.run argv
14
+ options = parse argv
15
+
16
+ GnuCash.connect! options.dbpath
17
+
18
+ # Print out invoice
19
+ unless argv.empty?
20
+ puts Printer.new(options.invoice_id).render
21
+ exit
22
+ end
23
+
24
+ # Show known invoices
25
+ Invoice.all.each do |invoice|
26
+ puts invoice
27
+ end
28
+ end
29
+
30
+
31
+ protected
32
+
33
+
34
+ def self.parse argv
35
+ options = OpenStruct.new
36
+
37
+ OptionParser.new do |opts|
38
+ opts.on('-d', '--dbpath [DATABASE]', 'SQLite database path') do |path|
39
+ options.dbpath = Pathname.new path
40
+ end
41
+ end.parse! argv
42
+
43
+ raise InvalidArguments, "No database selected" unless options.dbpath
44
+ raise InvalidArguments, "Can't find specified database" unless options.dbpath.exist?
45
+
46
+ options.invoice_id = argv.first
47
+
48
+ options
49
+ end
50
+ end
51
+ end
52
+ end
File without changes
@@ -0,0 +1,5 @@
1
+ module GnuCash
2
+ class Invoice
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,23 @@
1
+ module GnuCash
2
+ module Timestamps
3
+ FORMAT = "%Y%m%d%H%M%S"
4
+
5
+
6
+ def from_timestamp value
7
+ case value
8
+ when ::DateTime then value
9
+ when ::Numeric, /\A\d+\z/ then parse value.to_s
10
+ when ::String, /\A\d+\z/ then parse value
11
+ end
12
+ end
13
+
14
+
15
+ private
16
+
17
+
18
+ def parse str
19
+ ::DateTime.strptime str, FORMAT
20
+ end
21
+ end
22
+ end
23
+
@@ -0,0 +1,907 @@
1
+ /*!
2
+ * Bootstrap v2.2.1
3
+ *
4
+ * Copyright 2012 Twitter, Inc
5
+ * Licensed under the Apache License v2.0
6
+ * http://www.apache.org/licenses/LICENSE-2.0
7
+ *
8
+ * Designed and built with all the love in the world @twitter by @mdo and @fat.
9
+ */
10
+ .clearfix {
11
+ *zoom: 1;
12
+ }
13
+ .clearfix:before,
14
+ .clearfix:after {
15
+ display: table;
16
+ content: "";
17
+ line-height: 0;
18
+ }
19
+ .clearfix:after {
20
+ clear: both;
21
+ }
22
+ .hide-text {
23
+ font: 0/0 a;
24
+ color: transparent;
25
+ text-shadow: none;
26
+ background-color: transparent;
27
+ border: 0;
28
+ }
29
+ .input-block-level {
30
+ display: block;
31
+ width: 100%;
32
+ min-height: 30px;
33
+ -webkit-box-sizing: border-box;
34
+ -moz-box-sizing: border-box;
35
+ box-sizing: border-box;
36
+ }
37
+ body {
38
+ margin: 0;
39
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
40
+ font-size: 14px;
41
+ line-height: 20px;
42
+ color: #333333;
43
+ background-color: #ffffff;
44
+ }
45
+ a {
46
+ color: #0088cc;
47
+ text-decoration: none;
48
+ }
49
+ a:hover {
50
+ color: #005580;
51
+ text-decoration: underline;
52
+ }
53
+ .img-rounded {
54
+ -webkit-border-radius: 6px;
55
+ -moz-border-radius: 6px;
56
+ border-radius: 6px;
57
+ }
58
+ .img-polaroid {
59
+ padding: 4px;
60
+ background-color: #fff;
61
+ border: 1px solid #ccc;
62
+ border: 1px solid rgba(0, 0, 0, 0.2);
63
+ -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
64
+ -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
65
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
66
+ }
67
+ .img-circle {
68
+ -webkit-border-radius: 500px;
69
+ -moz-border-radius: 500px;
70
+ border-radius: 500px;
71
+ }
72
+ .row {
73
+ margin-left: -20px;
74
+ *zoom: 1;
75
+ }
76
+ .row:before,
77
+ .row:after {
78
+ display: table;
79
+ content: "";
80
+ line-height: 0;
81
+ }
82
+ .row:after {
83
+ clear: both;
84
+ }
85
+ [class*="span"] {
86
+ float: left;
87
+ min-height: 1px;
88
+ margin-left: 20px;
89
+ }
90
+ .container,
91
+ .navbar-static-top .container,
92
+ .navbar-fixed-top .container,
93
+ .navbar-fixed-bottom .container {
94
+ width: 940px;
95
+ }
96
+ .span12 {
97
+ width: 940px;
98
+ }
99
+ .span11 {
100
+ width: 860px;
101
+ }
102
+ .span10 {
103
+ width: 780px;
104
+ }
105
+ .span9 {
106
+ width: 700px;
107
+ }
108
+ .span8 {
109
+ width: 620px;
110
+ }
111
+ .span7 {
112
+ width: 540px;
113
+ }
114
+ .span6 {
115
+ width: 460px;
116
+ }
117
+ .span5 {
118
+ width: 380px;
119
+ }
120
+ .span4 {
121
+ width: 300px;
122
+ }
123
+ .span3 {
124
+ width: 220px;
125
+ }
126
+ .span2 {
127
+ width: 140px;
128
+ }
129
+ .span1 {
130
+ width: 60px;
131
+ }
132
+ .offset12 {
133
+ margin-left: 980px;
134
+ }
135
+ .offset11 {
136
+ margin-left: 900px;
137
+ }
138
+ .offset10 {
139
+ margin-left: 820px;
140
+ }
141
+ .offset9 {
142
+ margin-left: 740px;
143
+ }
144
+ .offset8 {
145
+ margin-left: 660px;
146
+ }
147
+ .offset7 {
148
+ margin-left: 580px;
149
+ }
150
+ .offset6 {
151
+ margin-left: 500px;
152
+ }
153
+ .offset5 {
154
+ margin-left: 420px;
155
+ }
156
+ .offset4 {
157
+ margin-left: 340px;
158
+ }
159
+ .offset3 {
160
+ margin-left: 260px;
161
+ }
162
+ .offset2 {
163
+ margin-left: 180px;
164
+ }
165
+ .offset1 {
166
+ margin-left: 100px;
167
+ }
168
+ .row-fluid {
169
+ width: 100%;
170
+ *zoom: 1;
171
+ }
172
+ .row-fluid:before,
173
+ .row-fluid:after {
174
+ display: table;
175
+ content: "";
176
+ line-height: 0;
177
+ }
178
+ .row-fluid:after {
179
+ clear: both;
180
+ }
181
+ .row-fluid [class*="span"] {
182
+ display: block;
183
+ width: 100%;
184
+ min-height: 30px;
185
+ -webkit-box-sizing: border-box;
186
+ -moz-box-sizing: border-box;
187
+ box-sizing: border-box;
188
+ float: left;
189
+ margin-left: 2.127659574468085%;
190
+ *margin-left: 2.074468085106383%;
191
+ }
192
+ .row-fluid [class*="span"]:first-child {
193
+ margin-left: 0;
194
+ }
195
+ .row-fluid .controls-row [class*="span"] + [class*="span"] {
196
+ margin-left: 2.127659574468085%;
197
+ }
198
+ .row-fluid .span12 {
199
+ width: 100%;
200
+ *width: 99.94680851063829%;
201
+ }
202
+ .row-fluid .span11 {
203
+ width: 91.48936170212765%;
204
+ *width: 91.43617021276594%;
205
+ }
206
+ .row-fluid .span10 {
207
+ width: 82.97872340425532%;
208
+ *width: 82.92553191489361%;
209
+ }
210
+ .row-fluid .span9 {
211
+ width: 74.46808510638297%;
212
+ *width: 74.41489361702126%;
213
+ }
214
+ .row-fluid .span8 {
215
+ width: 65.95744680851064%;
216
+ *width: 65.90425531914893%;
217
+ }
218
+ .row-fluid .span7 {
219
+ width: 57.44680851063829%;
220
+ *width: 57.39361702127659%;
221
+ }
222
+ .row-fluid .span6 {
223
+ width: 48.93617021276595%;
224
+ *width: 48.88297872340425%;
225
+ }
226
+ .row-fluid .span5 {
227
+ width: 40.42553191489362%;
228
+ *width: 40.37234042553192%;
229
+ }
230
+ .row-fluid .span4 {
231
+ width: 31.914893617021278%;
232
+ *width: 31.861702127659576%;
233
+ }
234
+ .row-fluid .span3 {
235
+ width: 23.404255319148934%;
236
+ *width: 23.351063829787233%;
237
+ }
238
+ .row-fluid .span2 {
239
+ width: 14.893617021276595%;
240
+ *width: 14.840425531914894%;
241
+ }
242
+ .row-fluid .span1 {
243
+ width: 6.382978723404255%;
244
+ *width: 6.329787234042553%;
245
+ }
246
+ .row-fluid .offset12 {
247
+ margin-left: 104.25531914893617%;
248
+ *margin-left: 104.14893617021275%;
249
+ }
250
+ .row-fluid .offset12:first-child {
251
+ margin-left: 102.12765957446808%;
252
+ *margin-left: 102.02127659574467%;
253
+ }
254
+ .row-fluid .offset11 {
255
+ margin-left: 95.74468085106382%;
256
+ *margin-left: 95.6382978723404%;
257
+ }
258
+ .row-fluid .offset11:first-child {
259
+ margin-left: 93.61702127659574%;
260
+ *margin-left: 93.51063829787232%;
261
+ }
262
+ .row-fluid .offset10 {
263
+ margin-left: 87.23404255319149%;
264
+ *margin-left: 87.12765957446807%;
265
+ }
266
+ .row-fluid .offset10:first-child {
267
+ margin-left: 85.1063829787234%;
268
+ *margin-left: 84.99999999999999%;
269
+ }
270
+ .row-fluid .offset9 {
271
+ margin-left: 78.72340425531914%;
272
+ *margin-left: 78.61702127659572%;
273
+ }
274
+ .row-fluid .offset9:first-child {
275
+ margin-left: 76.59574468085106%;
276
+ *margin-left: 76.48936170212764%;
277
+ }
278
+ .row-fluid .offset8 {
279
+ margin-left: 70.2127659574468%;
280
+ *margin-left: 70.10638297872339%;
281
+ }
282
+ .row-fluid .offset8:first-child {
283
+ margin-left: 68.08510638297872%;
284
+ *margin-left: 67.9787234042553%;
285
+ }
286
+ .row-fluid .offset7 {
287
+ margin-left: 61.70212765957446%;
288
+ *margin-left: 61.59574468085106%;
289
+ }
290
+ .row-fluid .offset7:first-child {
291
+ margin-left: 59.574468085106375%;
292
+ *margin-left: 59.46808510638297%;
293
+ }
294
+ .row-fluid .offset6 {
295
+ margin-left: 53.191489361702125%;
296
+ *margin-left: 53.085106382978715%;
297
+ }
298
+ .row-fluid .offset6:first-child {
299
+ margin-left: 51.063829787234035%;
300
+ *margin-left: 50.95744680851063%;
301
+ }
302
+ .row-fluid .offset5 {
303
+ margin-left: 44.68085106382979%;
304
+ *margin-left: 44.57446808510638%;
305
+ }
306
+ .row-fluid .offset5:first-child {
307
+ margin-left: 42.5531914893617%;
308
+ *margin-left: 42.4468085106383%;
309
+ }
310
+ .row-fluid .offset4 {
311
+ margin-left: 36.170212765957444%;
312
+ *margin-left: 36.06382978723405%;
313
+ }
314
+ .row-fluid .offset4:first-child {
315
+ margin-left: 34.04255319148936%;
316
+ *margin-left: 33.93617021276596%;
317
+ }
318
+ .row-fluid .offset3 {
319
+ margin-left: 27.659574468085104%;
320
+ *margin-left: 27.5531914893617%;
321
+ }
322
+ .row-fluid .offset3:first-child {
323
+ margin-left: 25.53191489361702%;
324
+ *margin-left: 25.425531914893618%;
325
+ }
326
+ .row-fluid .offset2 {
327
+ margin-left: 19.148936170212764%;
328
+ *margin-left: 19.04255319148936%;
329
+ }
330
+ .row-fluid .offset2:first-child {
331
+ margin-left: 17.02127659574468%;
332
+ *margin-left: 16.914893617021278%;
333
+ }
334
+ .row-fluid .offset1 {
335
+ margin-left: 10.638297872340425%;
336
+ *margin-left: 10.53191489361702%;
337
+ }
338
+ .row-fluid .offset1:first-child {
339
+ margin-left: 8.51063829787234%;
340
+ *margin-left: 8.404255319148938%;
341
+ }
342
+ [class*="span"].hide,
343
+ .row-fluid [class*="span"].hide {
344
+ display: none;
345
+ }
346
+ [class*="span"].pull-right,
347
+ .row-fluid [class*="span"].pull-right {
348
+ float: right;
349
+ }
350
+ .container {
351
+ margin-right: auto;
352
+ margin-left: auto;
353
+ *zoom: 1;
354
+ }
355
+ .container:before,
356
+ .container:after {
357
+ display: table;
358
+ content: "";
359
+ line-height: 0;
360
+ }
361
+ .container:after {
362
+ clear: both;
363
+ }
364
+ .container-fluid {
365
+ padding-right: 20px;
366
+ padding-left: 20px;
367
+ *zoom: 1;
368
+ }
369
+ .container-fluid:before,
370
+ .container-fluid:after {
371
+ display: table;
372
+ content: "";
373
+ line-height: 0;
374
+ }
375
+ .container-fluid:after {
376
+ clear: both;
377
+ }
378
+ p {
379
+ margin: 0 0 10px;
380
+ }
381
+ .lead {
382
+ margin-bottom: 20px;
383
+ font-size: 21px;
384
+ font-weight: 200;
385
+ line-height: 30px;
386
+ }
387
+ small {
388
+ font-size: 85%;
389
+ }
390
+ strong {
391
+ font-weight: bold;
392
+ }
393
+ em {
394
+ font-style: italic;
395
+ }
396
+ cite {
397
+ font-style: normal;
398
+ }
399
+ .muted {
400
+ color: #999999;
401
+ }
402
+ .text-warning {
403
+ color: #c09853;
404
+ }
405
+ a.text-warning:hover {
406
+ color: #a47e3c;
407
+ }
408
+ .text-error {
409
+ color: #b94a48;
410
+ }
411
+ a.text-error:hover {
412
+ color: #953b39;
413
+ }
414
+ .text-info {
415
+ color: #3a87ad;
416
+ }
417
+ a.text-info:hover {
418
+ color: #2d6987;
419
+ }
420
+ .text-success {
421
+ color: #468847;
422
+ }
423
+ a.text-success:hover {
424
+ color: #356635;
425
+ }
426
+ h1,
427
+ h2,
428
+ h3,
429
+ h4,
430
+ h5,
431
+ h6 {
432
+ margin: 10px 0;
433
+ font-family: inherit;
434
+ font-weight: bold;
435
+ line-height: 20px;
436
+ color: inherit;
437
+ text-rendering: optimizelegibility;
438
+ }
439
+ h1 small,
440
+ h2 small,
441
+ h3 small,
442
+ h4 small,
443
+ h5 small,
444
+ h6 small {
445
+ font-weight: normal;
446
+ line-height: 1;
447
+ color: #999999;
448
+ }
449
+ h1,
450
+ h2,
451
+ h3 {
452
+ line-height: 40px;
453
+ }
454
+ h1 {
455
+ font-size: 38.5px;
456
+ }
457
+ h2 {
458
+ font-size: 31.5px;
459
+ }
460
+ h3 {
461
+ font-size: 24.5px;
462
+ }
463
+ h4 {
464
+ font-size: 17.5px;
465
+ }
466
+ h5 {
467
+ font-size: 14px;
468
+ }
469
+ h6 {
470
+ font-size: 11.9px;
471
+ }
472
+ h1 small {
473
+ font-size: 24.5px;
474
+ }
475
+ h2 small {
476
+ font-size: 17.5px;
477
+ }
478
+ h3 small {
479
+ font-size: 14px;
480
+ }
481
+ h4 small {
482
+ font-size: 14px;
483
+ }
484
+ .page-header {
485
+ padding-bottom: 9px;
486
+ margin: 20px 0 30px;
487
+ border-bottom: 1px solid #eeeeee;
488
+ }
489
+ ul,
490
+ ol {
491
+ padding: 0;
492
+ margin: 0 0 10px 25px;
493
+ }
494
+ ul ul,
495
+ ul ol,
496
+ ol ol,
497
+ ol ul {
498
+ margin-bottom: 0;
499
+ }
500
+ li {
501
+ line-height: 20px;
502
+ }
503
+ ul.unstyled,
504
+ ol.unstyled {
505
+ margin-left: 0;
506
+ list-style: none;
507
+ }
508
+ dl {
509
+ margin-bottom: 20px;
510
+ }
511
+ dt,
512
+ dd {
513
+ line-height: 20px;
514
+ }
515
+ dt {
516
+ font-weight: bold;
517
+ }
518
+ dd {
519
+ margin-left: 10px;
520
+ }
521
+ .dl-horizontal {
522
+ *zoom: 1;
523
+ }
524
+ .dl-horizontal:before,
525
+ .dl-horizontal:after {
526
+ display: table;
527
+ content: "";
528
+ line-height: 0;
529
+ }
530
+ .dl-horizontal:after {
531
+ clear: both;
532
+ }
533
+ .dl-horizontal dt {
534
+ float: left;
535
+ width: 160px;
536
+ clear: left;
537
+ text-align: right;
538
+ overflow: hidden;
539
+ text-overflow: ellipsis;
540
+ white-space: nowrap;
541
+ }
542
+ .dl-horizontal dd {
543
+ margin-left: 180px;
544
+ }
545
+ hr {
546
+ margin: 20px 0;
547
+ border: 0;
548
+ border-top: 1px solid #eeeeee;
549
+ border-bottom: 1px solid #ffffff;
550
+ }
551
+ abbr[title],
552
+ abbr[data-original-title] {
553
+ cursor: help;
554
+ border-bottom: 1px dotted #999999;
555
+ }
556
+ abbr.initialism {
557
+ font-size: 90%;
558
+ text-transform: uppercase;
559
+ }
560
+ blockquote {
561
+ padding: 0 0 0 15px;
562
+ margin: 0 0 20px;
563
+ border-left: 5px solid #eeeeee;
564
+ }
565
+ blockquote p {
566
+ margin-bottom: 0;
567
+ font-size: 16px;
568
+ font-weight: 300;
569
+ line-height: 25px;
570
+ }
571
+ blockquote small {
572
+ display: block;
573
+ line-height: 20px;
574
+ color: #999999;
575
+ }
576
+ blockquote small:before {
577
+ content: '\2014 \00A0';
578
+ }
579
+ blockquote.pull-right {
580
+ float: right;
581
+ padding-right: 15px;
582
+ padding-left: 0;
583
+ border-right: 5px solid #eeeeee;
584
+ border-left: 0;
585
+ }
586
+ blockquote.pull-right p,
587
+ blockquote.pull-right small {
588
+ text-align: right;
589
+ }
590
+ blockquote.pull-right small:before {
591
+ content: '';
592
+ }
593
+ blockquote.pull-right small:after {
594
+ content: '\00A0 \2014';
595
+ }
596
+ q:before,
597
+ q:after,
598
+ blockquote:before,
599
+ blockquote:after {
600
+ content: "";
601
+ }
602
+ address {
603
+ display: block;
604
+ margin-bottom: 20px;
605
+ font-style: normal;
606
+ line-height: 20px;
607
+ }
608
+ .label,
609
+ .badge {
610
+ display: inline-block;
611
+ padding: 2px 4px;
612
+ font-size: 11.844px;
613
+ font-weight: bold;
614
+ line-height: 14px;
615
+ color: #ffffff;
616
+ vertical-align: baseline;
617
+ white-space: nowrap;
618
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
619
+ background-color: #999999;
620
+ }
621
+ .label {
622
+ -webkit-border-radius: 3px;
623
+ -moz-border-radius: 3px;
624
+ border-radius: 3px;
625
+ }
626
+ .badge {
627
+ padding-left: 9px;
628
+ padding-right: 9px;
629
+ -webkit-border-radius: 9px;
630
+ -moz-border-radius: 9px;
631
+ border-radius: 9px;
632
+ }
633
+ a.label:hover,
634
+ a.badge:hover {
635
+ color: #ffffff;
636
+ text-decoration: none;
637
+ cursor: pointer;
638
+ }
639
+ .label-important,
640
+ .badge-important {
641
+ background-color: #b94a48;
642
+ }
643
+ .label-important[href],
644
+ .badge-important[href] {
645
+ background-color: #953b39;
646
+ }
647
+ .label-warning,
648
+ .badge-warning {
649
+ background-color: #f89406;
650
+ }
651
+ .label-warning[href],
652
+ .badge-warning[href] {
653
+ background-color: #c67605;
654
+ }
655
+ .label-success,
656
+ .badge-success {
657
+ background-color: #468847;
658
+ }
659
+ .label-success[href],
660
+ .badge-success[href] {
661
+ background-color: #356635;
662
+ }
663
+ .label-info,
664
+ .badge-info {
665
+ background-color: #3a87ad;
666
+ }
667
+ .label-info[href],
668
+ .badge-info[href] {
669
+ background-color: #2d6987;
670
+ }
671
+ .label-inverse,
672
+ .badge-inverse {
673
+ background-color: #333333;
674
+ }
675
+ .label-inverse[href],
676
+ .badge-inverse[href] {
677
+ background-color: #1a1a1a;
678
+ }
679
+ .btn .label,
680
+ .btn .badge {
681
+ position: relative;
682
+ top: -1px;
683
+ }
684
+ .btn-mini .label,
685
+ .btn-mini .badge {
686
+ top: 0;
687
+ }
688
+ table {
689
+ max-width: 100%;
690
+ background-color: transparent;
691
+ border-collapse: collapse;
692
+ border-spacing: 0;
693
+ }
694
+ .table {
695
+ width: 100%;
696
+ margin-bottom: 20px;
697
+ }
698
+ .table th,
699
+ .table td {
700
+ padding: 8px;
701
+ line-height: 20px;
702
+ text-align: left;
703
+ vertical-align: top;
704
+ border-top: 1px solid #dddddd;
705
+ }
706
+ .table th {
707
+ font-weight: bold;
708
+ }
709
+ .table thead th {
710
+ vertical-align: bottom;
711
+ }
712
+ .table caption + thead tr:first-child th,
713
+ .table caption + thead tr:first-child td,
714
+ .table colgroup + thead tr:first-child th,
715
+ .table colgroup + thead tr:first-child td,
716
+ .table thead:first-child tr:first-child th,
717
+ .table thead:first-child tr:first-child td {
718
+ border-top: 0;
719
+ }
720
+ .table tbody + tbody {
721
+ border-top: 2px solid #dddddd;
722
+ }
723
+ .table-condensed th,
724
+ .table-condensed td {
725
+ padding: 4px 5px;
726
+ }
727
+ .table-bordered {
728
+ border: 1px solid #dddddd;
729
+ border-collapse: separate;
730
+ *border-collapse: collapse;
731
+ border-left: 0;
732
+ -webkit-border-radius: 4px;
733
+ -moz-border-radius: 4px;
734
+ border-radius: 4px;
735
+ }
736
+ .table-bordered th,
737
+ .table-bordered td {
738
+ border-left: 1px solid #dddddd;
739
+ }
740
+ .table-bordered caption + thead tr:first-child th,
741
+ .table-bordered caption + tbody tr:first-child th,
742
+ .table-bordered caption + tbody tr:first-child td,
743
+ .table-bordered colgroup + thead tr:first-child th,
744
+ .table-bordered colgroup + tbody tr:first-child th,
745
+ .table-bordered colgroup + tbody tr:first-child td,
746
+ .table-bordered thead:first-child tr:first-child th,
747
+ .table-bordered tbody:first-child tr:first-child th,
748
+ .table-bordered tbody:first-child tr:first-child td {
749
+ border-top: 0;
750
+ }
751
+ .table-bordered thead:first-child tr:first-child th:first-child,
752
+ .table-bordered tbody:first-child tr:first-child td:first-child {
753
+ -webkit-border-top-left-radius: 4px;
754
+ border-top-left-radius: 4px;
755
+ -moz-border-radius-topleft: 4px;
756
+ }
757
+ .table-bordered thead:first-child tr:first-child th:last-child,
758
+ .table-bordered tbody:first-child tr:first-child td:last-child {
759
+ -webkit-border-top-right-radius: 4px;
760
+ border-top-right-radius: 4px;
761
+ -moz-border-radius-topright: 4px;
762
+ }
763
+ .table-bordered thead:last-child tr:last-child th:first-child,
764
+ .table-bordered tbody:last-child tr:last-child td:first-child,
765
+ .table-bordered tfoot:last-child tr:last-child td:first-child {
766
+ -webkit-border-radius: 0 0 0 4px;
767
+ -moz-border-radius: 0 0 0 4px;
768
+ border-radius: 0 0 0 4px;
769
+ -webkit-border-bottom-left-radius: 4px;
770
+ border-bottom-left-radius: 4px;
771
+ -moz-border-radius-bottomleft: 4px;
772
+ }
773
+ .table-bordered thead:last-child tr:last-child th:last-child,
774
+ .table-bordered tbody:last-child tr:last-child td:last-child,
775
+ .table-bordered tfoot:last-child tr:last-child td:last-child {
776
+ -webkit-border-bottom-right-radius: 4px;
777
+ border-bottom-right-radius: 4px;
778
+ -moz-border-radius-bottomright: 4px;
779
+ }
780
+ .table-bordered caption + thead tr:first-child th:first-child,
781
+ .table-bordered caption + tbody tr:first-child td:first-child,
782
+ .table-bordered colgroup + thead tr:first-child th:first-child,
783
+ .table-bordered colgroup + tbody tr:first-child td:first-child {
784
+ -webkit-border-top-left-radius: 4px;
785
+ border-top-left-radius: 4px;
786
+ -moz-border-radius-topleft: 4px;
787
+ }
788
+ .table-bordered caption + thead tr:first-child th:last-child,
789
+ .table-bordered caption + tbody tr:first-child td:last-child,
790
+ .table-bordered colgroup + thead tr:first-child th:last-child,
791
+ .table-bordered colgroup + tbody tr:first-child td:last-child {
792
+ -webkit-border-top-right-radius: 4px;
793
+ border-top-right-radius: 4px;
794
+ -moz-border-radius-topright: 4px;
795
+ }
796
+ .table-striped tbody tr:nth-child(odd) td,
797
+ .table-striped tbody tr:nth-child(odd) th {
798
+ background-color: #f9f9f9;
799
+ }
800
+ .table-hover tbody tr:hover td,
801
+ .table-hover tbody tr:hover th {
802
+ background-color: #f5f5f5;
803
+ }
804
+ table td[class*="span"],
805
+ table th[class*="span"],
806
+ .row-fluid table td[class*="span"],
807
+ .row-fluid table th[class*="span"] {
808
+ display: table-cell;
809
+ float: none;
810
+ margin-left: 0;
811
+ }
812
+ .table td.span1,
813
+ .table th.span1 {
814
+ float: none;
815
+ width: 44px;
816
+ margin-left: 0;
817
+ }
818
+ .table td.span2,
819
+ .table th.span2 {
820
+ float: none;
821
+ width: 124px;
822
+ margin-left: 0;
823
+ }
824
+ .table td.span3,
825
+ .table th.span3 {
826
+ float: none;
827
+ width: 204px;
828
+ margin-left: 0;
829
+ }
830
+ .table td.span4,
831
+ .table th.span4 {
832
+ float: none;
833
+ width: 284px;
834
+ margin-left: 0;
835
+ }
836
+ .table td.span5,
837
+ .table th.span5 {
838
+ float: none;
839
+ width: 364px;
840
+ margin-left: 0;
841
+ }
842
+ .table td.span6,
843
+ .table th.span6 {
844
+ float: none;
845
+ width: 444px;
846
+ margin-left: 0;
847
+ }
848
+ .table td.span7,
849
+ .table th.span7 {
850
+ float: none;
851
+ width: 524px;
852
+ margin-left: 0;
853
+ }
854
+ .table td.span8,
855
+ .table th.span8 {
856
+ float: none;
857
+ width: 604px;
858
+ margin-left: 0;
859
+ }
860
+ .table td.span9,
861
+ .table th.span9 {
862
+ float: none;
863
+ width: 684px;
864
+ margin-left: 0;
865
+ }
866
+ .table td.span10,
867
+ .table th.span10 {
868
+ float: none;
869
+ width: 764px;
870
+ margin-left: 0;
871
+ }
872
+ .table td.span11,
873
+ .table th.span11 {
874
+ float: none;
875
+ width: 844px;
876
+ margin-left: 0;
877
+ }
878
+ .table td.span12,
879
+ .table th.span12 {
880
+ float: none;
881
+ width: 924px;
882
+ margin-left: 0;
883
+ }
884
+ .table tbody tr.success td {
885
+ background-color: #dff0d8;
886
+ }
887
+ .table tbody tr.error td {
888
+ background-color: #f2dede;
889
+ }
890
+ .table tbody tr.warning td {
891
+ background-color: #fcf8e3;
892
+ }
893
+ .table tbody tr.info td {
894
+ background-color: #d9edf7;
895
+ }
896
+ .table-hover tbody tr.success:hover td {
897
+ background-color: #d0e9c6;
898
+ }
899
+ .table-hover tbody tr.error:hover td {
900
+ background-color: #ebcccc;
901
+ }
902
+ .table-hover tbody tr.warning:hover td {
903
+ background-color: #faf2cc;
904
+ }
905
+ .table-hover tbody tr.info:hover td {
906
+ background-color: #c4e3f3;
907
+ }