@@ -0,0 +1,3 @@ | |||
data/ | |||
*sqlite3 | |||
@@ -0,0 +1,111 @@ | |||
require 'rubygems' | |||
require 'sinatra' | |||
require 'sinatra-helpers/haml/partials' | |||
require 'haml' | |||
require 'lib/models' | |||
get '/' do | |||
@directorates = Directorate.all | |||
# @results = repository(:default).adapter.query(" | |||
# SELECT p.name, | |||
# sum(c.votes_2010) AS votes, | |||
# p.colour | |||
# | |||
# FROM parties p, | |||
# councilcandidates c | |||
# | |||
# WHERE p.id = c.party_id | |||
# | |||
# GROUP BY p.name, p.colour | |||
# | |||
# ORDER BY votes desc | |||
# ;") | |||
# select p.name, count(c.*) AS seats | |||
# FROM parties p, councilcandidates c | |||
# GROUP BY p.id | |||
haml :home | |||
end | |||
get '/directorates/:id' do | |||
@directorate = Directorate.get(params[:id]) | |||
haml :directorate | |||
end | |||
get '/suppliers/:id.csv' do | |||
@supplier = Supplier.get(params[:id]) | |||
headers "Content-Disposition" => "attachment;filename=supplier#{@supplier.id}.csv", | |||
"Content-Type" => "application/octet-stream" | |||
result = "Date,Trans No,Directorate,Service,Amount ex. VAT\n" | |||
for payment in @supplier.payments | |||
result += "#{payment.d.strftime("%d %b %Y")},#{payment.trans_no},\"#{payment.directorate.name}\",#{payment.service.name},#{sprintf("%0.2f", payment.amount)}\n" | |||
end | |||
result | |||
end | |||
get '/suppliers/:id' do | |||
@supplier = Supplier.get(params[:id]) | |||
haml :supplier | |||
end | |||
get '/suppliers/?' do | |||
@suppliers = Supplier.all( :order => ['name'] ) | |||
haml :suppliers | |||
end | |||
get '/services/:id.csv' do | |||
@service = Service.get(params[:id]) | |||
headers "Content-Disposition" => "attachment;filename=service#{@service.id}.csv", | |||
"Content-Type" => "application/octet-stream" | |||
result = "Date,Trans No,Directorate,Supplier,Amount ex. VAT\n" | |||
for payment in @service.payments | |||
result += "#{payment.d.strftime("%d %b %Y")},#{payment.trans_no},\"#{payment.directorate.name}\",#{payment.supplier.name},#{sprintf("%0.2f", payment.amount)}\n" | |||
end | |||
result | |||
end | |||
get '/services/:id' do | |||
@service = Service.get(params[:id]) | |||
haml :service | |||
end | |||
get '/services/?' do | |||
@services = Service.all( :order => ['name'] ) | |||
haml :services | |||
end | |||
get '/wards/:slug/postcode/:postcode/?' do | |||
@ward = Ward.first(:slug => params[:slug]) | |||
@postcode = params[:postcode] | |||
haml :wards | |||
end | |||
get '/wards/:slug/?' do | |||
@ward = Ward.first(:slug => params[:slug]) | |||
haml :wards | |||
end | |||
get '/error' do | |||
haml :error | |||
end | |||
get '/about' do | |||
haml :about | |||
end | |||
not_found do | |||
haml :not_found | |||
end |
@@ -0,0 +1,45 @@ | |||
require 'lib/models' | |||
require 'csv' | |||
count = 0 | |||
CSV::Reader.parse(File.open('data/2009Q4.csv', 'rb')) do |row| | |||
# 2009Q4 Columns: | |||
# 0: Directorate | |||
# 1: Updated | |||
# 2: TransNo | |||
# 3: Service | |||
# 4: Cost Centre | |||
# 5: Supplier Name | |||
# 6: Amount excl vat | |||
# 7: Type | |||
count += 1 | |||
if (count > 4) # skip first four lines that don't contain data | |||
p row | |||
directorate = Directorate.first_or_create(:name => row[0].strip) | |||
service = Service.first_or_create(:name => row[3].strip) | |||
supplier = Supplier.first_or_create(:name => row[5].strip) | |||
payment = Payment.first_or_create( | |||
'trans_no' => row[2], | |||
'directorate' => directorate, | |||
'service' => service, | |||
'supplier' => supplier, | |||
'cost_centre' => row[4].strip, | |||
'amount' => row[6].strip.gsub(/,/, ''), | |||
'd' => row[1], | |||
'tyype' => row[7].strip | |||
) | |||
unless payment.save | |||
puts "ERROR: Failed to save payment" | |||
payment.errors.each do |e| | |||
puts e | |||
end | |||
end | |||
end | |||
end |
@@ -0,0 +1,39 @@ | |||
require 'lib/models' | |||
require 'csv' | |||
count = 0 | |||
# 2010Q1: 0-Directorate,1-Updated,2-Service,3-Supplier Name,4-Amount excl vat £,5-Type | |||
CSV::Reader.parse(File.open('data/2010Q1.csv', 'rb')) do |row| | |||
count += 1 | |||
if (count > 4) # skip first four lines that don't contain data | |||
p row | |||
directorate = Directorate.first_or_create(:name => row[0].strip) | |||
service = Service.first_or_create(:name => row[2].strip) | |||
supplier = Supplier.first_or_create(:name => row[3].strip) | |||
dt = row[1].strip.split('/') | |||
payment = Payment.first_or_create( | |||
'directorate' => directorate, | |||
'service' => service, | |||
'supplier' => supplier, | |||
'amount' => row[4].strip.gsub(/,/, ''), | |||
'd' => Date.new(dt[2].to_i, dt[1].to_i, dt[0].to_i), | |||
'tyype' => row[5].strip | |||
) | |||
unless payment.save | |||
puts "ERROR: Failed to save payment" | |||
payment.errors.each do |e| | |||
puts e | |||
end | |||
end | |||
end | |||
end |
@@ -0,0 +1,58 @@ | |||
require 'rubygems' | |||
require 'dm-core' | |||
require 'dm-validations' | |||
require 'dm-timestamps' | |||
require 'dm-aggregates' | |||
class Payment | |||
include DataMapper::Resource | |||
property :id, Serial | |||
property :trans_no, Integer, :required => false # "TransNo" in RBWM CSV files | |||
property :directorate_id, Integer, :required => true | |||
property :service_id, Integer, :required => true | |||
property :supplier_id, Integer, :required => true | |||
property :cost_centre, String, :required => false | |||
property :amount, BigDecimal, :precision => 10, :scale => 2, :required => true # ex VAT | |||
property :d, Date, :required => true # "Updated" in RBWM CSV files | |||
property :tyype, String, :required => true # Capital or Revenue | |||
belongs_to :directorate | |||
belongs_to :service | |||
belongs_to :supplier | |||
end | |||
class Directorate | |||
include DataMapper::Resource | |||
property :id, Serial | |||
property :name, String, :length => 255, :required => true | |||
has n, :payments, :order => ['d'] | |||
end | |||
class Service | |||
include DataMapper::Resource | |||
property :id, Serial | |||
property :name, String, :length => 255, :required => true | |||
has n, :payments, :order => ['d'] | |||
end | |||
class Supplier | |||
include DataMapper::Resource | |||
property :id, Serial | |||
property :name, String, :length => 255, :required => true | |||
has n, :payments, :order => ['d'] | |||
# def self.slugify(name) | |||
# name.gsub(/[^\w\s-]/, '').gsub(/\s+/, '-').downcase | |||
# end | |||
end | |||
DataMapper.setup(:default, ENV['DATABASE_URL'] || "sqlite3://#{Dir.pwd}/db.sqlite3") | |||
DataMapper.auto_upgrade! |
@@ -0,0 +1,42 @@ | |||
#breadcrumb { | |||
font: 11px Arial, Helvetica, sans-serif; | |||
background-image:url('/breadcrumb/bc_bg.gif'); | |||
background-repeat:repeat-x; | |||
height:30px; | |||
line-height:30px; | |||
color:#888; | |||
border:solid 1px #cacaca; | |||
width:100%; | |||
overflow:hidden; | |||
margin:0px; | |||
padding:0px; | |||
} | |||
#breadcrumb li { | |||
list-style-type:none; | |||
padding-left:10px; | |||
display:inline-block; | |||
float:left; | |||
} | |||
#breadcrumb a { | |||
display:inline-block; | |||
background-image:url('/breadcrumb/bc_separator.gif'); | |||
background-repeat:no-repeat; | |||
background-position:right; | |||
padding-right: 15px; | |||
text-decoration: none; | |||
color:#333333; | |||
outline:none; | |||
} | |||
.home { | |||
border:none; | |||
margin: 7px 0px; | |||
background-image:url('/breadcrumb/bc_separator.gif'); | |||
} | |||
#breadcrumb a:hover { | |||
color:#35acc5; | |||
} |
@@ -0,0 +1,338 @@ | |||
/* | |||
Variable Grid System. | |||
Learn more ~ http://www.spry-soft.com/grids/ | |||
Based on 960 Grid System - http://960.gs/ | |||
Licensed under GPL and MIT. | |||
*/ | |||
/* Containers | |||
----------------------------------------------------------------------------------------------------*/ | |||
.container_12 { | |||
margin-left: auto; | |||
margin-right: auto; | |||
width: 960px; | |||
} | |||
/* Grid >> Global | |||
----------------------------------------------------------------------------------------------------*/ | |||
.grid_1, | |||
.grid_2, | |||
.grid_3, | |||
.grid_4, | |||
.grid_5, | |||
.grid_6, | |||
.grid_7, | |||
.grid_8, | |||
.grid_9, | |||
.grid_10, | |||
.grid_11, | |||
.grid_12 { | |||
display:inline; | |||
float: left; | |||
position: relative; | |||
margin-left: 15px; | |||
margin-right: 15px; | |||
} | |||
/* Grid >> Children (Alpha ~ First, Omega ~ Last) | |||
----------------------------------------------------------------------------------------------------*/ | |||
.alpha { | |||
margin-left: 0; | |||
} | |||
.omega { | |||
margin-right: 0; | |||
} | |||
/* Grid >> 12 Columns | |||
----------------------------------------------------------------------------------------------------*/ | |||
.container_12 .grid_1 { | |||
width:50px; | |||
} | |||
.container_12 .grid_2 { | |||
width:130px; | |||
} | |||
.container_12 .grid_3 { | |||
width:210px; | |||
} | |||
.container_12 .grid_4 { | |||
width:290px; | |||
} | |||
.container_12 .grid_5 { | |||
width:370px; | |||
} | |||
.container_12 .grid_6 { | |||
width:450px; | |||
} | |||
.container_12 .grid_7 { | |||
width:530px; | |||
} | |||
.container_12 .grid_8 { | |||
width:610px; | |||
} | |||
.container_12 .grid_9 { | |||
width:690px; | |||
} | |||
.container_12 .grid_10 { | |||
width:770px; | |||
} | |||
.container_12 .grid_11 { | |||
width:850px; | |||
} | |||
.container_12 .grid_12 { | |||
width:930px; | |||
} | |||
/* Prefix Extra Space >> 12 Columns | |||
----------------------------------------------------------------------------------------------------*/ | |||
.container_12 .prefix_1 { | |||
padding-left:80px; | |||
} | |||
.container_12 .prefix_2 { | |||
padding-left:160px; | |||
} | |||
.container_12 .prefix_3 { | |||
padding-left:240px; | |||
} | |||
.container_12 .prefix_4 { | |||
padding-left:320px; | |||
} | |||
.container_12 .prefix_5 { | |||
padding-left:400px; | |||
} | |||
.container_12 .prefix_6 { | |||
padding-left:480px; | |||
} | |||
.container_12 .prefix_7 { | |||
padding-left:560px; | |||
} | |||
.container_12 .prefix_8 { | |||
padding-left:640px; | |||
} | |||
.container_12 .prefix_9 { | |||
padding-left:720px; | |||
} | |||
.container_12 .prefix_10 { | |||
padding-left:800px; | |||
} | |||
.container_12 .prefix_11 { | |||
padding-left:880px; | |||
} | |||
/* Suffix Extra Space >> 12 Columns | |||
----------------------------------------------------------------------------------------------------*/ | |||
.container_12 .suffix_1 { | |||
padding-right:80px; | |||
} | |||
.container_12 .suffix_2 { | |||
padding-right:160px; | |||
} | |||
.container_12 .suffix_3 { | |||
padding-right:240px; | |||
} | |||
.container_12 .suffix_4 { | |||
padding-right:320px; | |||
} | |||
.container_12 .suffix_5 { | |||
padding-right:400px; | |||
} | |||
.container_12 .suffix_6 { | |||
padding-right:480px; | |||
} | |||
.container_12 .suffix_7 { | |||
padding-right:560px; | |||
} | |||
.container_12 .suffix_8 { | |||
padding-right:640px; | |||
} | |||
.container_12 .suffix_9 { | |||
padding-right:720px; | |||
} | |||
.container_12 .suffix_10 { | |||
padding-right:800px; | |||
} | |||
.container_12 .suffix_11 { | |||
padding-right:880px; | |||
} | |||
/* Push Space >> 12 Columns | |||
----------------------------------------------------------------------------------------------------*/ | |||
.container_12 .push_1 { | |||
left:80px; | |||
} | |||
.container_12 .push_2 { | |||
left:160px; | |||
} | |||
.container_12 .push_3 { | |||
left:240px; | |||
} | |||
.container_12 .push_4 { | |||
left:320px; | |||
} | |||
.container_12 .push_5 { | |||
left:400px; | |||
} | |||
.container_12 .push_6 { | |||
left:480px; | |||
} | |||
.container_12 .push_7 { | |||
left:560px; | |||
} | |||
.container_12 .push_8 { | |||
left:640px; | |||
} | |||
.container_12 .push_9 { | |||
left:720px; | |||
} | |||
.container_12 .push_10 { | |||
left:800px; | |||
} | |||
.container_12 .push_11 { | |||
left:880px; | |||
} | |||
/* Pull Space >> 12 Columns | |||
----------------------------------------------------------------------------------------------------*/ | |||
.container_12 .pull_1 { | |||
left:-80px; | |||
} | |||
.container_12 .pull_2 { | |||
left:-160px; | |||
} | |||
.container_12 .pull_3 { | |||
left:-240px; | |||
} | |||
.container_12 .pull_4 { | |||
left:-320px; | |||
} | |||
.container_12 .pull_5 { | |||
left:-400px; | |||
} | |||
.container_12 .pull_6 { | |||
left:-480px; | |||
} | |||
.container_12 .pull_7 { | |||
left:-560px; | |||
} | |||
.container_12 .pull_8 { | |||
left:-640px; | |||
} | |||
.container_12 .pull_9 { | |||
left:-720px; | |||
} | |||
.container_12 .pull_10 { | |||
left:-800px; | |||
} | |||
.container_12 .pull_11 { | |||
left:-880px; | |||
} | |||
/* Clear Floated Elements | |||
----------------------------------------------------------------------------------------------------*/ | |||
/* http://sonspring.com/journal/clearing-floats */ | |||
.clear { | |||
clear: both; | |||
display: block; | |||
overflow: hidden; | |||
visibility: hidden; | |||
width: 0; | |||
height: 0; | |||
} | |||
/* http://perishablepress.com/press/2008/02/05/lessons-learned-concerning-the-clearfix-css-hack */ | |||
.clearfix:after { | |||
clear: both; | |||
content: ' '; | |||
display: block; | |||
font-size: 0; | |||
line-height: 0; | |||
visibility: hidden; | |||
width: 0; | |||
height: 0; | |||
} | |||
.clearfix { | |||
display: inline-block; | |||
} | |||
* html .clearfix { | |||
height: 1%; | |||
} | |||
.clearfix { | |||
display: block; | |||
} |
@@ -0,0 +1,9 @@ | |||
.noprint, #footer, #breadcrumb | |||
{ | |||
display: none; | |||
} | |||
body | |||
{ | |||
margin: 0 auto; | |||
} |
@@ -0,0 +1,121 @@ | |||
body | |||
{ | |||
background-color: #fff; | |||
color: #555; | |||
font-family: Helvetica, Arial, sans-serif; | |||
font-size: 100%; | |||
line-height: 1.5em; | |||
} | |||
p | |||
{ | |||
font-size: 110%; | |||
} | |||
input | |||
{ | |||
font-size: 130%; | |||
background-color: #fff; | |||
} | |||
#main | |||
{ | |||
margin: 30px 0; | |||
} | |||
#footer | |||
{ | |||
font-size: 100%; | |||
background-color: #fff; | |||
text-align: left; | |||
margin: 40px 0 40px 0; | |||
} | |||
a | |||
{ | |||
background-color: #dce9b0; | |||
padding: 1px 4px; | |||
color: #111; | |||
text-decoration: none; | |||
} | |||
a:visited | |||
{ | |||
background-color: #eee; | |||
padding: 1px 4px; | |||
color: #111; | |||
text-decoration: none; | |||
} | |||
a:hover | |||
{ | |||
background-color: #4f4f4f; | |||
color: #fff; | |||
} | |||
h1 | |||
{ | |||
margin-top: 20px; | |||
line-height: 1.4em; | |||
font-weight: bold; | |||
color: #86a11d; | |||
} | |||
h2 | |||
{ | |||
margin-top: 20px; | |||
line-height: 1.5em; | |||
font-weight: bold; | |||
color: #86a11d; | |||
} | |||
form | |||
{ | |||
font-size: 150%; | |||
} | |||
.highlight | |||
{ | |||
background-color: #fff7c0; | |||
padding: 5px; | |||
} | |||
strong | |||
{ | |||
color: #000; | |||
} | |||
table | |||
{ | |||
border-collapse: collapse; | |||
} | |||
td, th | |||
{ | |||
padding: 6px; | |||
} | |||
th | |||
{ | |||
text-align: left; | |||
} | |||
tr | |||
{ | |||
border-bottom: 1px solid #eee; | |||
} | |||
.right | |||
{ | |||
text-align: right; | |||
} | |||
.noborder | |||
{ | |||
border: 0; | |||
} |
@@ -0,0 +1,55 @@ | |||
.grid_9 | |||
%h1= @page_title = "About this website" | |||
%blockquote | |||
The swift and simple changes we are calling for today [to encourage councils to publish spending data] will unleash an army of armchair auditors and quite rightly make those charged with doling out the pennies stop and think twice about whether they are getting value for money. | |||
%p.right — Eric Pickles, Communities & Local Government Secretary, 5 June 2010 | |||
<!-- http://news.bbc.co.uk/1/hi/politics/10241522.stm --> | |||
%p.vcard | |||
This website was designed and written by | |||
%a.fn.url{ :href => 'http://adrianshort.co.uk' } Adrian Short | |||
\. | |||
You can contact me by email at | |||
%a.email{ :href => "mailto:adrian.short@gmail.com" } adrian.short@gmail.com | |||
and | |||
%a.url{ :href => "http://twitter.com/adrianshort" } follow me on Twitter | |||
\. | |||
%p.highlight | |||
This site is made with | |||
%a{ :href => 'http://www.rbwm.gov.uk/web/finance_payments_to_suppliers.htm' } | |||
the Royal Borough of Windsor and Maidenhead's spending data | |||
and is entirely indepdendent of the council. The council did not commission or pay for this website. | |||
%p Every page on this website prints beautifully. | |||
%p | |||
This site is written in | |||
%a{ :href => "http://www.ruby-lang.org/en/" }Ruby | |||
\ using the | |||
%a{ :href => "http://www.sinatrarb.com/" }Sinatra framework. | |||
%p | |||
The code for this website is | |||
%a{ :href => "http://github.com/adrianshort/Sutton-Elections" }open source and managed on Github. | |||
It is hosted by | |||
%a{ :href => "http://heroku.com/" }Heroku. | |||
%p | |||
The page templates use | |||
%a{ :href => "http://haml-lang.com/" }Haml | |||
and SprySoft's | |||
%a{ :href => "http://www.spry-soft.com/grids/" }Variable Grid System | |||
\. The database is | |||
%a{ :href => "http://www.sqlite.org/" }SQLite | |||
for development and | |||
%a{ :href => "http://www.postgresql.org/" }PostgreSQL | |||
for production, abstracted through | |||
%a{ :href => "http://datamapper.org/" }DataMapper. | |||
%p | |||
Source control and deployment is done with | |||
%a{ :href => "http://git-scm.com/" }Git. | |||
@@ -0,0 +1,30 @@ | |||
.grid_12 | |||
%h2= @page_title = @directorate.name + " Directorate" | |||
%ul#breadcrumb | |||
%li.home | |||
%a{ :href => '/'} Home | |||
%li | |||
%a{ :href => '/directorates' } Directorates | |||
%li | |||
= @directorate.name | |||
%table | |||
%tr | |||
%th Date | |||
%th Service | |||
%th Supplier | |||
%th £ | |||
- for payment in @directorate.payments | |||
%tr | |||
%td= payment.d.strftime("%d %b %Y") | |||
%td | |||
%a{ :href => '/services/' + payment.service.id.to_s } | |||
= payment.service.name | |||
%td | |||
%a{ :href => '/suppliers/' + payment.supplier.id.to_s } | |||
= payment.supplier.name | |||
%td.right= sprintf("%0d", payment.amount) | |||
@@ -0,0 +1,5 @@ | |||
.grid_12 | |||
%h1 Invalid postcode | |||
%p | |||
%a{ :href => "/" }Please go back and try again | |||
@@ -0,0 +1,11 @@ | |||
.grid_12 | |||
%ul#breadcrumb | |||
%li.home | |||
%h2 Directorates | |||
- for directorate in @directorates | |||
%p | |||
%a{ :href=> "/directorates/#{directorate.id}" } | |||
= directorate.name |
@@ -0,0 +1,26 @@ | |||
!!! XML | |||
!!! | |||
%html | |||
%head | |||
%title= @page_title ? @page_title + " - Armchair Auditor" : "Armchair Auditor" | |||
%link{ :rel => 'stylesheet', :type => 'text/css', :href => '/style.css' } | |||
%link{ :rel => 'stylesheet', :type => 'text/css', :href => '/breadcrumb/breadcrumb.css' } | |||
%link{ :rel => 'stylesheet', :type => 'text/css', :href => '/print.css', :media => 'print' } | |||
%link{ :rel => 'stylesheet', :type => 'text/css', :href => '/grid.css' } | |||
%body | |||
.container_12 | |||
.grid_12 | |||
%h1 Royal Borough of Windsor & Maidenhead Armchair Auditor | |||
= yield | |||
.clear | |||
#footer | |||
.grid_12 | |||
%p | |||
%a{ :href => '/' } Home | |||
%p | |||
%a{ :href => '/services' } Services | |||
%p | |||
%a{ :href => '/suppliers' } Suppliers | |||
%p | |||
%a{ :href => '/about' } About this website |
@@ -0,0 +1,7 @@ | |||
.grid_9 | |||
%h1 Not Found | |||
%p Oops, we can't find that page. | |||
%p | |||
%a{ :href => '/' }Go back to the home page |
@@ -0,0 +1,33 @@ | |||
.grid_12 | |||
%h2= @page_title = @service.name + " (Service)" | |||
%ul#breadcrumb | |||
%li.home | |||
%a{ :href => '/'} Home | |||
%li | |||
%a{ :href => '/services' } Services | |||
%li | |||
= @service.name | |||
%p | |||
%a{ :href => "/services/#{@service.id}.csv" } | |||
Download data as CSV | |||
%table | |||
%tr | |||
%th Date | |||
%th Directorate | |||
%th Supplier | |||
%th £ | |||
- for payment in @service.payments | |||
%tr | |||
%td= payment.d.strftime("%d %b %Y") | |||
%td | |||
%a{ :href => '/directorates/' + payment.directorate.id.to_s } | |||
= payment.directorate.name | |||
%td | |||
%a{ :href => '/suppliers/' + payment.supplier.id.to_s } | |||
= payment.supplier.name | |||
%td.right= sprintf("%0d", payment.amount) | |||
@@ -0,0 +1,13 @@ | |||
.grid_12 | |||
%h2 Services | |||
%ul#breadcrumb | |||
%li.home | |||
%a{ :href => '/'} Home | |||
%li | |||
Services | |||
- for service in @services | |||
%p | |||
%a{ :href=> "/services/#{service.id}" } | |||
= service.name |
@@ -0,0 +1,34 @@ | |||
.grid_12 | |||
%h2= @page_title = @supplier.name + " (Supplier)" | |||
%ul#breadcrumb | |||
%li.home | |||
%a{ :href => '/'} Home | |||
%li | |||
%a{ :href => '/suppliers' } Suppliers | |||
%li | |||
= @supplier.name | |||
%p.noprint | |||
%a{ :href => "/suppliers/#{@supplier.id}.csv" } | |||
Download data as CSV | |||
%table | |||
%tr | |||
%th Date | |||
%th Directorate | |||
%th Service | |||
%th £ | |||
- for payment in @supplier.payments | |||
%tr | |||
%td= payment.d.strftime("%d %b %Y") | |||
%td | |||
%a{ :href => '/directorates/' + payment.directorate.id.to_s } | |||
= payment.directorate.name | |||
%td | |||
%a{ :href => '/services/' + payment.service.id.to_s } | |||
= payment.service.name | |||
%td.right= sprintf("%0d", payment.amount) | |||
@@ -0,0 +1,13 @@ | |||
.grid_12 | |||
%h2 Suppliers | |||
%ul#breadcrumb | |||
%li.home | |||
%a{ :href => '/'} Home | |||
%li | |||
Suppliers | |||
- for supplier in @suppliers | |||
%p | |||
%a{ :href=> "/suppliers/#{supplier.id}" } | |||
= supplier.name |