stoptime/stoptime.rb

371 lines
9.5 KiB
Ruby
Raw Normal View History

#!/usr/bin/env camping
#
# stoptime.rb - The Stop… Camping Time! time registration and invoice
# application.
#
# Stop… Camping Time! is Copyright © 2011 Paul van Tilburg <paul@luon.net>
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2 of the License, or (at your
# option) any later version.
require "active_support"
require "camping"
require "markaby"
require "pathname"
Markaby::Builder.set(:indent, 2)
Camping.goes :StopTime
unless defined? BASE_DIR
BASE_DIR = Pathname.new(__FILE__).dirname.expand_path + "public"
# Set the default date(/time) format.
ActiveSupport::CoreExtensions::Time::Conversions::DATE_FORMATS.merge!(
:default => "%Y-%m-%d %H:%M")
ActiveSupport::CoreExtensions::Date::Conversions::DATE_FORMATS.merge!(
:default => "%Y-%m-%d")
end
module StopTime
def self.create
StopTime::Models.create_schema
end
end
module StopTime::Models
class Customer < Base
has_many :tasks
end
class Task < Base
has_many :time_entries
belongs_to :customer
end
class TimeEntry < Base
belongs_to :task
end
class StopTimeTables < V 1.0
def self.up
create_table Customer.table_name do |t|
t.string :name, :short_name,
:address_street, :address_postal_code, :address_city,
:email, :phone
t.timestamps
end
create_table Task.table_name do |t|
t.integer :customer_id
t.string :name
t.timestamps
end
create_table TimeEntry.table_name do |t|
t.integer :task_id
t.datetime :start, :end
t.timestamps
end
end
def self.down
drop_table Customer.table_name
drop_table Task.table_name
drop_table TimeEntry.table_name
end
end
end # module StopTime::Models
module StopTime::Controllers
class Index
def get
redirect R(Timereg)
end
end
2011-10-31 16:14:54 +01:00
class Customers
def get
@customers = Customer.all
render :customers
end
def post
return redirect R(Customers) if @input.cancel
@customer = Customer.create(
:name => @input.name,
:short_name => @input.short_name,
:address_street => @input.address_street,
:address_postal_code => @input.address_postal_code,
:address_city => @input.address_city,
:email => @input.email,
:phone => @input.phone)
@customer.save
if @customer.invalid?
@errors = @customer.errors
return render :customer_new
end
redirect R(Customers)
end
end
class CustomersNew
def get
render :customer_form
end
end
class CustomersN
2011-10-31 16:14:54 +01:00
def get(customer_id)
@customer = Customer.find(customer_id)
render :customer_form
2011-10-31 16:14:54 +01:00
end
def post(customer_id)
return redirect R(Customers) if @input.cancel
@customer = Customer.find(customer_id)
if @input.has_key? "delete"
@customer.delete
elsif @input.has_key? "save"
attrs = ["name", "short_name",
"address_street", "address_postal_code", "address_city",
"email", "phone"]
attrs.each do |attr|
@customer[attr] = @input[attr] unless @input[attr].blank?
end
@customer.save
if @customer.invalid?
@errors = @customer.errors
return render :customer_form
end
end
redirect R(Customers)
2011-10-31 16:14:54 +01:00
end
end
class CustomersNTasks
def post(customer_id)
if @input.has_key? "add"
@task = Task.create(
:customer_id => customer_id,
:name => @input.new_task)
@task.save
if @task.invalid?
@errors = @task.errors
end
elsif @input.has_key? "delete"
@input.tasks.each { |task_id| Task.find(task_id).delete }
end
redirect R(CustomersN, customer_id)
2011-10-31 16:14:54 +01:00
end
end
class Timereg
2011-10-31 16:14:54 +01:00
def get
@time_entries = TimeEntry.all
@customer_list = Customer.all.map { |c| [c.id, c.short_name] }
@task_list = Task.all.map { |t| [t.id, t.name] }
render :time_entries
2011-10-31 16:14:54 +01:00
end
def post
if @input.has_key? "enter"
@entry = TimeEntry.create(
:task_id => @input.task,
:start => @input.start,
:end => @input.end)
@entry.save
if @entry.invalid?
@errors = @entry.errors
end
elsif @input.has_key? "delete"
end
2011-10-31 16:14:54 +01:00
@time_entries = TimeEntry.all
@customer_list = Customer.all.map { |c| [c.id, c.short_name] }
@task_list = Task.all.map { |t| [t.id, t.name] }
2011-10-31 16:14:54 +01:00
render :time_entries
end
end
class TimeregN
def post(entry_id)
TimeEntry.find(entry_id).delete
redirect R(Timereg)
end
end
class Invoices
def get
render :invoices
end
end
end # module StopTime::Controllers
module StopTime::Views
def layout
xhtml_strict do
head do
title "Stop… Camping Time!"
end
body do
div.wrapper! do
h1 "Stop… Camping Time!"
_menu
div.content! do
self << yield
end
end
end
end
end
def _menu
ol.menu! do
li { a "Time Registration", :href => R(Timereg) }
li { a "Customers", :href => R(Customers) }
li { a "Invoices", :href => R(Invoices) }
end
2011-10-31 16:14:54 +01:00
end
def time_entries
h2 "List of time entries"
table do
tr do
th "Customer"
th "Project/task"
th "Start time"
th "End time"
th "Total"
end
form :action => R(Timereg), :method => :post do
tr do
td { _form_select("customer", @customer_list) }
td { _form_select("task", @task_list) }
td { input :type => :text, :name => "start" }
td { input :type => :text, :name => "end" }
td { "N/A" }
td do
input :type => :submit, :name => "enter", :value => "Enter"
input :type => :reset, :name => "clear", :value => "Clear"
end
end
end
@time_entries.each do |entry|
tr do
td { entry.task.customer.short_name }
td { entry.task.name }
td { entry.start }
td { entry.end }
td { "%.2fh" % ((entry.end - entry.start)/3600.0) }
td do
form :action => R(TimeregN, entry.id), :method => :post do
input :type => :submit, :name => "delete", :value => "Delete"
end
end
end
end
2011-10-31 16:14:54 +01:00
end
end
def customers
h2 "List of customers"
2011-10-31 16:14:54 +01:00
table do
tr do
th "Name"
th "Short name"
th "Address"
2011-10-31 16:14:54 +01:00
th "Email"
th "Phone"
end
@customers.each do |customer|
tr do
td { customer.name }
td { customer.short_name }
td { [customer.address_street,
customer.address_postal_code,
customer.address_city].join(", ") unless customer.address_street.blank? }
td { customer.email }
td { customer.phone }
td do
form :action => R(CustomersN, customer.id), :method => :get do
input :type => :submit, :value => "Edit"
end
form :action => R(CustomersN, customer.id), :method => :post do
input :type => :submit, :name => "delete", :value => "Delete"
end
end
2011-10-31 16:14:54 +01:00
end
end
end
p do
a "Add a new customer", :href=> R(CustomersNew)
2011-10-31 16:14:54 +01:00
end
end
def customer_form
2011-10-31 16:14:54 +01:00
if @customer
@edit_task = true
target = [CustomersN, @customer.id]
2011-10-31 16:14:54 +01:00
else
@customer = {}
target = [Customers]
end
form :action => R(*target), :method => :post do
ol do
li { _form_input(@customer, "Name", "name", :text) }
li { _form_input(@customer, "Short name", "short_name", :text) }
li { _form_input(@customer, "Street address", "address_street", :text) }
li { _form_input(@customer, "Postal code", "address_postal_code", :text) }
li { _form_input(@customer, "City/town", "address_city", :text) }
li { _form_input(@customer, "Email address", "email", :text) }
li { _form_input(@customer, "Phone number", "phone", :text) }
2011-10-31 16:14:54 +01:00
end
input :type => "submit", :name => "save", :value => "Save"
input :type => "submit", :name => "cancel", :value => "Cancel"
end
if @edit_task
form :action => R(CustomersNTasks, @customer.id), :method => :post do
h2 "Projects & Tasks"
select :name => "tasks[]", :multiple => "multiple", :size => 6 do
@customer.tasks.each do |task|
option(:value => task.id) { task.name }
end
end
input :type => :text, :name => "new_task"
input :type => :submit, :name => "add", :value => "Add"
input :type => :submit, :name => "delete", :value => "Delete"
end
end
end
def invoices
h2 "List of invoices"
2011-10-31 16:14:54 +01:00
end
def _form_input(obj, label_name, input_name, type, options={})
2011-10-31 16:14:54 +01:00
label label_name, :for => input_name
input :type => type, :name => input_name, :id => input_name,
:value => @input[input_name] || obj[input_name]
end
def _form_select(name, options)
select :name => name, :id => name do
options.each do |opt_val, opt_str|
if opt_val == @input[name]
option(:value => opt_val, :selected => "true") { opt_str }
else
option(:value => opt_val) { opt_str }
end
end
end
end
end # module StopTime::Views