Compare commits

...
10 Commits
Author SHA1 Message Date
ricky f0d6b08a3d 2026.9.0 2026-09-09 21:16:04 -04:00
ricky 9a9efcbaf9 Implement permission checks for adding and deleting line items in issue forms 2026-09-07 21:32:44 -04:00
ricky 0acf5ffca4 2026.4.2 2026-04-17 21:32:11 -04:00
ricky 5b1d98aa07 Show running total even if issue doesn't have line items but it's children do 2026-04-17 21:31:46 -04:00
ricky 3ca45a457f 2026.4.1 2026-04-17 21:14:09 -04:00
ricky c9d2a47a92 Added running total calculated using subtask totals from the entire issue tree 2026-04-17 21:13:33 -04:00
ricky b939d834e9 2026.4.0 2026-04-03 11:59:28 -04:00
ricky 9b9a5c3505 Preventing Implicit Deletions 2026-04-03 11:58:56 -04:00
ricky da49b996da removed redundant private 2026-03-22 18:31:24 -04:00
ricky 50a02cc497 2026.3.12 2026-03-22 14:26:22 -04:00
9 changed files with 105 additions and 26 deletions

No files matched your search

-2
View File
@@ -100,8 +100,6 @@ class ItemsController < ApplicationController
params.require(:item).permit(:name, :description, :sku, :unit_price, :active, :account_id, :type, :taxable)
end
private
def log(msg)
Rails.logger.info "[ItemsController] #{msg}"
end
@@ -45,11 +45,14 @@
</template>
<p>
<% if User.current.allowed_to?(:add_line_items, nil, global: true) %>
<button type="button"
class="icon icon-add"
data-nested-form-add>
<%= l(:button_add) %>
</button>
<% end %>
</p>
<% end %>
@@ -1,8 +1,12 @@
<% if @issue.line_items.any? %>
<% if @issue.line_items.any? || @issue.descendant_line_items_total > 0%>
<hr/>
<div>
<p><strong><%= t :label_line_items %></strong></p>
<% total = 0 %>
<% if @issue.line_items.any?%>
<table class="list line-items-table">
<thead>
<tr>
@@ -14,7 +18,6 @@
</thead>
<tbody>
<% total = 0 %>
<% @issue.line_items.each do |item| %>
<% line_total = item.quantity.to_f * item.unit_price.to_f %>
@@ -29,11 +32,26 @@
<% end %>
</tbody>
<% end %>
<tfoot>
<% if @issue.line_items.any?%>
<tr>
<td colspan="3" style="text-align:right;"><strong><%= t :label_total %></strong></td>
<td><strong><%= number_to_currency(total) %></strong></td>
<td>
<strong><%= number_to_currency(total) %></strong>
</td>
</tr>
<% end %>
<% if @issue.descendant_line_items_total > 0 %>
<tr>
<td colspan="3" style="text-align:right;"><strong><%= t :label_running_total %></strong></td>
<td>
<strong>(<%= number_to_currency(@issue.descendant_line_items_total + total) %>)</strong>
</td>
</tr>
<% end %>
</tfoot>
</table>
</div>
@@ -35,6 +35,7 @@
0.00
</td>
<% if User.current.allowed_to?(:delete_line_items, nil, global: true) %>
<% unless readonly %>
<td class="actions">
<button type="button"
@@ -44,4 +45,5 @@
</button>
</td>
<% end %>
<% end %>
</tr>
+1
View File
@@ -33,6 +33,7 @@ en:
label_no: "No"
label_qty: "Quantity"
label_remove: "Remove"
label_running_total: "Running Total"
label_sync_now_accounts: "Sync Accounts"
label_sync_now_items: "Sync Items"
label_type: "Type"
+6 -8
View File
@@ -14,7 +14,7 @@ Redmine::Plugin.register :redmine_qbo_lineitems do
name 'Redmine QBO Line Items plugin'
author 'Rick Barrette'
description 'A plugin for Redmine to extend the capabilitys of the Redmine QuickBooks Online plugin to attach billable line items to an isuue'
version '2026.3.11'
version '2026.9.0'
url 'https://github.com/rickbarrette/redmine_qbo_lineitems'
author_url 'https://barrettefabrication.com'
requires_redmine version_or_higher: '6.1.0'
@@ -29,14 +29,12 @@ Redmine::Plugin.register :redmine_qbo_lineitems do
# Add safe attributes for core models
Issue.safe_attributes :line_items_attributes
end
# Administration menu extension
Redmine::MenuManager.map :admin_menu do |menu|
menu.push :redmine_qbo_lineitems, { controller: 'items', action: 'index' },
icon: 'list',
caption: :label_items,
html: { class: 'icon icon-list' }
# Global Permissions
permission :view_line_items, { line_items: [:index, :show] }, global: true
permission :add_line_items, { line_items: [:new, :create] }, global: true
permission :edit_line_items, { line_items: [:edit, :update] }, global: true
permission :delete_line_items, { line_items: [:destroy] }, global: true
end
RedmineQboLineItems.setup
+8 -8
View File
@@ -24,18 +24,18 @@ module LineItems
])
end
# Add the line items form to the issue edit page
def view_issues_edit_notes_bottom(context = {})
return if context[:issue].closed?
context[:controller].send(:render_to_string, {
partial: 'line_items/issue_form',
locals: {
f: context[:form]
}
}
)
return unless User.current.allowed_to?(:edit_line_items, nil, global: true)
context[:controller].send(:render_to_string, { partial: 'line_items/issue_form', locals: { f: context[:form] } })
end
render_on :view_issues_show_description_bottom , partial: 'line_items/issue_line_items'
# Add the line items to the issue view page
def view_issues_show_description_bottom (context = {})
return unless User.current.allowed_to?(:view_line_items, nil, global: true)
context[:controller].send(:render_to_string, { partial: 'line_items/issue_line_items', locals: { issue: context[:issue] } })
end
end
end
+56 -5
View File
@@ -10,14 +10,65 @@
module LineItems
module Patches
module IssuePatch
extend ActiveSupport::Concern
module IssuePatch extend ActiveSupport::Concern
prepended do
has_many :line_items, dependent: :destroy
accepts_nested_attributes_for :line_items,
allow_destroy: true,
reject_if: proc { |attrs| attrs['description'].blank? }
accepts_nested_attributes_for :line_items, allow_destroy: true, reject_if: proc { |attrs| attrs['description'].blank? }
# Returns line items for immediate children
def children_line_items
LineItem.where(issue_id: self.children.pluck(:id))
end
# Calculates the total value of all child line items
def children_line_items_total
children_line_items.sum(:line_total)
end
# Returns line items for the entire tree below this issue
def descendant_line_items
LineItem.where(issue_id: self.descendants.pluck(:id))
end
# Calculates the total value of entire tree below this issue
def descendant_line_items_total
descendant_line_items.sum(:line_total)
end
def line_items_total
line_items.sum(:line_total)
end
def line_items_attributes=(attrs)
attrs = attrs.stringify_keys
# IDs submitted in the form
submitted_ids = attrs.values.map { |a| a['id'] }.compact.map(&:to_s)
# Existing IDs in DB
existing_ids = line_items.pluck(:id).map(&:to_s)
# Find missing ones (these would be implicitly deleted by Rails)
missing_ids = existing_ids - submitted_ids
# Re-add missing records so Rails doesn't delete them
missing_ids.each do |id|
attrs["preserve_#{id}"] = { 'id' => id }
end
# Only allow explicit deletes or valid updates/creates
filtered = attrs.select do |_, item_attrs|
item_attrs['_destroy'] == '1' ||
item_attrs['id'].present? ||
item_attrs['description'].present?
end
super(filtered)
rescue => e
logger.error "Error processing line items attributes: #{e.message}"
end
end
end
end
+8
View File
@@ -12,10 +12,18 @@ module RedmineQboLineItems
def self.setup
unless Issue.ancestors.include?(LineItems::Patches::IssuePatch)
# Patches
Issue.prepend LineItems::Patches::IssuePatch
# Hooks
LineItems::Hooks::IssuesSaveHookListener
LineItems::Hooks::QboHookListener
LineItems::Hooks::ViewHookListener
# Administration menu extension
Redmine::MenuManager.map :admin_menu do |menu|
menu.push :redmine_qbo_lineitems, { controller: 'items', action: 'index' }, icon: 'list', caption: :label_items, html: { class: 'icon icon-list' }
end
end
end
end