Neuer CRUD-Controller nur für Preisgruppen.
Die Reihenfolge der Preisgruppen kann nun eingestellt werden, und man
kann Preisgruppen auf ungültig setzen, sofern sie nicht mehr aktiv bei
Kunden in Verwendung sind, so daß sie bei Kunden oder neuen Belegen
nicht mehr ausgewählt werden können.
sub init_all_pricegroups {
my ($self) = @_;
- return SL::DB::Manager::Pricegroup->get_all(sort => 'id');
+ return SL::DB::Manager::Pricegroup->get_all_sorted;
}
sub init_settings {
$self->{all_delivery_terms} = SL::DB::Manager::DeliveryTerm->get_all();
- $self->{all_pricegroups} = SL::DB::Manager::Pricegroup->get_all();
+ $self->{all_pricegroups} = SL::DB::Manager::Pricegroup->get_all_sorted(query => [ or => [ id => $self->{cv}->pricegroup_id, obsolete => 0 ] ]);
$query =
'SELECT DISTINCT(cp_abteilung) AS department
}
sub init_pricegroups {
- SL::DB::Manager::Pricegroup->get_all;
+ SL::DB::Manager::Pricegroup->get_all_sorted;
}
sub init_partsgroups {
--- /dev/null
+package SL::Controller::Pricegroup;
+
+use strict;
+
+use parent qw(SL::Controller::Base);
+
+use SL::Helper::Flash;
+use SL::Locale::String;
+use SL::DB::Default;
+use SL::DB::Manager::Pricegroup;
+
+use Rose::Object::MakeMethods::Generic (
+ scalar => [ qw(pricegroup) ],
+ 'scalar --get_set_init' => [ qw(all_pricegroups) ],
+);
+
+__PACKAGE__->run_before('check_auth');
+__PACKAGE__->run_before('load_pricegroup', only => [ qw(edit update delete) ]);
+
+#
+# actions
+#
+
+sub action_list {
+ my ($self) = @_;
+
+ $self->render('pricegroup/list',
+ title => t8('Pricegroups'),
+ );
+}
+
+sub action_new {
+ my ($self) = @_;
+
+ $self->pricegroup( SL::DB::Pricegroup->new );
+ $self->render('pricegroup/form',
+ title => t8('Add pricegroup'),
+ );
+}
+
+sub action_edit {
+ my ($self) = @_;
+
+ $self->render('pricegroup/form',
+ title => t8('Edit pricegroup'),
+ );
+}
+
+sub action_create {
+ my ($self) = @_;
+
+ $self->pricegroup( SL::DB::Pricegroup->new );
+ $self->create_or_update;
+}
+
+sub action_update {
+ my ($self) = @_;
+ $self->create_or_update;
+}
+
+sub action_delete {
+ my ($self) = @_;
+
+ if ( !$self->pricegroup->orphaned ) {
+ flash_later('error', $::locale->text('The pricegroup has been used and cannot be deleted.'));
+ } elsif ( eval { $self->pricegroup->delete; 1; } ) {
+ flash_later('info', $::locale->text('The pricegroup has been deleted.'));
+ } else {
+ flash_later('error', $::locale->text('The pricegroup has been used and cannot be deleted.'));
+ };
+ $self->redirect_to(action => 'list');
+}
+
+sub action_reorder {
+ my ($self) = @_;
+
+ SL::DB::Pricegroup->reorder_list(@{ $::form->{pricegroup_id} || [] });
+ $self->render(\'', { type => 'json' });
+}
+
+#
+# filters
+#
+
+sub check_auth {
+ $::auth->assert('config');
+}
+
+sub load_pricegroup {
+ my ($self) = @_;
+
+ $self->pricegroup( SL::DB::Pricegroup->new(id => $::form->{id})->load );
+}
+
+sub init_all_pricegroups { SL::DB::Manager::Pricegroup->get_all_sorted }
+
+#
+# helpers
+#
+
+sub create_or_update {
+ my ($self) = @_;
+ my $is_new = !$self->pricegroup->id;
+
+ my $params = delete($::form->{pricegroup}) || { };
+
+ $self->pricegroup->assign_attributes(%{ $params });
+
+ my @errors = $self->pricegroup->validate;
+
+ if (@errors) {
+ flash('error', @errors);
+ $self->render('pricegroup/form',
+ title => $is_new ? t8('Add pricegroup') : t8('Edit pricegroup'),
+ );
+ return;
+ }
+
+ $self->pricegroup->save;
+
+ flash_later('info', $is_new ? t8('The pricegroup has been created.') : t8('The pricegroup has been saved.'));
+ $self->redirect_to(action => 'list');
+}
+
+1;
+
+__END__
+
+=encoding utf-8
+
+=head1 NAME
+
+SL::Controller::Pricegroup - CRUD controller for pricegroups
+
+=head1 SYNOPSIS
+
+A new controller to create / edit / delete pricegroups.
+
+Pricegroups can only be deleted if they haven't been used anywhere.
+
+=head1 OBSOLETE PRICEGROUPS
+
+Pricegroups can't be obsoleted while any of the customers still use that
+pricegroup as their default pricegroup. Obsoleting a pricegroup means it can't
+be selected when editing customers and it can't be selected as a price source
+for new records.
+
+=head1 AUTHOR
+
+G. Richardson E<lt>grichardson@kivitendo-premium.deE<gt>
+
+=cut
__PACKAGE__->make_manager_methods;
sub _sort_spec {
- return ( default => [ 'pricegroup', 1 ],
- columns => { SIMPLE => 'ALL',
- map { ( $_ => "lower(pricegroup.${_})" ) } qw(pricegroup),
- });
+ return ( default => [ 'sortkey', 1 ],
+ columns => { SIMPLE => 'ALL' });
}
1;
__PACKAGE__->meta->columns(
id => { type => 'integer', not_null => 1, sequence => 'id' },
+ obsolete => { type => 'boolean', default => 'false' },
pricegroup => { type => 'text', not_null => 1 },
+ sortkey => { type => 'integer', not_null => 1 },
);
__PACKAGE__->meta->primary_key_columns([ 'id' ]);
use SL::DB::MetaSetup::Pricegroup;
use SL::DB::Manager::Pricegroup;
+use SL::DB::Helper::ActsAsList;
+use SL::DB::Customer;
__PACKAGE__->meta->initialize;
return join ' ', grep $_, $self->id, $self->pricegroup;
}
+sub validate {
+ my ($self) = @_;
+
+ my @errors;
+
+ if ( $self->obsolete && SL::DB::Manager::Customer->get_all_count(query => [ pricegroup_id => $self->id ]) ) {
+ push @errors, $::locale->text('The pricegroup is being used by customers.');
+ }
+
+ return @errors;
+}
+
+sub orphaned {
+ my ($self) = @_;
+ die 'not an accessor' if @_ > 1;
+
+ return 1 unless $self->id;
+
+ my @relations = qw(
+ SL::DB::Customer
+ SL::DB::Price
+ );
+
+ # check if pricegroup is the default pricegroup for any customers and has any
+ # prices assigned.
+
+ for my $class (@relations) {
+ eval "require $class";
+ return 0 if $class->_get_manager_class->get_all_count(query => [ pricegroup_id => $self->id ]);
+ }
+
+ # check if pricegroup was used in any pricesource
+ my @item_relations = qw(
+ SL::DB::OrderItem
+ SL::DB::DeliveryOrderItem
+ SL::DB::InvoiceItem
+ );
+
+ for my $class (@item_relations) {
+ eval "require $class";
+ return 0 if $class->_get_manager_class->get_all_count(query => [ active_price_source => 'pricegroup/' . $self->id ]);
+ }
+
+ return 1;
+}
1;
my $item = $self->record_item;
+ my $query = [ parts_id => $item->parts_id, price => { gt => 0 } ];
+
+ # add a pricegroup_filter for obsolete pricegroups, unless part of an
+ # existing pricegroup where that pricegroup was actually used.
+ if ( $self->record->id and $item->active_price_source =~ m/^pricegroup/ ) {
+ my ($pricegroup_id) = $item->active_price_source =~ m/^pricegroup\/(\d+)$/;
+ push(@{$query}, or => [ 'pricegroup.obsolete' => 0, 'pricegroup_id' => $pricegroup_id ]);
+ } else {
+ push(@{$query}, 'pricegroup.obsolete' => 0);
+ }
+
my $prices = SL::DB::Manager::Price->get_all(
- query => [ parts_id => $item->parts_id, price => { gt => 0 } ],
+ query => $query,
with_objects => 'pricegroup',
- sort_by => 'pricegroup.id',
+ sort_by => 'pricegroup.sortkey',
);
return () unless @$prices;
insertdate shop
);
- my $pricegroups = SL::DB::Manager::Pricegroup->get_all(sort => 'id');
+ my $pricegroups = SL::DB::Manager::Pricegroup->get_all_sorted;
my @pricegroup_columns;
my %column_defs_pricegroups;
if ($form->{l_pricegroups}) {
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#======================================================================
#
-# partsgroup, pricegroup administration
+# partsgroup administration
#
#======================================================================
if ($::form->{type} eq 'partsgroup') {
PE->get_partsgroup(\%::myconfig, $::form);
}
- if ($::form->{type} eq 'pricegroup') {
- PE->get_pricegroup(\%::myconfig, $::form);
- }
call_sub("form_$::form->{type}");
$::lxdebug->leave_sub;
$::form->redirect($::locale->text('Group saved!'));
}
- # choice pricegroup and save
- if ($::form->{type} eq 'pricegroup') {
- $::form->isblank("pricegroup", $::locale->text('Pricegroup missing!'));
- PE->save_pricegroup(\%::myconfig, $::form);
- $::form->redirect($::locale->text('Pricegroup saved!'));
- }
# saving the history
if(!exists $::form->{addition} && $::form->{id} ne "") {
$::form->{snumbers} = qq|projectnumber_| . $::form->{projectnumber};
if ($::form->{type} eq 'partsgroup') {
$::form->redirect($::locale->text('Group deleted!'));
}
- if ($::form->{type} eq 'pricegroup') {
- $::form->redirect($::locale->text('Pricegroup deleted!'));
- }
- # saving the history
- if(!exists $::form->{addition}) {
- $::form->{snumbers} = qq|projectnumber_| . $::form->{projectnumber};
- $::form->{addition} = "DELETED";
- $::form->save_history;
- }
- # /saving the history
$::lxdebug->leave_sub;
}
$::lxdebug->leave_sub;
}
-
-sub pricegroup_report {
- $::lxdebug->enter_sub;
- $::auth->assert('config');
-
- $::form->{$_} = $::form->unescape($::form->{$_}) for qw(pricegroup);
- PE->pricegroups(\%::myconfig, $::form);
-
- my $callback = build_std_url('action=pricegroup_report', qw(type status));
-
- my $option = '';
- $option .= $::locale->text('All') if $::form->{status} eq 'all';
- $option .= $::locale->text('Orphaned') if $::form->{status} eq 'orphaned';
-
- if ($::form->{pricegroup}) {
- $callback .= "&pricegroup=$::form->{pricegroup}";
- $option .= ", " . $::locale->text('Pricegroup') . " : $::form->{pricegroup}";
- }
-
- # escape callback
- $::form->{callback} = $callback;
-
- $::form->header;
- print $::form->parse_html_template('pe/pricegroup_report', {
- option => $option,
- callback => $callback,
- editlink => build_std_url('action=edit', qw(type status callback)),
- });
-
- $::lxdebug->leave_sub;
-}
-
-sub form_pricegroup {
- $::lxdebug->enter_sub;
- $::auth->assert('config');
-
- # $locale->text('Add Pricegroup')
- # $locale->text('Edit Pricegroup')
- $::form->{title} = $::locale->text("$::form->{title} Pricegroup");
-
- $::form->header;
- print $::form->parse_html_template('pe/pricegroup_form');
-
- $::lxdebug->leave_sub;
-}
- Briefe sind jetzt auch für Lieferanten verfügbar. Die neuen Rechte dafür
sind für Gruppen vergeben, die auch Einkaufsbelege bearbeiten dürfen.
+ - Neuer Controller für Preisgruppen, die nun sortiert und ungültig gesetzt
+ werden können.
+
Administrative Änderungen
- Diverse Textsuchen werden jetzt durch eine neue Klasse Indizes
'Add Letter' => 'Brief hinzufügen',
'Add Part' => 'Ware erfassen',
'Add Price Factor' => 'Preisfaktor erfassen',
- 'Add Pricegroup' => 'Preisgruppe erfassen',
'Add Printer' => 'Drucker hinzufügen',
'Add Project' => 'Projekt erfassen',
'Add Purchase Delivery Order' => 'Lieferschein (Einkauf) erfassen',
'Add part' => 'Artikel hinzufügen',
'Add picture' => 'Bild hinzufügen',
'Add picture to text block' => 'Bild dem Textblock hinzufügen',
+ 'Add pricegroup' => 'Preisgruppe hinzufügen',
'Add section' => 'Abschnitt hinzufügen',
'Add sub function block' => 'Unterfunktionsblock hinzufügen',
'Add taxzone' => 'Steuerzone hinzufügen',
'Edit Part' => 'Ware bearbeiten',
'Edit Preferences for #1' => 'Einstellungen von #1 bearbeiten',
'Edit Price Factor' => 'Preisfaktor bearbeiten',
- 'Edit Pricegroup' => 'Preisgruppe bearbeiten',
'Edit Printer' => 'Drucker bearbeiten',
'Edit Purchase Delivery Order' => 'Lieferschein (Einkauf) bearbeiten',
'Edit Purchase Order' => 'Lieferantenauftrag bearbeiten',
'Edit picture' => 'Bild bearbeiten',
'Edit predefined text' => 'Vordefinierten Textblock bearbeiten',
'Edit price rule' => 'Preisregel bearbeiten',
+ 'Edit pricegroup' => 'Preisgruppe bearbeiten',
'Edit prices and discount (if not used, textfield is ONLY set readonly)' => 'Preise und Rabatt in Formularen frei anpassen (falls deaktiviert, wird allerdings NUR das textfield auf READONLY gesetzt / kann je nach Browserversion und technischen Fähigkeiten des Anwenders noch umgangen werden)',
'Edit project' => 'Projekt bearbeiten',
'Edit project #1' => 'Projekt #1 bearbeiten',
'Price sources deactivated in this client' => 'Preisquellen die in diesem Mandanten deaktiviert sind',
'Price type explanation' => 'Preistyp Erklärung',
'Pricegroup' => 'Preisgruppe',
- 'Pricegroup deleted!' => 'Preisgruppe gelöscht!',
- 'Pricegroup missing!' => 'Preisgruppe fehlt!',
- 'Pricegroup saved!' => 'Preisgruppe gespeichert!',
'Pricegroups' => 'Preisgruppen',
'Prices' => 'Preise',
'Print' => 'Drucken',
'The price rule has been saved.' => 'Die Preisregel wurde gespeichert.',
'The price rule is not a rule for discounts' => 'Die Preisregel ist keine Regel für Rabatte',
'The price rule is not a rule for prices' => 'Die Preisregel ist keine Regel für Preise',
+ 'The pricegroup has been created.' => 'Die Preisgruppe wurde erstellt.',
+ 'The pricegroup has been deleted.' => 'Die Preisgruppe wurde gelöscht.',
+ 'The pricegroup has been saved.' => 'Die Preisgruppe wurde gespeichert.',
+ 'The pricegroup has been used and cannot be deleted.' => 'Die Preisgruppe wurde bereits verwendet und kann nicht gelöscht werden.',
+ 'The pricegroup is being used by customers.' => 'Die Preisgruppe wird von Kunden verwendet.',
'The printer could not be deleted.' => 'Der Drucker konnte nicht gelöscht werden.',
'The printer has been created.' => 'Der Drucker wurde angelegt.',
'The printer has been deleted.' => 'Der Drucker wurde entfernt.',
id: system_pricegroups
name: Pricegroups
order: 1000
- module: pe.pl
params:
- action: search
- type: pricegroup
+ action: Pricegroup/list
- parent: system
id: system_edit_units
name: Edit units
--- /dev/null
+-- @tag: pricegroup_sortkey_obsolete
+-- @description: Sortierreihenfolge und ungültig für Preisgruppen
+-- @charset: UTF-8
+-- @depends: release_3_4_1
+-- @ignore: 0
+
+ALTER TABLE pricegroup ADD COLUMN obsolete BOOLEAN DEFAULT FALSE;
+ALTER TABLE pricegroup ADD COLUMN sortkey INTEGER;
+
+CREATE SEQUENCE tmp_counter;
+UPDATE pricegroup SET sortkey = nextval('tmp_counter');
+DROP SEQUENCE tmp_counter;
+ALTER TABLE pricegroup ALTER COLUMN sortkey SET NOT NULL;
+++ /dev/null
-[%- USE L %]
-[%- USE T8 %]
-[%- USE HTML %]
-<h1>[% title %]</h1>
-[% L.javascript_tag('show_history.js') %]
-
-<form method=post action="[% script %]">
-
-<input type=hidden name=id value="[% id %]">
-<input type=hidden name=type value="[% type %]">
-
-<table width=100%>
- <tr>
- <td>
- <table width=100%>
- <tr>
- <th align=right>[% 'Pricegroup' | $T8 %]</th>
- <td><input name=pricegroup size=30 value="[% pricegroup | html %]"></td>
- </tr>
- </table>
- </td>
- </tr>
- <tr>
- <td colspan=2><hr size=3 noshade></td>
- </tr>
-</table>
-
-<br>
-
-<input name=callback type=hidden value="[% callback | html %]">
-<input type=submit class=submit name=action value="[% 'Save' | $T8 %]">
-[%- IF id && orphaned %]
-<input type=submit class=submit name=action value="[% 'Delete' | $T8 %]">
-[%- END %]
-
-[%- IF ( id ) %]
- <input type=button onclick="set_history_window([% id %], 'id');" name=history id=history value="[% 'history' | $T8 %]">
-[%- END %]
-
-</form>
-
+++ /dev/null
-[%- USE HTML %]
-[%- USE T8 %]
-<h1>[% 'Pricegroup' | $T8 %]</h1>
-
-<table width=100%>
- <tr>
- <td>[% option %]</td>
- </tr>
- <tr>
- <td>
- <table width=100%>
- <tr class=listheading>
- <th class=listheading width=90%>[% 'Pricegroup' | $T8 %]</th>
- </tr>
-[%- FOREACH row = item_list %]
- <tr valign=top class="listrow[% loop.count % 2 %]">
- <td><a href="[% editlink %]&id=[% row.id %]">[% row.pricegroup %]</a></td>
- </tr>
-[%- END %]
- </table>
- </td>
- </tr>
- <tr>
- <td><hr size=3 noshade></td>
- </tr>
-</table>
-
-<br>
-<form method=post action="[% script %]">
- <input name=callback type=hidden value="[% callback | html %]">
- <input type=hidden name=type value="[% type %]">
- <input class=submit type=submit name=action value="[% 'Add' | $T8 %]">
-</form>
-
-
--- /dev/null
+[%- USE HTML -%][%- USE LxERP -%][%- USE L -%][%- USE T8 -%]
+
+[% SET style="width: 400px" %]
+[% SET size=15 %]
+
+<h1>[% HTML.escape(title) %]</h1>
+
+<form action="controller.pl" method="post">
+
+[%- INCLUDE 'common/flash.html' %]
+
+[%- L.hidden_tag("id", SELF.pricegroup.id) %]
+
+<table>
+ <tr>
+ <th align="right">[% 'Description' | $T8 %]</th>
+ <td>
+ [%- L.input_tag("pricegroup.pricegroup", SELF.pricegroup.pricegroup) %]
+ </td>
+ [% IF SELF.pricegroup.id %]
+ <tr>
+ <th align="right">[% 'Obsolete' | $T8 %]</th>
+ <td>[% L.checkbox_tag('pricegroup.obsolete', checked = SELF.pricegroup.obsolete, for_submit=1) %]</td>
+ </tr>
+ </tr>
+ [% END %]
+</table>
+
+ <p>
+ [% L.hidden_tag("action", "Pricegroup/dispatch") %]
+ [% L.submit_tag("action_" _ (SELF.pricegroup.id ? "update" : "create"), LxERP.t8('Save'), onclick="return check_prerequisites();") %]
+ [%- IF SELF.pricegroup.id AND SELF.pricegroup.orphaned -%]
+ [% L.submit_tag("action_delete", LxERP.t8('Delete')) %]
+ [%- END %]
+ <a href="[% SELF.url_for(action='list') %]">[%- LxERP.t8("Cancel") %]</a>
+ </p>
+
+ <hr>
+
+<script type="text/javascript">
+<!--
+function check_prerequisites() {
+ if ($('#pricegroup_pricegroup').val() === "") {
+ alert(kivi.t8('The description is missing.'));
+ return false;
+ }
+ return true;
+}
+-->
+</script>
--- /dev/null
+[%- USE HTML -%][%- USE LxERP -%][%- USE L -%][%- USE T8 -%][%- INCLUDE 'common/flash.html' %]
+
+<h1>[% title %]</h1>
+
+<p>
+ <table width="100%" id="pricegroup_list">
+ <thead>
+ <tr class="listheading">
+ <th align="center" width="1%"><img src="image/updown.png" alt="[ LxERP.t8('reorder item') %]"></th>
+ <th>[% 'Description' | $T8 %]</th>
+ <th>[% 'Obsolete' | $T8 %]</th>
+ </tr>
+ </thead>
+
+ <tbody>
+ [%- FOREACH pricegroup = SELF.all_pricegroups %]
+ <tr class="listrow" id="pricegroup_id_[% pricegroup.id %]">
+ <td align="center" class="dragdrop"><img src="image/updown.png" alt="[ LxERP.t8('reorder item') %]"></td>
+ <td><a href="[% SELF.url_for(action='edit', id=pricegroup.id) %]">[% HTML.escape(pricegroup.pricegroup) %]</a></td>
+ <td>[% HTML.escape(pricegroup.obsolete) %]</a></td>
+ </tr>
+ [%- END %]
+ </tbody>
+ </table>
+</p>
+
+<hr height="3">
+
+[% L.sortable_element('#pricegroup_list tbody', url=SELF.url_for(action='reorder'), with='pricegroup_id') %]
+
+<p>
+ <a href="[% SELF.url_for(action='new') %]">[%- 'Add' | $T8 %]</a>
+</p>