From 7a1d18269af19c892ddb12072c1157dc24c6324c Mon Sep 17 00:00:00 2001 From: Paul van Tilburg Date: Fri, 19 Jun 2015 21:39:34 +0200 Subject: [PATCH 1/5] Turn the time entries view into a partial view; create timeline view Adapt the task form view to use the time entries partial view as well. --- stoptime.rb | 234 ++++++++++++++++++++++++++++------------------------ 1 file changed, 124 insertions(+), 110 deletions(-) diff --git a/stoptime.rb b/stoptime.rb index ce9c0d0..930d917 100644 --- a/stoptime.rb +++ b/stoptime.rb @@ -1476,10 +1476,10 @@ module StopTime::Controllers # also for quickly registering time. # # path:: +/timeline+ - # view:: {Views#time_entries} + # view:: {Views#timeline} class Timeline # Retrieves all registered time in descending order to present - # the timeline using {Views#time_entries}. + # the timeline using {Views#timeline}. def get if @input["show"] == "all" @time_entries = TimeEntry.order("start DESC") @@ -1498,7 +1498,7 @@ module StopTime::Controllers end @input["bill"] = true # Bill by default. @input["task"] = @time_entries.first.task.id if @time_entries.present? - render :time_entries + render :timeline end # Registers a time entry and redirects back to the referer. @@ -1858,118 +1858,26 @@ module StopTime::Views end # The main overview showing the timeline of registered time. - # If a task ID is given as an argument, the task column will be hidden - # and it will be assumed it is used as a partial view. # - # FIXME: This should be done in a nicer way. - # - # @param [Fixnum, nil] task_id ID of a task # @return [Mab::Mixin::Tag] the main timeline (time entry list) overview - def time_entries(task_id=nil) - if task_id.present? - h2 "Registered #{@task.billed? ? "billed" : "unbilled"} time" - else - header.page_header do - h1 do - text! "Timeline" - small "#{@time_entries.count} time entries" - div.btn_group.pull_right do - a.btn.btn_small.dropdown_toggle :href => "#", "data-toggle" => "dropdown" do - text! @input["show"] == "all" ? "All" : "Unbilled" - span.caret - end - ul.dropdown_menu :role => "menu", :aria_labelledby => "dLabel" do - li { a "All", :href => R(Timeline, :show => "all") } - li { a "Unbilled", :href => R(Timeline, :show => "unbilled") } - end - end - end - end - end - table.table.table_condensed.table_striped.table_hover do - unless task_id.present? - col.customer_short - col.task - end - col.date - col.start_time - col.end_time - col.comment - col.hours - col.flag - thead do - tr do - unless task_id.present? - th "Customer" - th "Project/Task" - end - th "Date" - th "Start" - th "End" - th "Comment" - th "Total" - th "Bill?" - th {} - end - end - tbody do - form.form_inline :action => R(Timeline), :method => :post do - tr do - if task_id.present? - input :type => :hidden, :name => "task", :value => task_id - else - td { } - td { _form_select_nested("task", @task_list, :class => "task") } - end - td { input.date :type => :text, :name => "date", - :value => DateTime.now.to_date.to_formatted_s } - td { input.start_time :type => :text, :name => "start", - :value => DateTime.now.to_time.to_formatted_s(:time_only) } - td { input.end_time :type => :text, :name => "end" } - td { input.comment :type => :text, :name => "comment" } - td { "N/A" } - td { _form_input_checkbox("bill") } - td do - button.btn.btn_small.btn_primary "Enter", :type => :submit, :name => "enter", :value => "Enter" - end - end - end - @time_entries.each do |entry| - tr(:class => entry.task.billed? ? "billed" : nil) do - unless task_id.present? - td do - a entry.customer.shortest_name, - :title => entry.customer.shortest_name, - :href => R(CustomersN, entry.customer.id) - end - td do - a entry.task.name, - :title => entry.task.name, - :href => R(CustomersNTasksN, entry.customer.id, entry.task.id) - end - end - td { entry.date.to_date } - td { entry.start.to_formatted_s(:time_only) } - td { entry.end.to_formatted_s(:time_only)} - if entry.comment.present? - td { a entry.comment, :href => R(TimelineN, entry.id), - :title => entry.comment } - else - td { a(:href => R(TimelineN, entry.id)) { i "None" } } - end - td { "%.2fh" % entry.hours_total } - td do - i(:class => "icon-ok") if entry.bill? - end - td do - form.form_inline :action => R(TimelineN, entry.id), :method => :post do - button.btn.btn_mini.btn_danger "Delete", :type => :submit, :name => "delete", :value => "Delete" - end - end + def timeline + header.page_header do + h1 do + text! "Timeline" + small "#{@time_entries.count} time entries" + div.btn_group.pull_right do + a.btn.btn_small.dropdown_toggle :href => "#", "data-toggle" => "dropdown" do + text! @input["show"] == "all" ? "All" : "Unbilled" + span.caret + end + ul.dropdown_menu :role => "menu", :aria_labelledby => "dLabel" do + li { a "All", :href => R(Timeline, :show => "all") } + li { a "Unbilled", :href => R(Timeline, :show => "unbilled") } end end end end + _time_entries end # Form for editing a time entry ({Models::TimeEntry}). @@ -2299,7 +2207,8 @@ module StopTime::Views end end # Show registered time (ab)using the time_entries view as partial view. - time_entries(@task.id) unless @method == "create" + h2 "Registered #{@task.billed? ? "billed" : "unbilled"} time" + _time_entries(@customer, @task) unless @method == "create" end # The main overview of the existing invoices. @@ -2918,4 +2827,109 @@ module StopTime::Views end end + # Partial view that shows time entries. If a customer is given, + # only time entries of that customer are shown, and if also a task is + # given, then only the time entries of that task are shown. + # + # @param [Customer, nil] customer an customer to show time entries for + # @param [Customer, nil] task a task to show time entries for + # @return [Mab::Mixin::Tag] the main menu + def _time_entries(customer=nil, task=nil) + table.table.table_condensed.table_striped.table_hover do + if customer.blank? + col.customer_short + end + if task.blank? + col.task + end + col.date + col.start_time + col.end_time + col.comment + col.hours + col.flag + thead do + tr do + if customer.blank? + th "Customer" + end + if task.blank? + th "Project/Task" + end + th "Date" + th "Start" + th "End" + th "Comment" + th "Total" + th "Bill?" + th {} + end + end + tbody do + form.form_inline :action => R(Timeline), :method => :post do + tr do + if task.present? + input :type => :hidden, :name => "task", :value => task.id + else + if customer.blank? + td { } + end + td { _form_select_nested("task", @task_list, :class => "task") } + end + td { input.date :type => :text, :name => "date", + :value => DateTime.now.to_date.to_formatted_s } + td { input.start_time :type => :text, :name => "start", + :value => DateTime.now.to_time.to_formatted_s(:time_only) } + td { input.end_time :type => :text, :name => "end" } + td { input.comment :type => :text, :name => "comment" } + td { "N/A" } + td { _form_input_checkbox("bill") } + td do + button.btn.btn_small.btn_primary "Enter", + :type => :submit, + :name => "enter", + :value => "Enter" + end + end + end + @time_entries.each do |entry| + tr(:class => entry.task.billed? ? "billed" : nil) do + if customer.blank? + td do + a entry.customer.shortest_name, + :title => entry.customer.shortest_name, + :href => R(CustomersN, entry.customer.id) + end + end + if task.blank? + td do + a entry.task.name, + :title => entry.task.name, + :href => R(CustomersNTasksN, entry.customer.id, entry.task.id) + end + end + td { entry.date.to_date } + td { entry.start.to_formatted_s(:time_only) } + td { entry.end.to_formatted_s(:time_only)} + if entry.comment.present? + td { a entry.comment, :href => R(TimelineN, entry.id), + :title => entry.comment } + else + td { a(:href => R(TimelineN, entry.id)) { i "None" } } + end + td { "%.2fh" % entry.hours_total } + td do + i(:class => "icon-ok") if entry.bill? + end + td do + form.form_inline :action => R(TimelineN, entry.id), :method => :post do + button.btn.btn_mini.btn_danger "Delete", :type => :submit, :name => "delete", :value => "Delete" + end + end + end + end + end + end + end + end # module StopTime::Views From 55e18615e4e9341aadfd2e9e785efb4b31e21581 Mon Sep 17 00:00:00 2001 From: Paul van Tilburg Date: Fri, 19 Jun 2015 21:41:22 +0200 Subject: [PATCH 2/5] Hook up a customer-specific time entries list in the customer form view --- stoptime.rb | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/stoptime.rb b/stoptime.rb index 930d917..41644eb 100644 --- a/stoptime.rb +++ b/stoptime.rb @@ -1014,10 +1014,17 @@ module StopTime::Controllers end end + @time_entries = @customer.time_entries.order("start DESC") @invoices = @customer.invoices @invoices.each do |i| @input["paid_#{i.number}"] = true if i.paid? end + @task_list = Hash.new { |h, k| h[k] = Array.new } + @customer.tasks.reject { |t| t.billed? }.each do |t| + @task_list[t.customer.shortest_name] << [t.id, t.name] + end + @input["bill"] = true # Bill by default. + @input["task"] = @time_entries.first.task.id if @time_entries.present? @target = [CustomersN, @customer.id] @button = "update" @@ -2146,6 +2153,14 @@ module StopTime::Views end end end + + div.row do + div.span12 do + # Show registered time (ab)using the time_entries view as partial view. + h2 "Registered time" + _time_entries(@customer) unless @method == "create" + end + end end # Form for updating the properties of a task ({Models::Task}). From a744a4f0d1b817361c91b069f72982faa9e484ef Mon Sep 17 00:00:00 2001 From: Paul van Tilburg Date: Fri, 19 Jun 2015 22:02:13 +0200 Subject: [PATCH 3/5] Add a grand total of unbilled hours and amount to the overview view --- stoptime.rb | 17 +++++++++++++++++ templates/sass/style.sass | 5 +++++ 2 files changed, 22 insertions(+) diff --git a/stoptime.rb b/stoptime.rb index 41644eb..817b911 100644 --- a/stoptime.rb +++ b/stoptime.rb @@ -898,6 +898,7 @@ module StopTime::Controllers @task_count = 0 @active_tasks = {} @active_tasks_summary = {} + @totals = [0.0, 0,0] Customer.all.each do |customer| tasks = customer.unbilled_tasks @tasks[customer] = tasks @@ -908,7 +909,9 @@ module StopTime::Controllers active_tasks.inject([0.0, 0.0]) do |summ, task| task_summ = task.summary summ[0] += task_summ[0] + @totals[0] += task_summ[0] summ[1] += task_summ[2] + @totals[1] += task_summ[2] summ end end @@ -1861,6 +1864,20 @@ module StopTime::Views end end end + div.row do + div.span12 do + table.table.table_condensed.grand_total do + col + col.total_hours + col.total_amount + tr do + td { big { b "Grand total" } } + td.text_right { big { b("%.2fh" % @totals[0]) } } + td.text_right { big { b("€ %.2f" % @totals[1]) } } + end + end + end + end end end diff --git a/templates/sass/style.sass b/templates/sass/style.sass index a6752eb..6c7c4d8 100644 --- a/templates/sass/style.sass +++ b/templates/sass/style.sass @@ -48,6 +48,8 @@ table col.amount, col.hours, col.hourly_rate width: 85px + col.total_hours, col.total_amount + width: 130px col.reg_hours width: 90px col.flag @@ -93,6 +95,9 @@ table td.indent padding-left: 20px +table.grand-total + margin-top: 30px + /* Form layout */ .form-condensed padding-top: 0px From a97a305f6cc89c4c06a01ed0ed7259b9997f8c13 Mon Sep 17 00:00:00 2001 From: Paul van Tilburg Date: Fri, 19 Jun 2015 22:08:08 +0200 Subject: [PATCH 4/5] Bump version to 1.14.0 --- stoptime.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stoptime.rb b/stoptime.rb index 817b911..c38b0be 100644 --- a/stoptime.rb +++ b/stoptime.rb @@ -54,7 +54,7 @@ end module StopTime # The version of the application - VERSION = '1.12.1' + VERSION = '1.14.0' puts "Starting Stop… Camping Time! version #{VERSION}" # @return [Hash{String=>Object}] The parsed configuration. From 0d75f1aaae2855f1b0d65d412adcd9987f8a6e2a Mon Sep 17 00:00:00 2001 From: Paul van Tilburg Date: Fri, 19 Jun 2015 22:11:06 +0200 Subject: [PATCH 5/5] Update the changelog for the 1.14.0 release --- CHANGELOG.rdoc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.rdoc b/CHANGELOG.rdoc index e8a5597..de33843 100644 --- a/CHANGELOG.rdoc +++ b/CHANGELOG.rdoc @@ -1,5 +1,13 @@ = Stop… Camping Time! release news +== 1.14.0 + +Features: + +* Show customer specific time entries in the customer form [#08650a] + * Also allow for registering time for the customer +* Show a grand total of unbilled hours and amount in the overview + == 1.12.1 Small update release to include to missing 1.12 change log and added