Compare commits
12 Commits
developmen
...
feature/lo
Author | SHA1 | Date |
---|---|---|
Bram Senders | b31cd03330 | |
Bram Senders | d706e91e9d | |
Bram Senders | 553a919cb1 | |
Bram Senders | 0ae19e3376 | |
Bram Senders | 1da6ca6e85 | |
Bram Senders | 2b4acbdd35 | |
Bram Senders | f8286ba862 | |
Bram Senders | 2926b30969 | |
Bram Senders | 803a8894dc | |
Bram Senders | f9836029c7 | |
Bram Senders | 1b00857d62 | |
Bram Senders | a4e42d9aca |
201
stoptime.rb
201
stoptime.rb
|
@ -156,6 +156,7 @@ module StopTime::Models
|
||||||
# task.
|
# task.
|
||||||
class TimeEntry < Base
|
class TimeEntry < Base
|
||||||
belongs_to :task
|
belongs_to :task
|
||||||
|
belongs_to :location
|
||||||
has_one :customer, :through => :task
|
has_one :customer, :through => :task
|
||||||
|
|
||||||
# Returns the total amount of time, the duration, in hours.
|
# Returns the total amount of time, the duration, in hours.
|
||||||
|
@ -170,6 +171,7 @@ module StopTime::Models
|
||||||
# tasks and through the tasks registered time.
|
# tasks and through the tasks registered time.
|
||||||
class Invoice < Base
|
class Invoice < Base
|
||||||
has_many :tasks
|
has_many :tasks
|
||||||
|
has_many :locations
|
||||||
has_many :time_entries, :through => :tasks
|
has_many :time_entries, :through => :tasks
|
||||||
belongs_to :customer
|
belongs_to :customer
|
||||||
|
|
||||||
|
@ -203,6 +205,22 @@ module StopTime::Models
|
||||||
class CompanyInfo < Base
|
class CompanyInfo < Base
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# == The location class
|
||||||
|
#
|
||||||
|
# This class represents a specific location where work can be done.
|
||||||
|
# Each location has a name, a distance (in kilometres, from the user's
|
||||||
|
# home) and travel time (in minutes, also from the user's home). Each
|
||||||
|
# time entry can be associated with a location.
|
||||||
|
class Location < Base
|
||||||
|
has_many :time_entries
|
||||||
|
belongs_to :invoice
|
||||||
|
|
||||||
|
# Returns whether the location is billed, i.e. included in an invoice.
|
||||||
|
def billed?
|
||||||
|
not invoice.nil?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
class StopTimeTables < V 1.0 # :nodoc:
|
class StopTimeTables < V 1.0 # :nodoc:
|
||||||
def self.up
|
def self.up
|
||||||
create_table Customer.table_name do |t|
|
create_table Customer.table_name do |t|
|
||||||
|
@ -341,6 +359,23 @@ module StopTime::Models
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class LocationSupport < V 1.9 # :nodoc:
|
||||||
|
def self.up
|
||||||
|
create_table Location.table_name do |t|
|
||||||
|
t.string :name
|
||||||
|
t.float :distance, :travel_time
|
||||||
|
t.integer :invoice_id
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
add_column(TimeEntry.table_name, :location_id, :integer)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.down
|
||||||
|
drop_table Location.table_name
|
||||||
|
remove_column(TimeEntry.table_name, :location_id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end # StopTime::Models
|
end # StopTime::Models
|
||||||
|
|
||||||
# = The Stop… Camping Time! controllers
|
# = The Stop… Camping Time! controllers
|
||||||
|
@ -792,6 +827,9 @@ module StopTime::Controllers
|
||||||
@task_list = Task.all.reject { |t| t.billed? }.map do |t|
|
@task_list = Task.all.reject { |t| t.billed? }.map do |t|
|
||||||
[t.id, t.name]
|
[t.id, t.name]
|
||||||
end
|
end
|
||||||
|
@location_list = Location.all.reject { |l| l.billed? }.map do |l|
|
||||||
|
[l.id, l.name]
|
||||||
|
end
|
||||||
@input["bill"] = true # Bill by default.
|
@input["bill"] = true # Bill by default.
|
||||||
render :time_entries
|
render :time_entries
|
||||||
end
|
end
|
||||||
|
@ -802,6 +840,7 @@ module StopTime::Controllers
|
||||||
if @input.has_key? "enter"
|
if @input.has_key? "enter"
|
||||||
@time_entry = TimeEntry.create(
|
@time_entry = TimeEntry.create(
|
||||||
:task_id => @input.task,
|
:task_id => @input.task,
|
||||||
|
:location_id => @input.location,
|
||||||
:date => @input.date,
|
:date => @input.date,
|
||||||
:start => "#{@input.date} #{@input.start}",
|
:start => "#{@input.date} #{@input.start}",
|
||||||
:end => "#{@input.date} #{@input.end}",
|
:end => "#{@input.date} #{@input.end}",
|
||||||
|
@ -858,6 +897,7 @@ module StopTime::Controllers
|
||||||
@input = @time_entry.attributes
|
@input = @time_entry.attributes
|
||||||
@input["customer"] = @time_entry.task.customer.id
|
@input["customer"] = @time_entry.task.customer.id
|
||||||
@input["task"] = @time_entry.task.id
|
@input["task"] = @time_entry.task.id
|
||||||
|
@input["location"] = @time_entry.location.id
|
||||||
@input["date"] = @time_entry.date.to_date
|
@input["date"] = @time_entry.date.to_date
|
||||||
@input["start"] = @time_entry.start.to_formatted_s(:time_only)
|
@input["start"] = @time_entry.start.to_formatted_s(:time_only)
|
||||||
@input["end"] = @time_entry.end.to_formatted_s(:time_only)
|
@input["end"] = @time_entry.end.to_formatted_s(:time_only)
|
||||||
|
@ -890,6 +930,7 @@ module StopTime::Controllers
|
||||||
@time_entry.start = "#{@input["date"]} #{@input["start"]}"
|
@time_entry.start = "#{@input["date"]} #{@input["start"]}"
|
||||||
@time_entry.end = "#{@input["date"]} #{@input["end"]}"
|
@time_entry.end = "#{@input["date"]} #{@input["end"]}"
|
||||||
@time_entry.task = Task.find(@input.task)
|
@time_entry.task = Task.find(@input.task)
|
||||||
|
@time_entry.location = Location.find(@input.location)
|
||||||
@time_entry.bill = @input.has_key? "bill"
|
@time_entry.bill = @input.has_key? "bill"
|
||||||
@time_entry.save
|
@time_entry.save
|
||||||
if @time_entry.invalid?
|
if @time_entry.invalid?
|
||||||
|
@ -975,6 +1016,102 @@ module StopTime::Controllers
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# == The locations controller
|
||||||
|
#
|
||||||
|
# Controller for viewing a list of existing locations or creating a new
|
||||||
|
# one.
|
||||||
|
#
|
||||||
|
# path:: /locations
|
||||||
|
# view:: Views#locations and Views#location_form
|
||||||
|
class Locations
|
||||||
|
# Gets the list of locations and displays them via Views#locations.
|
||||||
|
def get
|
||||||
|
@locations = Location.all
|
||||||
|
render :locations
|
||||||
|
end
|
||||||
|
|
||||||
|
# Creates a new location object (Models::Location) if the input is
|
||||||
|
# valid and redirects to LocationsN.
|
||||||
|
# If the provided information is invalid, the errors are retrieved
|
||||||
|
# and shown in the initial form (Views#location_form).
|
||||||
|
def post
|
||||||
|
return redirect R(Locations) if @input.cancel
|
||||||
|
@location = Location.create(
|
||||||
|
:name => @input.name,
|
||||||
|
:distance => @input.distance,
|
||||||
|
:travel_time => @input.travel_time)
|
||||||
|
@location.save
|
||||||
|
if @location.invalid?
|
||||||
|
@errors = @location.errors
|
||||||
|
@target = [Location]
|
||||||
|
@button = "create"
|
||||||
|
return render :location_form
|
||||||
|
end
|
||||||
|
redirect R(LocationsN, @location.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# == The location creation controller
|
||||||
|
#
|
||||||
|
# Controller for filling in the information to create a new location.
|
||||||
|
#
|
||||||
|
# path:: /locations/new
|
||||||
|
# view:: Views#location_form
|
||||||
|
class LocationsNew
|
||||||
|
# Generates the form to create a new location object
|
||||||
|
# (Models::Location) using Views#location_form.
|
||||||
|
def get
|
||||||
|
@location = Location.new
|
||||||
|
@input = @location.attributes
|
||||||
|
|
||||||
|
@target = [Locations]
|
||||||
|
@button = "create"
|
||||||
|
render :location_form
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# == The location controller
|
||||||
|
#
|
||||||
|
# Controller for viewing and updating information of a location.
|
||||||
|
#
|
||||||
|
# path:: /locations/_location_id_
|
||||||
|
# view:: Views#location_form
|
||||||
|
class LocationsN
|
||||||
|
# Finds the specific location for the given _location_id_ and shows
|
||||||
|
# a form for updating via Views#location_form.
|
||||||
|
def get(location_id)
|
||||||
|
@location = Location.find(location_id)
|
||||||
|
@input = @location.attributes
|
||||||
|
|
||||||
|
@target = [LocationsN, @location.id]
|
||||||
|
@button = "update"
|
||||||
|
render :location_form
|
||||||
|
end
|
||||||
|
|
||||||
|
# Updates or deletes the location with the given _location_id_ if the
|
||||||
|
# input is valid and redirects to LocationsN.
|
||||||
|
# If the provided information is invalid, the errors are retrieved
|
||||||
|
# and shown in the initial form (Views#location_form).
|
||||||
|
def post(location_id)
|
||||||
|
return redirect R(Locations) if @input.cancel
|
||||||
|
@location = Location.find(location_id)
|
||||||
|
if @input.has_key? "delete"
|
||||||
|
@location.delete
|
||||||
|
elsif @input.has_key? "update"
|
||||||
|
attrs = ["name", "distance", "travel_time"]
|
||||||
|
attrs.each do |attr|
|
||||||
|
@location[attr] = @input[attr]
|
||||||
|
end
|
||||||
|
@location.save
|
||||||
|
if @location.invalid?
|
||||||
|
@errors = @location.errors
|
||||||
|
return render :location_form
|
||||||
|
end
|
||||||
|
end
|
||||||
|
redirect R(Locations)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# == The static data controller
|
# == The static data controller
|
||||||
#
|
#
|
||||||
# Controller for serving static data information available in the
|
# Controller for serving static data information available in the
|
||||||
|
@ -1031,7 +1168,8 @@ module StopTime::Views
|
||||||
["Timeline", Timeline],
|
["Timeline", Timeline],
|
||||||
["Customers", Customers],
|
["Customers", Customers],
|
||||||
["Invoices", Invoices],
|
["Invoices", Invoices],
|
||||||
["Company", Company]].each { |label, ctrl| _menu_link(label, ctrl) }
|
["Company", Company],
|
||||||
|
["Locations", Locations]].each { |label, ctrl| _menu_link(label, ctrl) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1095,6 +1233,7 @@ module StopTime::Views
|
||||||
h2 "Timeline"
|
h2 "Timeline"
|
||||||
table.timeline do
|
table.timeline do
|
||||||
col.task {}
|
col.task {}
|
||||||
|
col.location {}
|
||||||
col.date {}
|
col.date {}
|
||||||
col.start_time {}
|
col.start_time {}
|
||||||
col.end_time {}
|
col.end_time {}
|
||||||
|
@ -1103,6 +1242,7 @@ module StopTime::Views
|
||||||
col.flag {}
|
col.flag {}
|
||||||
tr do
|
tr do
|
||||||
th "Project/Task"
|
th "Project/Task"
|
||||||
|
th "Location"
|
||||||
th "Date"
|
th "Date"
|
||||||
th "Start time"
|
th "Start time"
|
||||||
th "End time"
|
th "End time"
|
||||||
|
@ -1113,6 +1253,7 @@ module StopTime::Views
|
||||||
form :action => R(Timeline), :method => :post do
|
form :action => R(Timeline), :method => :post do
|
||||||
tr do
|
tr do
|
||||||
td { _form_select("task", @task_list) }
|
td { _form_select("task", @task_list) }
|
||||||
|
td { _form_select("location", @location_list) }
|
||||||
td { input :type => :text, :name => "date",
|
td { input :type => :text, :name => "date",
|
||||||
:value => DateTime.now.to_date.to_formatted_s }
|
:value => DateTime.now.to_date.to_formatted_s }
|
||||||
td { input :type => :text, :name => "start",
|
td { input :type => :text, :name => "start",
|
||||||
|
@ -1131,6 +1272,12 @@ module StopTime::Views
|
||||||
tr do
|
tr do
|
||||||
td { a entry.task.name,
|
td { a entry.task.name,
|
||||||
:href => R(CustomersNTasksN, entry.customer.id, entry.task.id) }
|
:href => R(CustomersNTasksN, entry.customer.id, entry.task.id) }
|
||||||
|
if entry.location
|
||||||
|
td { a entry.location.name,
|
||||||
|
:href => R(LocationsN, entry.location.id) }
|
||||||
|
else
|
||||||
|
td { "unspecified" }
|
||||||
|
end
|
||||||
td { a entry.date.to_date,
|
td { a entry.date.to_date,
|
||||||
:href => R(TimelineN, entry.id) }
|
:href => R(TimelineN, entry.id) }
|
||||||
td { entry.start.to_formatted_s(:time_only) }
|
td { entry.start.to_formatted_s(:time_only) }
|
||||||
|
@ -1564,6 +1711,58 @@ module StopTime::Views
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# The main overview of the list of locations.
|
||||||
|
def locations
|
||||||
|
h2 "Locations"
|
||||||
|
if @locations.empty?
|
||||||
|
p do
|
||||||
|
text "None found! You can create one "
|
||||||
|
a "here", :href => R(LocationsNew)
|
||||||
|
text "."
|
||||||
|
end
|
||||||
|
else
|
||||||
|
table.locations do
|
||||||
|
col.name {}
|
||||||
|
col.distance {}
|
||||||
|
col.travel_time {}
|
||||||
|
tr do
|
||||||
|
th "Name"
|
||||||
|
th "Distance (km)"
|
||||||
|
th "Travel time (min)"
|
||||||
|
end
|
||||||
|
@locations.each do |location|
|
||||||
|
tr do
|
||||||
|
td { a location.name, :href => R(LocationsN, location.id) }
|
||||||
|
td { location.distance }
|
||||||
|
td { location.travel_time }
|
||||||
|
td do
|
||||||
|
form :action => R(LocationsN, location.id), :method => :post do
|
||||||
|
input :type => :submit, :name => "delete", :value => "Delete"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
a "Add a new location", :href => R(LocationsNew)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Form for editing the properties of a location (Models::Location)
|
||||||
|
def location_form
|
||||||
|
form.float_left :action => R(*@target), :method => :post do
|
||||||
|
h2 "Location Information"
|
||||||
|
ol do
|
||||||
|
li { _form_input_with_label("Name", "name", :text) }
|
||||||
|
li { _form_input_with_label("Distance (km)", "distance", :text) }
|
||||||
|
li { _form_input_with_label("Travel time (min)", "travel_time", :text) }
|
||||||
|
end
|
||||||
|
input :type => "submit", :name => @button, :value => @button.capitalize
|
||||||
|
input :type => "submit", :name => "cancel", :value => "Cancel"
|
||||||
|
end
|
||||||
|
div.clear {}
|
||||||
|
end
|
||||||
|
|
||||||
# Partial view that generates a form label with the given _label_name_
|
# Partial view that generates a form label with the given _label_name_
|
||||||
# and a form input with the given _input_name_ and _type_, such that the
|
# and a form input with the given _input_name_ and _type_, such that the
|
||||||
# label is linked to the input.
|
# label is linked to the input.
|
||||||
|
|
Loading…
Reference in New Issue