From 88925dfa866c6c2b95df8382ff5887f711e683f5 Mon Sep 17 00:00:00 2001 From: erickson Date: Mon, 20 Oct 2008 19:42:01 +0000 Subject: [PATCH] Merging acq-experiment to trunk, since rel_1_4 has been branched. The majority of this is new ACQ-related code, with a few modifications of existing code. Truncated svnmerge commit message: Merged revisions 8236,8238-8244,8246-8249,8251-8252,8254-8257,8260,8266-8269,8271,8273,8276-8286,8288-8290,8303,8313,8317-8318,8320-8321,8323-8324,8326,8328-8330,8332-8335,8337-8340,8342,8345,8349-8350,8355-8360,8362-8378,8385-8386,8388,8390-8391,8393-8397,8399-8401,8404,8406-8413,8415-8422,8424-8425,8427-8430,8451,8453-8454,8456,8458,8467,8469-8470,8476,8478,8482-8487,8495,8497,8510,8534,8539-8541,8574,8576,8578-8579,8581,8593-8599,8601,8603,8605,8607-8608,8617,8620-8623,8629,8631,8633,8635,8637-8639,8646-8647,8649-8651,8657-8660,8662,8664,8668,8670-8671,8675-8676,8678,8681-8688,8692,8694-8695,8697-8698,8706,8712,8714-8720,8722,8724,8727-8730,8732-8736,8744-8748,8756-8758,8762-8764,8768-8770,8773-8785,8789-8793,8795-8797,8799-8800,8803-8807,8814-8816,8821-8822,8826-8828,8831-8834,8836,8838-8839,8841,8843,8864-8867,8876-8880,8882,8885,8929,8932,8934-8935,8937-8941,8945-8947,8957-8958,8965,8978-8984,8996-8998,9005-9006,9109,9136,9139,9153,9167,9188,9202,9205,9237-9240,9246,9249,9251,9262-9264,9267-9268,9273-9274,9283,9290,9315-9317,9324,9326,9331-9332,9337,9341,9344,9346,9348,9351,9355,9382-9384,9404-9411,9416-9423,9438,9446,9463,9480,9487-9488,9508-9510,9516-9519,9531,9537-9538,9550-9563,9572-9576,9578-9583,9585-9586,9588,9590-9592,9596-9598,9602-9605,9607-9611,9613,9617-9621,9623-9624,9628,9630,9633,9636-9640,9643-9648,9651,9656-9668,9673-9674,9676,9679-9683,9687-9692,9702,9709-9713,9722,9735,9810,9812,9818-9821,9894,9905,9907,9911,9913,9915-9917,9921-9924,9928-9930,9935,9937-9940,9944,9946,9997,10006-10007,10017-10018,10026,10034-10039,10043-10045,10053-10055,10057-10058,10062,10086,10089-10090,10094-10111,10113-10114,10116-10118,10120,10122,10146,10149,10160,10163,10172,10175-10176,10180-10186,10196-10198,10234-10235,10239,10309,10316-10318,10322,10327-10330,10342,10349-10356,10460-10462,10567,10703,10720,10826,10834-10836,10854-10855,10857,10867 via svnmerge from svn://svn.open-ils.org/ILS/branches/acq-experiment git-svn-id: svn://svn.open-ils.org/ILS/trunk@10879 dcc99617-32d9-48b4-a31d-7c20da2025e4 --- Open-ILS/examples/apache/eg_vhost.conf | 20 + Open-ILS/examples/fm_IDL.xml | 528 +++++++++- Open-ILS/examples/oils_web.xml | 35 + Open-ILS/src/extras/ils_events.xml | 102 +- .../src/perlmods/OpenILS/Application/Acq.pm | 10 + .../OpenILS/Application/Acq/Financials.pm | 855 +++++++++++++++++ .../OpenILS/Application/Acq/Lineitem.pm | 903 ++++++++++++++++++ .../OpenILS/Application/Acq/Picklist.pm | 461 +++++++++ .../OpenILS/Application/Acq/Provider.pm | 183 ++++ Open-ILS/src/perlmods/OpenILS/Const.pm | 3 + Open-ILS/src/perlmods/OpenILS/Utils/MFHD.pm | 58 ++ .../perlmods/OpenILS/Utils/MFHD/Caption.pm | 78 ++ .../perlmods/OpenILS/Utils/MFHD/Holding.pm | 90 ++ .../src/perlmods/OpenILS/Utils/MFHDParser.pm | 168 ++++ Open-ILS/src/perlmods/OpenILS/WWW/EGWeb.pm | 150 +++ Open-ILS/src/sql/Pg/200.schema.acq.sql | 440 +++++++++ Open-ILS/src/sql/Pg/210.schema.serials.sql | 64 ++ Open-ILS/src/sql/Pg/950.data.seed-values.sql | 29 +- Open-ILS/src/sql/Pg/build-db.sh | 3 + .../support-scripts/test-scripts/acq_fund.py | 41 + Open-ILS/web/css/skin/default.css | 62 ++ Open-ILS/web/css/skin/default/acq.css | 94 ++ Open-ILS/web/css/skin/default/admin.css | 3 + Open-ILS/web/css/theme/default.css | 58 ++ Open-ILS/web/css/theme/default/acq.css | 53 + Open-ILS/web/css/theme/default/admin.css | 1 + Open-ILS/web/images/eg_logo.jpg | Bin 0 -> 8571 bytes Open-ILS/web/images/eg_tiny_logo.jpg | Bin 0 -> 1722 bytes Open-ILS/web/js/dojo/openils/Util.js | 82 ++ .../web/js/dojo/openils/acq/CurrencyType.js | 55 ++ Open-ILS/web/js/dojo/openils/acq/Fund.js | 213 +++++ .../web/js/dojo/openils/acq/FundingSource.js | 140 +++ Open-ILS/web/js/dojo/openils/acq/Lineitem.js | 239 +++++ .../web/js/dojo/openils/acq/LineitemAttr.js | 55 ++ Open-ILS/web/js/dojo/openils/acq/PO.js | 76 ++ Open-ILS/web/js/dojo/openils/acq/Picklist.js | 166 ++++ Open-ILS/web/js/dojo/openils/acq/Provider.js | 174 ++++ Open-ILS/web/js/dojo/openils/editors.js | 83 ++ .../js/dojo/openils/widget/FundSelector.js | 18 + .../dojo/openils/widget/ProviderSelector.js | 20 + Open-ILS/web/js/ui/base.js | 34 + .../web/js/ui/default/acq/common/jubgrid.js | 382 ++++++++ .../acq/financial/list_currency_types.js | 25 + .../acq/financial/list_funding_sources.js | 35 + .../js/ui/default/acq/financial/list_funds.js | 63 ++ .../default/acq/financial/list_providers.js | 35 + .../js/ui/default/acq/financial/view_fund.js | 86 ++ .../acq/financial/view_funding_source.js | 100 ++ .../ui/default/acq/financial/view_provider.js | 99 ++ .../js/ui/default/acq/picklist/bib_search.js | 205 ++++ .../js/ui/default/acq/picklist/view_list.js | 81 ++ .../web/js/ui/default/acq/po/li_search.js | 189 ++++ Open-ILS/web/js/ui/default/acq/po/search.js | 81 ++ Open-ILS/web/js/ui/default/acq/po/view_po.js | 82 ++ .../js/ui/default/acq/receiving/process.js | 62 ++ .../web/js/ui/default/acq/settings/li_attr.js | 105 ++ Open-ILS/web/templates/base.tt2 | 41 + .../templates/default/acq/common/jubgrid.tt2 | 209 ++++ .../acq/financial/list_currency_types.tt2 | 58 ++ .../acq/financial/list_funding_sources.tt2 | 98 ++ .../default/acq/financial/list_funds.tt2 | 109 +++ .../default/acq/financial/list_providers.tt2 | 75 ++ .../default/acq/financial/view_fund.tt2 | 145 +++ .../acq/financial/view_funding_source.tt2 | 162 ++++ .../default/acq/financial/view_provider.tt2 | 117 +++ .../default/acq/picklist/bib_search.tt2 | 99 ++ .../templates/default/acq/picklist/list.tt2 | 66 ++ .../templates/default/acq/picklist/view.tt2 | 41 + .../templates/default/acq/po/li_search.tt2 | 109 +++ .../web/templates/default/acq/po/search.tt2 | 76 ++ .../web/templates/default/acq/po/view.tt2 | 40 + .../default/acq/receiving/process.tt2 | 38 + .../default/acq/settings/li_attr.tt2 | 73 ++ Open-ILS/web/templates/default/base.tt2 | 28 + Open-ILS/web/templates/default/footer.tt2 | 2 + Open-ILS/web/templates/default/header.tt2 | 11 + Open-ILS/web/templates/default/menu.tt2 | 83 ++ 77 files changed, 9153 insertions(+), 4 deletions(-) create mode 100644 Open-ILS/examples/oils_web.xml create mode 100644 Open-ILS/src/perlmods/OpenILS/Application/Acq.pm create mode 100644 Open-ILS/src/perlmods/OpenILS/Application/Acq/Financials.pm create mode 100644 Open-ILS/src/perlmods/OpenILS/Application/Acq/Lineitem.pm create mode 100644 Open-ILS/src/perlmods/OpenILS/Application/Acq/Picklist.pm create mode 100644 Open-ILS/src/perlmods/OpenILS/Application/Acq/Provider.pm create mode 100755 Open-ILS/src/perlmods/OpenILS/Utils/MFHD.pm create mode 100755 Open-ILS/src/perlmods/OpenILS/Utils/MFHD/Caption.pm create mode 100755 Open-ILS/src/perlmods/OpenILS/Utils/MFHD/Holding.pm create mode 100644 Open-ILS/src/perlmods/OpenILS/Utils/MFHDParser.pm create mode 100644 Open-ILS/src/perlmods/OpenILS/WWW/EGWeb.pm create mode 100644 Open-ILS/src/sql/Pg/200.schema.acq.sql create mode 100644 Open-ILS/src/sql/Pg/210.schema.serials.sql create mode 100644 Open-ILS/src/support-scripts/test-scripts/acq_fund.py create mode 100644 Open-ILS/web/css/skin/default.css create mode 100644 Open-ILS/web/css/skin/default/acq.css create mode 100644 Open-ILS/web/css/skin/default/admin.css create mode 100644 Open-ILS/web/css/theme/default.css create mode 100644 Open-ILS/web/css/theme/default/acq.css create mode 100644 Open-ILS/web/css/theme/default/admin.css create mode 100644 Open-ILS/web/images/eg_logo.jpg create mode 100644 Open-ILS/web/images/eg_tiny_logo.jpg create mode 100644 Open-ILS/web/js/dojo/openils/Util.js create mode 100644 Open-ILS/web/js/dojo/openils/acq/CurrencyType.js create mode 100644 Open-ILS/web/js/dojo/openils/acq/Fund.js create mode 100644 Open-ILS/web/js/dojo/openils/acq/FundingSource.js create mode 100644 Open-ILS/web/js/dojo/openils/acq/Lineitem.js create mode 100644 Open-ILS/web/js/dojo/openils/acq/LineitemAttr.js create mode 100644 Open-ILS/web/js/dojo/openils/acq/PO.js create mode 100644 Open-ILS/web/js/dojo/openils/acq/Picklist.js create mode 100644 Open-ILS/web/js/dojo/openils/acq/Provider.js create mode 100644 Open-ILS/web/js/dojo/openils/editors.js create mode 100644 Open-ILS/web/js/dojo/openils/widget/FundSelector.js create mode 100644 Open-ILS/web/js/dojo/openils/widget/ProviderSelector.js create mode 100644 Open-ILS/web/js/ui/base.js create mode 100644 Open-ILS/web/js/ui/default/acq/common/jubgrid.js create mode 100644 Open-ILS/web/js/ui/default/acq/financial/list_currency_types.js create mode 100644 Open-ILS/web/js/ui/default/acq/financial/list_funding_sources.js create mode 100644 Open-ILS/web/js/ui/default/acq/financial/list_funds.js create mode 100644 Open-ILS/web/js/ui/default/acq/financial/list_providers.js create mode 100644 Open-ILS/web/js/ui/default/acq/financial/view_fund.js create mode 100644 Open-ILS/web/js/ui/default/acq/financial/view_funding_source.js create mode 100644 Open-ILS/web/js/ui/default/acq/financial/view_provider.js create mode 100644 Open-ILS/web/js/ui/default/acq/picklist/bib_search.js create mode 100644 Open-ILS/web/js/ui/default/acq/picklist/view_list.js create mode 100644 Open-ILS/web/js/ui/default/acq/po/li_search.js create mode 100644 Open-ILS/web/js/ui/default/acq/po/search.js create mode 100644 Open-ILS/web/js/ui/default/acq/po/view_po.js create mode 100644 Open-ILS/web/js/ui/default/acq/receiving/process.js create mode 100644 Open-ILS/web/js/ui/default/acq/settings/li_attr.js create mode 100644 Open-ILS/web/templates/base.tt2 create mode 100644 Open-ILS/web/templates/default/acq/common/jubgrid.tt2 create mode 100644 Open-ILS/web/templates/default/acq/financial/list_currency_types.tt2 create mode 100644 Open-ILS/web/templates/default/acq/financial/list_funding_sources.tt2 create mode 100644 Open-ILS/web/templates/default/acq/financial/list_funds.tt2 create mode 100644 Open-ILS/web/templates/default/acq/financial/list_providers.tt2 create mode 100644 Open-ILS/web/templates/default/acq/financial/view_fund.tt2 create mode 100644 Open-ILS/web/templates/default/acq/financial/view_funding_source.tt2 create mode 100644 Open-ILS/web/templates/default/acq/financial/view_provider.tt2 create mode 100644 Open-ILS/web/templates/default/acq/picklist/bib_search.tt2 create mode 100644 Open-ILS/web/templates/default/acq/picklist/list.tt2 create mode 100644 Open-ILS/web/templates/default/acq/picklist/view.tt2 create mode 100644 Open-ILS/web/templates/default/acq/po/li_search.tt2 create mode 100644 Open-ILS/web/templates/default/acq/po/search.tt2 create mode 100644 Open-ILS/web/templates/default/acq/po/view.tt2 create mode 100644 Open-ILS/web/templates/default/acq/receiving/process.tt2 create mode 100644 Open-ILS/web/templates/default/acq/settings/li_attr.tt2 create mode 100644 Open-ILS/web/templates/default/base.tt2 create mode 100644 Open-ILS/web/templates/default/footer.tt2 create mode 100644 Open-ILS/web/templates/default/header.tt2 create mode 100644 Open-ILS/web/templates/default/menu.tt2 diff --git a/Open-ILS/examples/apache/eg_vhost.conf b/Open-ILS/examples/apache/eg_vhost.conf index b2b2f390a4..e353a842dd 100644 --- a/Open-ILS/examples/apache/eg_vhost.conf +++ b/Open-ILS/examples/apache/eg_vhost.conf @@ -353,3 +353,23 @@ RewriteMap openurl prg:/openils/bin/openurl_map.pl RewriteCond %{QUERY_STRING} (^.*$) RewriteRule ^/openurl$ ${openurl:%1} [NE,PT] + + +# General Evergreen web template processor + + SetHandler perl-script + PerlHandler OpenILS::WWW::EGWeb + Options +ExecCGI + PerlSendHeader On + allow from all + +# Note: the template processor will decline handling anything it does not +# have an explicit configuration for, which means it will fall back to +# Apache to serve the file. However, in the interest of speed, go ahead +# and tell Apache to avoid asking OpenILS::WWW::EGWeb for static content. +# Add more exemptions as needed. + + SetHandler None + + + diff --git a/Open-ILS/examples/fm_IDL.xml b/Open-ILS/examples/fm_IDL.xml index d7f73ae709..60b00346d1 100644 --- a/Open-ILS/examples/fm_IDL.xml +++ b/Open-ILS/examples/fm_IDL.xml @@ -1,5 +1,4 @@ - + + diff --git a/Open-ILS/examples/oils_web.xml b/Open-ILS/examples/oils_web.xml new file mode 100644 index 0000000000..babb51abb0 --- /dev/null +++ b/Open-ILS/examples/oils_web.xml @@ -0,0 +1,35 @@ + + + /eg + + + + + + + + /openils/var/web/templates + + + + + + + + + + + + + + + + + + + + + diff --git a/Open-ILS/src/extras/ils_events.xml b/Open-ILS/src/extras/ils_events.xml index dd991d610a..bda47ff464 100644 --- a/Open-ILS/src/extras/ils_events.xml +++ b/Open-ILS/src/extras/ils_events.xml @@ -585,7 +585,87 @@ - + + The requested acq.picklist was not found + + + The requested acq.lineitem_attr was not found + + + The requested acq.funding_source was not found + + + The requested acq.provider was not found + + + The requested acq.funding_source_credit was not found + + + The requested acq.funding_source_dedit was not found + + + The requested acq.fund was not found + + + The requested acq.fund_debit_total was not found + + + The requested acq.fund_allocation_total was not found + + + The requested acq.fund_encumbrance_total was not found + + + The requested acq.fund_spent_total was not found + + + The requested acq.fund_combined_balance was not found + + + The requested acq.fund_spent_balance was not found + + + The requested acq.funding_source_credit_total was not found + + + The requested acq.funding_source_allocation_total was not found + + + The requested acq.funding_source_balance was not found + + + The requested acq.po_lineitem was not found + + + The requested acq.purchase_order was not found + + + The requested acq.lineitem_detail was not found + + + The requested permission.usr_object_perm_map was not found + + + The requested acq.lineitem_provider_attr_definition was not found + + + The requested acq.currency_type was not found + + + The requested acq_lineitem_attr was not found + + + The requested acq_lineitem_attr was not found + + + The requested acq_lineitem_attr was not found + + + The requested acq_lineitem_attr was not found + + + The requested acq_lineitem_attr was not found + @@ -767,6 +847,26 @@ A report with the given name and folder already exists + + The lineitem cannot be altered because it has already been approved + + + The lineitem has no attached copies + + + The lineitem detail has no associated fund + + + The lineitem detail has no owning_lib + + + The lineitem has no price + + + The lineitem has no price + + + diff --git a/Open-ILS/src/perlmods/OpenILS/Application/Acq.pm b/Open-ILS/src/perlmods/OpenILS/Application/Acq.pm new file mode 100644 index 0000000000..789252ce0a --- /dev/null +++ b/Open-ILS/src/perlmods/OpenILS/Application/Acq.pm @@ -0,0 +1,10 @@ +package OpenILS::Application::Acq; +use base qw/OpenILS::Application/; +use strict; use warnings; + +use OpenILS::Application::Acq::Picklist; +use OpenILS::Application::Acq::Financials; +use OpenILS::Application::Acq::Provider; +use OpenILS::Application::Acq::Lineitem; + +1; diff --git a/Open-ILS/src/perlmods/OpenILS/Application/Acq/Financials.pm b/Open-ILS/src/perlmods/OpenILS/Application/Acq/Financials.pm new file mode 100644 index 0000000000..5125386c0a --- /dev/null +++ b/Open-ILS/src/perlmods/OpenILS/Application/Acq/Financials.pm @@ -0,0 +1,855 @@ +package OpenILS::Application::Acq::Financials; +use base qw/OpenILS::Application/; +use strict; use warnings; + +use OpenSRF::Utils::Logger qw(:logger); +use OpenILS::Utils::Fieldmapper; +use OpenILS::Utils::CStoreEditor q/:funcs/; +use OpenILS::Const qw/:const/; +use OpenSRF::Utils::SettingsClient; +use OpenILS::Event; +use OpenILS::Application::AppUtils; +my $U = 'OpenILS::Application::AppUtils'; + +# ---------------------------------------------------------------------------- +# Funding Sources +# ---------------------------------------------------------------------------- + +__PACKAGE__->register_method( + method => 'create_funding_source', + api_name => 'open-ils.acq.funding_source.create', + signature => { + desc => 'Creates a new funding_source', + params => [ + {desc => 'Authentication token', type => 'string'}, + {desc => 'funding source object to create', type => 'object'} + ], + return => {desc => 'The ID of the new funding_source'} + } +); + +sub create_funding_source { + my($self, $conn, $auth, $funding_source) = @_; + my $e = new_editor(xact=>1, authtoken=>$auth); + return $e->die_event unless $e->checkauth; + return $e->die_event unless $e->allowed('ADMIN_FUNDING_SOURCE', $funding_source->owner); + $e->create_acq_funding_source($funding_source) or return $e->die_event; + $e->commit; + return $funding_source->id; +} + + +__PACKAGE__->register_method( + method => 'delete_funding_source', + api_name => 'open-ils.acq.funding_source.delete', + signature => { + desc => 'Deletes a funding_source', + params => [ + {desc => 'Authentication token', type => 'string'}, + {desc => 'funding source ID', type => 'number'} + ], + return => {desc => '1 on success, Event on failure'} + } +); + +sub delete_funding_source { + my($self, $conn, $auth, $funding_source_id) = @_; + my $e = new_editor(xact=>1, authtoken=>$auth); + return $e->die_event unless $e->checkauth; + my $funding_source = $e->retrieve_acq_funding_source($funding_source_id) or return $e->die_event; + return $e->die_event unless $e->allowed('ADMIN_FUNDING_SOURCE', $funding_source->owner, $funding_source); + $e->delete_acq_funding_source($funding_source) or return $e->die_event; + $e->commit; + return 1; +} + +__PACKAGE__->register_method( + method => 'retrieve_funding_source', + api_name => 'open-ils.acq.funding_source.retrieve', + signature => { + desc => 'Retrieves a new funding_source', + params => [ + {desc => 'Authentication token', type => 'string'}, + {desc => 'funding source ID', type => 'number'} + ], + return => {desc => 'The funding_source object on success, Event on failure'} + } +); + +sub retrieve_funding_source { + my($self, $conn, $auth, $funding_source_id, $options) = @_; + my $e = new_editor(authtoken=>$auth); + return $e->event unless $e->checkauth; + $options ||= {}; + + my $flesh = {flesh => 1, flesh_fields => {acqfs => []}}; + push(@{$flesh->{flesh_fields}->{acqfs}}, 'credits') if $$options{flesh_credits}; + push(@{$flesh->{flesh_fields}->{acqfs}}, 'allocations') if $$options{flesh_allocations}; + + my $funding_source = $e->retrieve_acq_funding_source([$funding_source_id, $flesh]) or return $e->event; + + return $e->event unless $e->allowed( + ['ADMIN_FUNDING_SOURCE','MANAGE_FUNDING_SOURCE', 'VIEW_FUNDING_SOURCE'], + $funding_source->owner, $funding_source); + + $funding_source->summary(retrieve_funding_source_summary_impl($e, $funding_source)) + if $$options{flesh_summary}; + return $funding_source; +} + +__PACKAGE__->register_method( + method => 'retrieve_org_funding_sources', + api_name => 'open-ils.acq.funding_source.org.retrieve', + stream => 1, + signature => { + desc => 'Retrieves all the funding_sources associated with an org unit that the requestor has access to see', + params => [ + {desc => 'Authentication token', type => 'string'}, + {desc => 'List of org Unit IDs. If no IDs are provided, this method returns the + full set of funding sources this user has permission to view', type => 'number'}, + {desc => q/Limiting permission. this permission is used find the work-org tree from which + the list of orgs is generated if no org ids are provided. + The default is ADMIN_FUNDING_SOURCE/, type => 'string'}, + ], + return => {desc => 'The funding_source objects on success, empty array otherwise'} + } +); + +sub retrieve_org_funding_sources { + my($self, $conn, $auth, $org_id_list, $options) = @_; + my $e = new_editor(authtoken=>$auth); + return $e->event unless $e->checkauth; + $options ||= {}; + + my $limit_perm = ($$options{limit_perm}) ? $$options{limit_perm} : 'ADMIN_FUNDING_SOURCE'; + return OpenILS::Event->new('BAD_PARAMS') + unless $limit_perm =~ /(ADMIN|MANAGE|VIEW)_FUNDING_SOURCE/; + + my $org_ids = ($org_id_list and @$org_id_list) ? $org_id_list : + $U->find_highest_work_orgs($e, $limit_perm, {descendants =>1}); + + return [] unless @$org_ids; + my $sources = $e->search_acq_funding_source({owner => $org_ids}); + + for my $source (@$sources) { + $source->summary(retrieve_funding_source_summary_impl($e, $source)) + if $$options{flesh_summary}; + $conn->respond($source); + } + + return undef; +} + +sub retrieve_funding_source_summary_impl { + my($e, $source) = @_; + my $at = $e->search_acq_funding_source_allocation_total({funding_source => $source->id})->[0]; + my $b = $e->search_acq_funding_source_balance({funding_source => $source->id})->[0]; + my $ct = $e->search_acq_funding_source_credit_total({funding_source => $source->id})->[0]; + return { + allocation_total => ($at) ? $at->amount : 0, + balance => ($b) ? $b->amount : 0, + credit_total => ($ct) ? $ct->amount : 0, + }; +} + + +__PACKAGE__->register_method( + method => 'create_funding_source_credit', + api_name => 'open-ils.acq.funding_source_credit.create', + signature => { + desc => 'Create a new funding source credit', + params => [ + {desc => 'Authentication token', type => 'string'}, + {desc => 'funding source credit object', type => 'object'} + ], + return => {desc => 'The ID of the new funding source credit on success, Event on failure'} + } +); + +sub create_funding_source_credit { + my($self, $conn, $auth, $fs_credit) = @_; + my $e = new_editor(authtoken=>$auth, xact=>1); + return $e->event unless $e->checkauth; + + my $fs = $e->retrieve_acq_funding_source($fs_credit->funding_source) + or return $e->die_event; + return $e->die_event unless $e->allowed(['MANAGE_FUNDING_SOURCE'], $fs->owner, $fs); + + $e->create_acq_funding_source_credit($fs_credit) or return $e->die_event; + $e->commit; + return $fs_credit->id; +} + + +# --------------------------------------------------------------- +# funds +# --------------------------------------------------------------- + +__PACKAGE__->register_method( + method => 'create_fund', + api_name => 'open-ils.acq.fund.create', + signature => { + desc => 'Creates a new fund', + params => [ + {desc => 'Authentication token', type => 'string'}, + {desc => 'fund object to create', type => 'object'} + ], + return => {desc => 'The ID of the newly created fund object'} + } +); + +sub create_fund { + my($self, $conn, $auth, $fund) = @_; + my $e = new_editor(xact=>1, authtoken=>$auth); + return $e->die_event unless $e->checkauth; + return $e->die_event unless $e->allowed('ADMIN_FUND', $fund->org); + $e->create_acq_fund($fund) or return $e->die_event; + $e->commit; + return $fund->id; +} + + +__PACKAGE__->register_method( + method => 'delete_fund', + api_name => 'open-ils.acq.fund.delete', + signature => { + desc => 'Deletes a fund', + params => { + {desc => 'Authentication token', type => 'string'}, + {desc => 'fund ID', type => 'number'} + }, + return => {desc => '1 on success, Event on failure'} + } +); + +sub delete_fund { + my($self, $conn, $auth, $fund_id) = @_; + my $e = new_editor(xact=>1, authtoken=>$auth); + return $e->die_event unless $e->checkauth; + my $fund = $e->retrieve_acq_fund($fund_id) or return $e->die_event; + return $e->die_event unless $e->allowed('ADMIN_FUND', $fund->org, $fund); + $e->delete_acq_fund($fund) or return $e->die_event; + $e->commit; + return 1; +} + +__PACKAGE__->register_method( + method => 'retrieve_fund', + api_name => 'open-ils.acq.fund.retrieve', + signature => { + desc => 'Retrieves a new fund', + params => [ + {desc => 'Authentication token', type => 'string'}, + {desc => 'fund ID', type => 'number'} + ], + return => {desc => 'The fund object on success, Event on failure'} + } +); + +sub retrieve_fund { + my($self, $conn, $auth, $fund_id, $options) = @_; + my $e = new_editor(authtoken=>$auth); + return $e->event unless $e->checkauth; + $options ||= {}; + + my $flesh = {flesh => 2, flesh_fields => {acqf => []}}; + push(@{$flesh->{flesh_fields}->{acqf}}, 'debits') if $$options{flesh_debits}; + push(@{$flesh->{flesh_fields}->{acqf}}, 'allocations') if $$options{flesh_allocations}; + push(@{$flesh->{flesh_fields}->{acqfa}}, 'funding_source') if $$options{flesh_allocation_sources}; + + my $fund = $e->retrieve_acq_fund([$fund_id, $flesh]) or return $e->event; + return $e->event unless $e->allowed(['ADMIN_FUND','MANAGE_FUND', 'VIEW_FUND'], $fund->org, $fund); + $fund->summary(retrieve_fund_summary_impl($e, $fund)) + if $$options{flesh_summary}; + return $fund; +} + +__PACKAGE__->register_method( + method => 'retrieve_org_funds', + api_name => 'open-ils.acq.fund.org.retrieve', + stream => 1, + signature => { + desc => 'Retrieves all the funds associated with an org unit', + params => [ + {desc => 'Authentication token', type => 'string'}, + {desc => 'List of org Unit IDs. If no IDs are provided, this method returns the + full set of funding sources this user has permission to view', type => 'number'}, + {desc => q/Options hash. + "limit_perm" -- this permission is used find the work-org tree from which + the list of orgs is generated if no org ids are provided. The default is ADMIN_FUND. + "flesh_summary" -- if true, the summary field on each fund is fleshed + The default is ADMIN_FUND/, type => 'string'}, + ], + return => {desc => 'The fund objects on success, Event on failure'} + } +); + +sub retrieve_org_funds { + my($self, $conn, $auth, $org_id_list, $options) = @_; + my $e = new_editor(authtoken=>$auth); + return $e->event unless $e->checkauth; + $options ||= {}; + + my $limit_perm = ($$options{limit_perm}) ? $$options{limit_perm} : 'ADMIN_FUND'; + return OpenILS::Event->new('BAD_PARAMS') + unless $limit_perm =~ /(ADMIN|MANAGE|VIEW)_FUND/; + + my $org_ids = ($org_id_list and @$org_id_list) ? $org_id_list : + $U->find_highest_work_orgs($e, $limit_perm, {descendants =>1}); + return undef unless @$org_ids; + my $funds = $e->search_acq_fund({org => $org_ids}); + + for my $fund (@$funds) { + $fund->summary(retrieve_fund_summary_impl($e, $fund)) + if $$options{flesh_summary}; + $conn->respond($fund); + } + + return undef; +} + +__PACKAGE__->register_method( + method => 'retrieve_fund_summary', + api_name => 'open-ils.acq.fund.summary.retrieve', + signature => { + desc => 'Returns a summary of credits/debits/encumbrances for a fund', + params => [ + {desc => 'Authentication token', type => 'string'}, + {desc => 'fund id', type => 'number' } + ], + return => {desc => 'A hash of summary information, Event on failure'} + } +); + +sub retrieve_fund_summary { + my($self, $conn, $auth, $fund_id) = @_; + my $e = new_editor(authtoken=>$auth); + return $e->event unless $e->checkauth; + my $fund = $e->retrieve_acq_fund($fund_id) or return $e->event; + return $e->event unless $e->allowed('MANAGE_FUND', $fund->org, $fund); + return retrieve_fund_summary_impl($e, $fund); +} + + +sub retrieve_fund_summary_impl { + my($e, $fund) = @_; + + my $at = $e->search_acq_fund_allocation_total({fund => $fund->id})->[0]; + my $dt = $e->search_acq_fund_debit_total({fund => $fund->id})->[0]; + my $et = $e->search_acq_fund_encumbrance_total({fund => $fund->id})->[0]; + my $st = $e->search_acq_fund_spent_total({fund => $fund->id})->[0]; + my $cb = $e->search_acq_fund_combined_balance({fund => $fund->id})->[0]; + my $sb = $e->search_acq_fund_spent_balance({fund => $fund->id})->[0]; + + return { + allocation_total => ($at) ? $at->amount : 0, + debit_total => ($dt) ? $dt->amount : 0, + encumbrance_total => ($et) ? $et->amount : 0, + spent_total => ($st) ? $st->amount : 0, + combined_balance => ($cb) ? $cb->amount : 0, + spent_balance => ($sb) ? $sb->amount : 0, + }; +} + + +# --------------------------------------------------------------- +# fund Allocations +# --------------------------------------------------------------- + +__PACKAGE__->register_method( + method => 'create_fund_alloc', + api_name => 'open-ils.acq.fund_allocation.create', + signature => { + desc => 'Creates a new fund_allocation', + params => [ + {desc => 'Authentication token', type => 'string'}, + {desc => 'fund allocation object to create', type => 'object'} + ], + return => {desc => 'The ID of the new fund_allocation'} + } +); + +sub create_fund_alloc { + my($self, $conn, $auth, $fund_alloc) = @_; + my $e = new_editor(xact=>1, authtoken=>$auth); + return $e->die_event unless $e->checkauth; + + # this action is equivalent to both debiting a funding source and crediting a fund + + my $source = $e->retrieve_acq_funding_source($fund_alloc->funding_source) + or return $e->die_event; + return $e->die_event unless $e->allowed('MANAGE_FUNDING_SOURCE', $source->owner); + + my $fund = $e->retrieve_acq_fund($fund_alloc->fund) or return $e->die_event; + return $e->die_event unless $e->allowed('MANAGE_FUND', $fund->org, $fund); + + $fund_alloc->allocator($e->requestor->id); + $e->create_acq_fund_allocation($fund_alloc) or return $e->die_event; + $e->commit; + return $fund_alloc->id; +} + + +__PACKAGE__->register_method( + method => 'delete_fund_alloc', + api_name => 'open-ils.acq.fund_allocation.delete', + signature => { + desc => 'Deletes a fund_allocation', + params => [ + {desc => 'Authentication token', type => 'string'}, + {desc => 'fund Alocation ID', type => 'number'} + ], + return => {desc => '1 on success, Event on failure'} + } +); + +sub delete_fund_alloc { + my($self, $conn, $auth, $fund_alloc_id) = @_; + my $e = new_editor(xact=>1, authtoken=>$auth); + return $e->die_event unless $e->checkauth; + + my $fund_alloc = $e->retrieve_acq_fund_allocation($fund_alloc_id) or return $e->die_event; + + my $source = $e->retrieve_acq_funding_source($fund_alloc->funding_source) + or return $e->die_event; + return $e->die_event unless $e->allowed('MANAGE_FUNDING_SOURCE', $source->owner, $source); + + my $fund = $e->retrieve_acq_fund($fund_alloc->fund) or return $e->die_event; + return $e->die_event unless $e->allowed('MANAGE_FUND', $fund->org, $fund); + + $e->delete_acq_fund_allocation($fund_alloc) or return $e->die_event; + $e->commit; + return 1; +} + +__PACKAGE__->register_method( + method => 'retrieve_fund_alloc', + api_name => 'open-ils.acq.fund_allocation.retrieve', + signature => { + desc => 'Retrieves a new fund_allocation', + params => [ + {desc => 'Authentication token', type => 'string'}, + {desc => 'fund Allocation ID', type => 'number'} + ], + return => {desc => 'The fund allocation object on success, Event on failure'} + } +); + +sub retrieve_fund_alloc { + my($self, $conn, $auth, $fund_alloc_id) = @_; + my $e = new_editor(authtoken=>$auth); + return $e->event unless $e->checkauth; + my $fund_alloc = $e->retrieve_acq_fund_allocation($fund_alloc_id) or return $e->event; + + my $source = $e->retrieve_acq_funding_source($fund_alloc->funding_source) + or return $e->die_event; + return $e->die_event unless $e->allowed('MANAGE_FUNDING_SOURCE', $source->owner, $source); + + my $fund = $e->retrieve_acq_fund($fund_alloc->fund) or return $e->die_event; + return $e->die_event unless $e->allowed('MANAGE_FUND', $fund->org, $fund); + + return $fund_alloc; +} + + +__PACKAGE__->register_method( + method => 'retrieve_funding_source_allocations', + api_name => 'open-ils.acq.funding_source.allocations.retrieve', + signature => { + desc => 'Retrieves a new fund_allocation', + params => [ + {desc => 'Authentication token', type => 'string'}, + {desc => 'fund Allocation ID', type => 'number'} + ], + return => {desc => 'The fund allocation object on success, Event on failure'} + } +); + +sub retrieve_funding_source_allocations { + my($self, $conn, $auth, $fund_alloc_id) = @_; + my $e = new_editor(authtoken=>$auth); + return $e->event unless $e->checkauth; + my $fund_alloc = $e->retrieve_acq_fund_allocation($fund_alloc_id) or return $e->event; + + my $source = $e->retrieve_acq_funding_source($fund_alloc->funding_source) + or return $e->die_event; + return $e->die_event unless $e->allowed('MANAGE_FUNDING_SOURCE', $source->owner, $source); + + my $fund = $e->retrieve_acq_fund($fund_alloc->fund) or return $e->die_event; + return $e->die_event unless $e->allowed('MANAGE_FUND', $fund->org, $fund); + + return $fund_alloc; +} + +# ---------------------------------------------------------------------------- +# Currency +# ---------------------------------------------------------------------------- + +__PACKAGE__->register_method( + method => 'retrieve_all_currency_type', + api_name => 'open-ils.acq.currency_type.all.retrieve', + signature => { + desc => 'Retrieves all currency_type objects', + params => [ + {desc => 'Authentication token', type => 'string'}, + ], + return => {desc => 'List of currency_type objects', type => 'list'} + } +); + +sub retrieve_all_currency_type { + my($self, $conn, $auth, $fund_alloc_id) = @_; + my $e = new_editor(authtoken=>$auth); + return $e->event unless $e->checkauth; + return $e->event unless $e->allowed('GENERAL_ACQ'); + return $e->retrieve_all_acq_currency_type(); +} + +sub currency_conversion_impl { + my($src_currency, $dest_currency, $amount) = @_; + my $result = new_editor()->json_query({ + select => { + acqct => [{ + params => [$dest_currency, $amount], + transform => 'acq.exchange_ratio', + column => 'code', + alias => 'value' + }] + }, + where => {code => $src_currency}, + from => 'acqct' + }); + + return $result->[0]->{value}; +} + + +# ---------------------------------------------------------------------------- +# Purchase Orders +# ---------------------------------------------------------------------------- + +__PACKAGE__->register_method( + method => 'create_purchase_order', + api_name => 'open-ils.acq.purchase_order.create', + signature => { + desc => 'Creates a new purchase order', + params => [ + {desc => 'Authentication token', type => 'string'}, + {desc => 'purchase_order to create', type => 'object'} + ], + return => {desc => 'The purchase order id, Event on failure'} + } +); + +sub create_purchase_order { + my($self, $conn, $auth, $p_order) = @_; + my $e = new_editor(xact=>1, authtoken=>$auth); + return $e->die_event unless $e->checkauth; + + $p_order->owner($e->requestor->id); + $p_order->ordering_agency($e->requestor->ws_ou); + return $e->die_event unless + $e->allowed('CREATE_PURCHASE_ORDER', $p_order->ordering_agency); + + my $provider = $e->retrieve_acq_provider($p_order->provider) + or return $e->die_event; + return $e->die_event unless + $e->allowed('MANAGE_PROVIDER', $provider->owner, $provider); + + $e->create_acq_purchase_order($p_order) or return $e->die_event; + + $e->commit; + return $p_order->id; +} + + +# returns (price, type), where type=1 is local, type=2 is provider, type=3 is marc +sub get_li_price { + my $li = shift; + my $attrs = $li->attributes; + my ($marc_estimated, $local_estimated, $local_actual, $prov_estimated, $prov_actual); + + for my $attr (@$attrs) { + if($attr->attr_name eq 'estimated_price') { + $local_estimated = $attr->attr_value + if $attr->attr_type eq 'lineitem_local_attr_definition'; + $prov_estimated = $attr->attr_value + if $attr->attr_type eq 'lineitem_prov_attr_definition'; + $marc_estimated = $attr->attr_value + if $attr->attr_type eq 'lineitem_marc_attr_definition'; + + } elsif($attr->attr_name eq 'actual_price') { + $local_actual = $attr->attr_value + if $attr->attr_type eq 'lineitem_local_attr_definition'; + $prov_actual = $attr->attr_value + if $attr->attr_type eq 'lineitem_prov_attr_definition'; + } + } + + return ($local_actual, 1) if $local_actual; + return ($prov_actual, 2) if $prov_actual; + return ($local_estimated, 1) if $local_estimated; + return ($prov_estimated, 2) if $prov_estimated; + return ($marc_estimated, 3); +} + + +__PACKAGE__->register_method( + method => 'create_purchase_order_debits', + api_name => 'open-ils.acq.purchase_order.debits.create', + signature => { + desc => 'Creates debits associated with a PO', + params => [ + {desc => 'Authentication token', type => 'string'}, + {desc => 'purchase_order whose debits to create', type => 'number'}, + {desc => 'arguments hash. Options include: encumbrance=bool', type => 'object'}, + ], + return => {desc => 'The total amount of all created debits, Event on error'} + } +); + +sub create_purchase_order_debits { + my($self, $conn, $auth, $po_id, $args) = @_; + my $e = new_editor(xact=>1, authtoken=>$auth); + return $e->die_event unless $e->checkauth; + + my $total = 0; + my $po = $e->retrieve_acq_purchase_order($po_id) or return $e->die_event; + # XXX which perms? + + my $li_ids = $e->search_acq_lineitem( + {purchase_order => $po_id}, + {idlist => 1} + ); + + for my $li_id (@$li_ids) { + my $li = $e->retrieve_acq_lineitem([ + $li_id, + { flesh => 1, + flesh_fields => {jub => ['attributes']}, + } + ]); + + my ($price, $ptype) = get_li_price($li); + unless($price) { + $e->rollback; + return OpenILS::Event->new('ACQ_LINEITEM_NO_PRICE', payload => $li->id); + } + + unless($li->provider) { + $e->rollback; + return OpenILS::Event->new('ACQ_LINEITEM_NO_PROVIDER', payload => $li->id); + } + + my $lid_ids = $e->search_acq_lineitem_detail( + {lineitem => $li->id}, + {idlist=>1} + ); + + for my $lid_id (@$lid_ids) { + my $lid = $e->retrieve_acq_lineitem_detail([ + $lid_id, + { flesh => 1, + flesh_fields => {acqlid => ['fund']} + } + ]); + + my $debit = Fieldmapper::acq::fund_debit->new; + $debit->fund($lid->fund->id); + $debit->origin_amount($price); + + if($ptype == 2) { # price from vendor + $debit->origin_currency_type($li->provider->currency_type); + $debit->amount(currency_conversion_impl( + $li->provider->currency_type, $lid->fund->currency_type, $price)); + } else { + $debit->origin_currency_type($lid->fund->currency_type); + $debit->amount($price); + } + + $debit->encumbrance($args->{encumbrance}); + $debit->debit_type('purchase'); + $e->create_acq_fund_debit($debit) or return $e->die_event; + + # point the lineitem detail at the fund debit object + $lid->fund_debit($debit->id); + $lid->fund($lid->fund->id); + $e->update_acq_lineitem_detail($lid) or return $e->die_event; + $total += $debit->amount; + } + } + + $e->commit; + return $total; +} + + +__PACKAGE__->register_method( + method => 'retrieve_all_user_purchase_order', + api_name => 'open-ils.acq.purchase_order.user.all.retrieve', + stream => 1, + signature => { + desc => 'Retrieves a purchase order', + params => [ + {desc => 'Authentication token', type => 'string'}, + {desc => 'purchase_order to retrieve', type => 'number'}, + {desc => q/Options hash. flesh_lineitems: to get the lineitems and lineitem_attrs; + clear_marc: to clear the MARC data from the lineitem (for reduced bandwidth); + limit: number of items to return ,defaults to 50; + offset: offset in the list of items to return + order_by: sort the result, provide one or more colunm names, separated by commas, + optionally followed by ASC or DESC as a single string + li_limit : number of lineitems to return if fleshing line items; + li_offset : lineitem offset if fleshing line items + li_order_by : lineitem sort definition if fleshing line items + flesh_lineitem_detail_count : flesh lineitem_detail_count field + /, + type => 'hash'} + ], + return => {desc => 'The purchase order, Event on failure'} + } +); + +sub retrieve_all_user_purchase_order { + my($self, $conn, $auth, $options) = @_; + my $e = new_editor(authtoken=>$auth); + return $e->event unless $e->checkauth; + $options ||= {}; + + # grab purchase orders I have + my $perm_orgs = $U->find_highest_work_orgs($e, 'MANAGE_PROVIDER', {descendants =>1}); + return OpenILS::Event->new('PERM_FAILURE', ilsperm => 'MANAGE_PROVIDER') + unless @$perm_orgs; + my $provider_ids = $e->search_acq_provider({owner => $perm_orgs}, {idlist=>1}); + my $po_ids = $e->search_acq_purchase_order({provider => $provider_ids}, {idlist=>1}); + + # grab my purchase orders + push(@$po_ids, @{$e->search_acq_purchase_order({owner => $e->requestor->id}, {idlist=>1})}); + + return undef unless @$po_ids; + + # now get the db to limit/sort for us + $po_ids = $e->search_acq_purchase_order( + [ {id => $po_ids}, { + limit => $$options{limit} || 50, + offset => $$options{offset} || 0, + order_by => {acqpo => $$options{order_by} || 'create_time'} + } + ], + {idlist => 1} + ); + + $conn->respond(retrieve_purchase_order_impl($e, $_, $options)) for @$po_ids; + return undef; +} + + +__PACKAGE__->register_method( + method => 'search_purchase_order', + api_name => 'open-ils.acq.purchase_order.search', + stream => 1, + signature => { + desc => 'Search for a purchase order', + params => [ + {desc => 'Authentication token', type => 'string'}, + {desc => q/Search hash. Search fields include id, provider/, type => 'hash'} + ], + return => {desc => 'A stream of POs'} + } +); + +sub search_purchase_order { + my($self, $conn, $auth, $search, $options) = @_; + my $e = new_editor(authtoken=>$auth); + return $e->event unless $e->checkauth; + my $po_ids = $e->search_acq_purchase_order($search, {idlist=>1}); + for my $po_id (@$po_ids) { + $conn->respond($e->retrieve_acq_purchase_order($po_id)) + unless po_perm_failure($e, $po_id); + } + + return undef; +} + + + +__PACKAGE__->register_method( + method => 'retrieve_purchase_order', + api_name => 'open-ils.acq.purchase_order.retrieve', + signature => { + desc => 'Retrieves a purchase order', + params => [ + {desc => 'Authentication token', type => 'string'}, + {desc => 'purchase_order to retrieve', type => 'number'}, + {desc => q/Options hash. flesh_lineitems, to get the lineitems and lineitem_attrs; + clear_marc, to clear the MARC data from the lineitem (for reduced bandwidth) + li_limit : number of lineitems to return if fleshing line items; + li_offset : lineitem offset if fleshing line items + li_order_by : lineitem sort definition if fleshing line items + /, + type => 'hash'} + ], + return => {desc => 'The purchase order, Event on failure'} + } +); + +sub retrieve_purchase_order { + my($self, $conn, $auth, $po_id, $options) = @_; + my $e = new_editor(authtoken=>$auth); + return $e->event unless $e->checkauth; + return $e->event if po_perm_failure($e, $po_id); + return retrieve_purchase_order_impl($e, $po_id, $options); +} + + +# if the user does not have permission to perform actions on this PO, return the perm failure event +sub po_perm_failure { + my($e, $po_id, $fund_id) = @_; + my $po = $e->retrieve_acq_purchase_order($po_id) or return $e->event; + my $provider = $e->retrieve_acq_provider($po->provider) or return $e->event; + return $e->event unless $e->allowed('MANAGE_PROVIDER', $provider->owner, $provider); + if($fund_id) { + my $fund = $e->retrieve_acq_fund($po->$fund_id); + return $e->event unless $e->allowed('MANAGE_FUND', $fund->org, $fund); + } + return undef; +} + +sub retrieve_purchase_order_impl { + my($e, $po_id, $options) = @_; + + $options ||= {}; + my $po = $e->retrieve_acq_purchase_order($po_id) or return $e->event; + + if($$options{flesh_lineitems}) { + my $items = $e->search_acq_lineitem([ + {purchase_order => $po_id}, + { + flesh => 1, + flesh_fields => { + jub => ['attributes'] + }, + limit => $$options{li_limit} || 50, + offset => $$options{li_offset} || 0, + order_by => {jub => $$options{li_order_by} || 'create_time'} + } + ]); + + if($$options{clear_marc}) { + $_->clear_marc for @$items; + } + + $po->lineitems($items); + } + + if($$options{flesh_lineitem_count}) { + my $items = $e->search_acq_lineitem({purchase_order => $po_id}, {idlist=>1}); + $po->lineitem_count(scalar(@$items)); + } + + return $po; +} + + +1; + diff --git a/Open-ILS/src/perlmods/OpenILS/Application/Acq/Lineitem.pm b/Open-ILS/src/perlmods/OpenILS/Application/Acq/Lineitem.pm new file mode 100644 index 0000000000..f3cfb8dc50 --- /dev/null +++ b/Open-ILS/src/perlmods/OpenILS/Application/Acq/Lineitem.pm @@ -0,0 +1,903 @@ +package OpenILS::Application::Acq::Picklist; +use base qw/OpenILS::Application/; +use strict; use warnings; + +use OpenILS::Event; +use OpenSRF::Utils::Logger qw(:logger); +use OpenILS::Utils::Fieldmapper; +use OpenILS::Utils::CStoreEditor q/:funcs/; +use OpenILS::Const qw/:const/; +use OpenSRF::Utils::SettingsClient; +use OpenILS::Application::AppUtils; +my $U = 'OpenILS::Application::AppUtils'; + + +__PACKAGE__->register_method( + method => 'create_lineitem', + api_name => 'open-ils.acq.lineitem.create', + signature => { + desc => 'Creates a lineitem', + params => [ + {desc => 'Authentication token', type => 'string'}, + {desc => 'The lineitem object to create', type => 'object'}, + ], + return => {desc => 'ID of newly created lineitem on success, Event on error'} + } +); + +sub create_lineitem { + my($self, $conn, $auth, $li) = @_; + my $e = new_editor(xact=>1, authtoken=>$auth); + return $e->die_event unless $e->checkauth; + + + if($li->picklist) { + my $picklist = $e->retrieve_acq_picklist($li->picklist) + or return $e->die_event; + + if($picklist->owner != $e->requestor->id) { + return $e->die_event unless + $e->allowed('CREATE_PICKLIST', $picklist->org_unit, $picklist); + } + + # indicate the picklist was updated + $picklist->edit_time('now'); + $e->update_acq_picklist($picklist) or return $e->die_event; + } + + if($li->purchase_order) { + my $po = $e->retrieve_acq_purchase_order($li->purchase_order) + or return $e->die_event; + return $e->die_event unless + $e->allowed('MANAGE_PROVIDER', $po->ordering_agency, $po); + } + + $li->selector($e->requestor->id); + $e->create_acq_lineitem($li) or return $e->die_event; + + $e->commit; + return $li->id; +} + +__PACKAGE__->register_method( + method => 'create_po_assets', + api_name => 'open-ils.acq.purchase_order.assets.create', + signature => { + desc => q/Creates assets for each lineitem in the purchase order/, + params => [ + {desc => 'Authentication token', type => 'string'}, + {desc => 'The purchase order id', type => 'number'}, + {desc => q/Options hash./} + ], + return => {desc => 'Streams a total versus completed counts object, event on error'} + } +); + +sub create_po_assets { + my($self, $conn, $auth, $po_id, $options) = @_; + my $e = new_editor(authtoken=>$auth, xact=>1); + return $e->die_event unless $e->checkauth; + + my $po = $e->retrieve_acq_purchase_order($po_id) or return $e->event; + return $e->die_event unless + $e->allowed('CREATE_PURCHASE_ORDER', $po->ordering_agency); + + my $li_ids = $e->search_acq_lineitem({purchase_order=>$po_id},{idlist=>1}); + my $total = @$li_ids; + my $count = 0; + + for my $li_id (@$li_ids) { + my $resp = create_lineitem_assets_impl($e, $auth, $li_id); + if($U->event_code($resp)) { + $e->rollback; + return $resp; + } + $conn->respond({total=>$count, progress=>++$count}); + } + + $po->edit_time('now'); + $e->update_acq_purchase_order($po) or return $e->die_event; + $e->commit; + + return {complete=>1}; +} + +__PACKAGE__->register_method( + method => 'create_lineitem_assets', + api_name => 'open-ils.acq.lineitem.assets.create', + signature => { + desc => q/Creates the bibliographic data, volume, and copies associated with a lineitem./, + params => [ + {desc => 'Authentication token', type => 'string'}, + {desc => 'The lineitem id', type => 'number'}, + {desc => q/Options hash./} + ], + return => {desc => 'ID of newly created bib record, Event on error'} + } +); + +sub create_lineitem_assets { + my($self, $conn, $auth, $li_id, $options) = @_; + my $e = new_editor(authtoken=>$auth, xact=>1); + return $e->die_event unless $e->checkauth; + my $resp = create_lineitem_assets_impl($e, $auth, $li_id, $options); + if($U->event_code($resp)) { + $e->rollback; + return $resp; + } + $e->commit; + return $resp; +} + +sub create_lineitem_assets_impl { + my($e, $auth, $li_id, $options) = @_; + my $li = $e->retrieve_acq_lineitem([ + $li_id, + { flesh => 1, + flesh_fields => {jub => ['purchase_order']} + } + ]) or return $e->die_event; + + return OpenILS::Event->new('BAD_PARAMS') # make this perm-based, not owner-based + unless $li->purchase_order->owner == $e->requestor->id; + + # ----------------------------------------------------------------- + # first, create the bib record if necessary + # ----------------------------------------------------------------- + unless($li->eg_bib_id) { + my $record = $U->simplereq( + 'open-ils.cat', + 'open-ils.cat.biblio.record.xml.import', + $auth, $li->marc, $li->source_label); + + if($U->event_code($record)) { + $e->rollback; + return $record; + } + + $li->eg_bib_id($record->id); + $e->update_acq_lineitem($li) or return $e->die_event; + } + + my $li_details = $e->search_acq_lineitem_detail({lineitem => $li_id}, {idlist=>1}); + + # ----------------------------------------------------------------- + # for each lineitem_detail, create the volume if necessary, create + # a copy, and link them all together. + # ----------------------------------------------------------------- + my %volcache; + for my $li_detail_id (@{$li_details}) { + + my $li_detail = $e->retrieve_acq_lineitem_detail($li_detail_id) + or return $e->die_event; + + my $volume = $volcache{$li_detail->cn_label}; + unless($volume and $volume->owning_lib == $li_detail->owning_lib) { + my $vol_id = $U->simplereq( + 'open-ils.cat', + 'open-ils.cat.call_number.find_or_create', + $auth, $li_detail->cn_label, $li->eg_bib_id, $li_detail->owning_lib); + $volume = $e->retrieve_asset_call_number($vol_id) or return $e->die_event; + $volcache{$vol_id} = $volume; + } + + if($U->event_code($volume)) { + $e->rollback; + return $volume; + } + + my $copy = Fieldmapper::asset::copy->new; + $copy->isnew(1); + $copy->loan_duration(2); + $copy->fine_level(2); + $copy->status(OILS_COPY_STATUS_ON_ORDER); + $copy->barcode($li_detail->barcode); + $copy->location($li_detail->location); + $copy->call_number($volume->id); + $copy->circ_lib($volume->owning_lib); + + my $stat = $U->simplereq( + 'open-ils.cat', + 'open-ils.cat.asset.copy.fleshed.batch.update', $auth, [$copy]); + + if($U->event_code($stat)) { + $e->rollback; + return $stat; + } + + my $new_copy = $e->search_asset_copy({deleted=>'f', barcode=>$copy->barcode})->[0] + or return $e->die_event; + + $li_detail->eg_copy_id($new_copy->id); + $e->update_acq_lineitem_detail($li_detail) or return $e->die_event; + } + + return 1; +} + + + +__PACKAGE__->register_method( + method => 'retrieve_lineitem', + api_name => 'open-ils.acq.lineitem.retrieve', + signature => { + desc => 'Retrieves a lineitem', + params => [ + {desc => 'Authentication token', type => 'string'}, + {desc => 'lineitem ID to retrieve', type => 'number'}, + {options => q/Hash of options, including + "flesh_attrs", which fleshes the attributes; + "flesh_li_details", which fleshes the order details objects/, type => 'hash'}, + ], + return => {desc => 'lineitem object on success, Event on error'} + } +); + + +sub retrieve_lineitem { + my($self, $conn, $auth, $li_id, $options) = @_; + my $e = new_editor(authtoken=>$auth); + return $e->die_event unless $e->checkauth; + $options ||= {}; + + # XXX finer grained perms... + + my $li; + + if($$options{flesh_attrs}) { + $li = $e->retrieve_acq_lineitem([ + $li_id, {flesh => 1, flesh_fields => {jub => ['attributes']}}]) + or return $e->event; + } else { + $li = $e->retrieve_acq_lineitem($li_id) or return $e->event; + } + + if($$options{flesh_li_details}) { + my $ops = { + flesh => 1, + flesh_fields => {acqlid => []} + }; + push(@{$ops->{flesh_fields}->{acqlid}}, 'fund') if $$options{flesh_fund}; + push(@{$ops->{flesh_fields}->{acqlid}}, 'fund_debit') if $$options{flesh_fund_debit}; + my $details = $e->search_acq_lineitem_detail([{lineitem => $li_id}, $ops]); + $li->lineitem_details($details); + $li->item_count(scalar(@$details)); + } else { + my $details = $e->search_acq_lineitem_detail({lineitem => $li_id}, {idlist=>1}); + $li->item_count(scalar(@$details)); + } + + if($li->picklist) { + my $picklist = $e->retrieve_acq_picklist($li->picklist) + or return $e->event; + + if($picklist->owner != $e->requestor->id) { + return $e->event unless + $e->allowed('VIEW_PICKLIST', undef, $picklist); + } + } + + $li->clear_marc if $$options{clear_marc}; + + return $li; +} + + + +__PACKAGE__->register_method( + method => 'delete_lineitem', + api_name => 'open-ils.acq.lineitem.delete', + signature => { + desc => 'Deletes a lineitem', + params => [ + {desc => 'Authentication token', type => 'string'}, + {desc => 'lineitem ID to delete', type => 'number'}, + ], + return => {desc => '1 on success, Event on error'} + } +); + +sub delete_lineitem { + my($self, $conn, $auth, $li_id) = @_; + my $e = new_editor(xact=>1, authtoken=>$auth); + return $e->die_event unless $e->checkauth; + + my $li = $e->retrieve_acq_lineitem($li_id) + or return $e->die_event; + + # XXX check state + + if($li->picklist) { + my $picklist = $e->retrieve_acq_picklist($li->picklist) + or return $e->die_event; + return OpenILS::Event->new('BAD_PARAMS') + if $picklist->owner != $e->requestor->id; + } else { + # check PO perms + } + + # delete the attached lineitem_details + my $lid_ids = $e->search_acq_lineitem_detail( + {lineitem => $li_id}, {idlist=>1}); + + for my $lid_id (@$lid_ids) { + $e->delete_acq_lineitem_detail( + $e->retrieve_acq_lineitem_detail($lid_id)) + or return $e->die_event; + } + + $e->delete_acq_lineitem($li) or return $e->die_event; + $e->commit; + return 1; +} + + +__PACKAGE__->register_method( + method => 'update_lineitem', + api_name => 'open-ils.acq.lineitem.update', + signature => { + desc => 'Update a lineitem', + params => [ + {desc => 'Authentication token', type => 'string'}, + {desc => 'lineitem object update', type => 'object'} + ], + return => {desc => '1 on success, Event on error'} + } +); + +sub update_lineitem { + my($self, $conn, $auth, $li) = @_; + my $e = new_editor(xact=>1, authtoken=>$auth); + return $e->die_event unless $e->checkauth; + + my $orig_li = $e->retrieve_acq_lineitem([ + $li->id, + { flesh => 1, # grab the lineitem with picklist attached + flesh_fields => {jub => ['picklist', 'purchase_order']} + } + ]) or return $e->die_event; + + # the marc may have been cleared on retrieval... + $li->marc($e->retrieve_acq_lineitem($li->id)->marc) + unless $li->marc; + + $e->update_acq_lineitem($li) or return $e->die_event; + $e->commit; + return 1; +} + +__PACKAGE__->register_method( + method => 'lineitem_search', + api_name => 'open-ils.acq.lineitem.search', + stream => 1, + signature => { + desc => 'Searches lineitems', + params => [ + {desc => 'Authentication token', type => 'string'}, + {desc => 'Search definition', type => 'object'}, + {desc => 'Optoins hash. idlist=true', type => 'object'}, + {desc => 'List of lineitems', type => 'object/number'}, + ] + } +); + +sub lineitem_search { + my($self, $conn, $auth, $search, $options) = @_; + my $e = new_editor(authtoken=>$auth, xact=>1); + return $e->event unless $e->checkauth; + return $e->event unless $e->allowed('CREATE_PICKLIST'); + # XXX needs permissions consideration + my $lis = $e->search_acq_lineitem($search, {idlist=>1}); + for my $li_id (@$lis) { + if($$options{idlist}) { + $conn->respond($li_id); + } else { + my $res = retrieve_lineitem($self, $conn, $auth, $li_id, $options); + $conn->respond($res) unless $U->event_code($res); + } + } + return undef; +} + + +__PACKAGE__->register_method( + method => 'lineitem_search_ident', + api_name => 'open-ils.acq.lineitem.search.ident', + stream => 1, + signature => { + desc => 'Performs a search against lineitem_attrs where ident is true', + params => [ + {desc => 'Authentication token', type => 'string'}, + { desc => q/Search definition. Options are: + attr_values : list of attribute values (required) + li_states : list of lineitem states + po_agencies : list of purchase order ordering agencies (org) ids + /, + type => 'object', + }, + { desc => q/ + Options hash. Options are: + idlist : if set, only return lineitem IDs + clear_marc : if set, strip the MARC xml from the lineitem before delivery + flesh_attrs : flesh lineitem attributes; + /, + type => 'object', + } + ] + } +); + +my $LI_ATTR_SEARCH = { + select => {acqlia => ['lineitem']}, + from => { + acqlia => { + acqliad => { + field => 'id', + fkey => 'definition' + }, + jub => { + field => 'id', + fkey => 'lineitem', + join => { + acqpo => { + field => 'id', + fkey => 'purchase_order' + } + } + } + } + } +}; + +sub lineitem_search_ident { + my($self, $conn, $auth, $search, $options) = @_; + my $e = new_editor(authtoken=>$auth, xact=>1); + return $e->event unless $e->checkauth; + # XXX needs permissions consideration + + return [] unless $search; + my $attr_values = $search->{attr_values}; + my $li_states = $search->{li_states}; + my $po_agencies = $search->{po_agencies}; # XXX if none, base it on perms + + my $where_clause = { + '-or' => [], + '+acqlia' => { + '+acqliad' => {ident => 't'}, + } + }; + + push(@{$where_clause->{'-or'}}, {attr_value => {ilike => "%$_%"}}) for @$attr_values; + + $where_clause->{'+jub'} = {state => {in => $li_states}} + if $li_states and @$li_states; + + $where_clause->{'+acqpo'} = {ordering_agency => $po_agencies} + if $po_agencies and @$po_agencies; + + $LI_ATTR_SEARCH->{where} = $where_clause; + + my $lis = $e->json_query($LI_ATTR_SEARCH); + + for my $li_id_obj (@$lis) { + my $li_id = $li_id_obj->{lineitem}; + if($$options{idlist}) { + $conn->respond($li_id); + } else { + my $li; + if($$options{flesh_attrs}) { + $li = $e->retrieve_acq_lineitem([ + $li_id, {flesh => 1, flesh_fields => {jub => ['attributes']}}]) + } else { + $li = $e->retrieve_acq_lineitem($li_id); + } + $li->clear_marc if $$options{clear_marc}; + $conn->respond($li); + } + } + return undef; +} + + +__PACKAGE__->register_method( + method => 'create_lineitem_detail', + api_name => 'open-ils.acq.lineitem_detail.create', + signature => { + desc => q/Creates a new purchase order line item detail. + Additionally creates the associated fund_debit/, + params => [ + {desc => 'Authentication token', type => 'string'}, + {desc => 'lineitem_detail to create', type => 'object'}, + ], + return => {desc => 'The purchase order line item detail id, Event on failure'} + } +); + +sub create_lineitem_detail { + my($self, $conn, $auth, $li_detail, $options) = @_; + my $e = new_editor(xact=>1, authtoken=>$auth); + return $e->die_event unless $e->checkauth; + $options ||= {}; + + my $li = $e->retrieve_acq_lineitem($li_detail->lineitem) + or return $e->die_event; + + my $evt = update_li_edit_time($e, $li); + return $evt if $evt; + + # XXX check lineitem provider perms + + if($li_detail->fund) { + my $fund = $e->retrieve_acq_fund($li_detail->fund) or return $e->die_event; + return $e->die_event unless + $e->allowed('MANAGE_FUND', $fund->org, $fund); + } + + $e->create_acq_lineitem_detail($li_detail) or return $e->die_event; + + unless($li_detail->barcode) { + my $pfx = $U->ou_ancestor_setting_value($li_detail->owning_lib, 'acq.tmp_barcode_prefix') || 'ACQ'; + $li_detail->barcode($pfx.$li_detail->id); + } + unless($li_detail->cn_label) { + my $pfx = $U->ou_ancestor_setting_value($li_detail->owning_lib, 'acq.tmp_callnumber_prefix') || 'ACQ'; + $li_detail->cn_label($pfx.$li_detail->id); + } + + if(my $loc = $U->ou_ancestor_setting_value($li_detail->owning_lib, 'acq.default_copy_location')) { + $li_detail->location($loc); + } + + $e->update_acq_lineitem_detail($li_detail) or return $e->die_event; + + $e->commit; + return $li_detail if $$options{return_obj}; + return $li_detail->id +} + +__PACKAGE__->register_method( + method => 'update_lineitem_detail', + api_name => 'open-ils.acq.lineitem_detail.update', + signature => { + desc => q/Updates a lineitem detail/, + params => [ + {desc => 'Authentication token', type => 'string'}, + {desc => 'lineitem_detail to update', type => 'object'}, + ], + return => {desc => '1 on success, Event on failure'} + } +); + +sub update_lineitem_detail { + my($self, $conn, $auth, $li_detail) = @_; + my $e = new_editor(xact=>1, authtoken=>$auth); + return $e->die_event unless $e->checkauth; + + if($li_detail->fund) { + my $fund = $e->retrieve_acq_fund($li_detail->fund) or return $e->die_event; + return $e->die_event unless + $e->allowed('MANAGE_FUND', $fund->org, $fund); + } + + # XXX check lineitem perms + + my $li = $e->retrieve_acq_lineitem($li_detail->lineitem) + or return $e->die_event; + my $evt = update_li_edit_time($e, $li); + return $evt if $evt; + + $e->update_acq_lineitem_detail($li_detail) or return $e->die_event; + $e->commit; + return 1; +} + +sub update_li_edit_time { + my ($e, $li) = @_; + # some lineitem edits are allowed after approval time... +# return OpenILS::Event->new('ACQ_LINEITEM_APPROVED', payload => $li->id) +# if $li->state eq 'approved'; + $li->edit_time('now'); + $e->update_acq_lineitem($li) or return $e->die_event; + return undef; +} + + +__PACKAGE__->register_method( + method => 'delete_lineitem_detail', + api_name => 'open-ils.acq.lineitem_detail.delete', + signature => { + desc => q/Deletes a lineitem detail/, + params => [ + {desc => 'Authentication token', type => 'string'}, + {desc => 'lineitem_detail ID to delete', type => 'number'}, + ], + return => {desc => '1 on success, Event on failure'} + } +); + +sub delete_lineitem_detail { + my($self, $conn, $auth, $li_detail_id) = @_; + my $e = new_editor(xact=>1, authtoken=>$auth); + return $e->die_event unless $e->checkauth; + my $li_detail = $e->retrieve_acq_lineitem_detail([ + $li_detail_id, + { flesh => 1, + flesh_fields => {acqlid => ['lineitem']} + } + ]) or return $e->die_event; + + my $li = $li_detail->lineitem; + + my $evt = update_li_edit_time($e, $li); + return $evt if $evt; + + return OpenILS::Event->new('BAD_PARAMS') unless + $li->state =~ /new|approved/; + + # XXX check lineitem perms + + $e->delete_acq_lineitem_detail($li_detail) or return $e->die_event; + $e->commit; + return 1; +} + + +__PACKAGE__->register_method( + method => 'retrieve_lineitem_detail', + api_name => 'open-ils.acq.lineitem_detail.retrieve', + signature => { + desc => q/Updates a lineitem detail/, + params => [ + {desc => 'Authentication token', type => 'string'}, + {desc => 'id of lineitem_detail to retrieve', type => 'number'}, + ], + return => {desc => 'object on success, Event on failure'} + } +); +sub retrieve_lineitem_detail { + my($self, $conn, $auth, $li_detail_id) = @_; + my $e = new_editor(authtoken=>$auth); + return $e->event unless $e->checkauth; + + my $li_detail = $e->retrieve_acq_lineitem_detail($li_detail_id) + or return $e->event; + + if($li_detail->fund) { + my $fund = $e->retrieve_acq_fund($li_detail->fund) or return $e->event; + return $e->event unless + $e->allowed('MANAGE_FUND', $fund->org, $fund); + } + + # XXX check lineitem perms + return $li_detail; +} + + + +__PACKAGE__->register_method( + method => 'approve_lineitem', + api_name => 'open-ils.acq.lineitem.approve', + signature => { + desc => 'Mark a lineitem as approved', + params => [ + {desc => 'Authentication token', type => 'string'}, + {desc => 'lineitem ID', type => 'number'} + ], + return => {desc => '1 on success, Event on error'} + } +); +sub approve_lineitem { + my($self, $conn, $auth, $li_id) = @_; + my $e = new_editor(xact=>1, authtoken=>$auth); + return $e->die_event unless $e->checkauth; + + # XXX perm checks for each lineitem detail + + my $li = $e->retrieve_acq_lineitem($li_id) + or return $e->die_event; + + return OpenILS::Event->new('ACQ_LINEITEM_APPROVED', payload => $li_id) + if $li->state eq 'approved'; + + my $details = $e->search_acq_lineitem_detail({lineitem => $li_id}); + return OpenILS::Event->new('ACQ_LINEITEM_NO_COPIES', payload => $li_id) + unless scalar(@$details) > 0; + + for my $detail (@$details) { + return OpenILS::Event->new('ACQ_LINEITEM_DETAIL_NO_FUND', payload => $detail->id) + unless $detail->fund; + + return OpenILS::Event->new('ACQ_LINEITEM_DETAIL_NO_ORG', payload => $detail->id) + unless $detail->owning_lib; + } + + $li->state('approved'); + $li->edit_time('now'); + $e->update_acq_lineitem($li) or return $e->die_event; + + $e->commit; + return 1; +} + + +__PACKAGE__->register_method( + method => 'receive_lineitem_detail', + api_name => 'open-ils.acq.lineitem_detail.receive', + signature => { + desc => 'Mark a lineitem_detail as received', + params => [ + {desc => 'Authentication token', type => 'string'}, + {desc => 'lineitem detail ID', type => 'number'} + ], + return => {desc => '1 on success, Event on error'} + } +); +sub receive_lineitem_detail { + my($self, $conn, $auth, $lid_id) = @_; + my $e = new_editor(xact=>1, authtoken=>$auth); + return $e->die_event unless $e->checkauth; + my $resp = receive_lineitem_detail_impl($e, $lid_id); + if($resp) {$e->rollback; return $resp;} + $e->commit; + return 1; +} + +sub receive_lineitem_detail_impl { + my($e, $lid_id) = @_; + + my $lid = $e->retrieve_acq_lineitem_detail([ + $lid_id, + { flesh => 1, + flesh_fields => { + acqlid => ['fund_debit'] + } + } + ]) or return $e->die_event; + + return OpenILS::Event->new( + 'ACQ_LINEITEM_DETAIL_RECEIVED') if $lid->recv_time; + + $lid->recv_time('now'); + $e->update_acq_lineitem_detail($lid) or return $e->die_event; + + my $copy = $e->retrieve_asset_copy($lid->eg_copy_id) + or return $e->die_event; + + $copy->status(OILS_COPY_STATUS_IN_PROCESS); + $copy->edit_date('now'); + $copy->editor($e->requestor->id); + $e->update_asset_copy($copy) or return $e->die_event; + + if($lid->fund_debit) { + $lid->fund_debit->encumbrance('f'); + $e->update_acq_fund_debit($lid->fund_debit) or return $e->die_event; + } + + # ------------------------------------------------------------- + # if all of the lineitem details for this lineitem have + # been received, mark the lineitem as received + # ------------------------------------------------------------- + my $non_recv = $e->search_acq_lineitem_detail( + {recv_time => undef, lineitem => $lid->lineitem}, {idlist=>1}); + + return undef if @$non_recv; + + my $li = $e->retrieve_acq_lineitem($lid->lineitem); + $li->state('received'); + $li->edit_time('now'); + $e->update_acq_lineitem($li) or return $e->die_event; + + # ------------------------------------------------------------- + # if all of the lineitems for this PO are received, + # mark the PO as received + # ------------------------------------------------------------- + my $non_recv_li = $e->search_acq_lineitem( + { purchase_order => $li->purchase_order, + state => {'!=' => 'received'} + }, {idlist=>1}); + + return undef if @$non_recv_li; + + my $po = $e->retrieve_acq_purchase_order($li->purchase_order); + $po->state('received'); + $po->edit_time('now'); + $e->update_acq_purchase_order($po) or return $e->die_event; + + return undef; +} + + +__PACKAGE__->register_method( + method => 'set_lineitem_attr', + api_name => 'open-ils.acq.lineitem_usr_attr.set', + signature => { + desc => 'Sets a lineitem_usr_attr value', + params => [ + {desc => 'Authentication token', type => 'string'}, + {desc => 'Lineitem ID', type => 'number'}, + {desc => 'Attr name', type => 'string'}, + {desc => 'Attr value', type => 'string'} + ], + return => {desc => '1 on success, Event on error'} + } +); + +__PACKAGE__->register_method( + method => 'set_lineitem_attr', + api_name => 'open-ils.acq.lineitem_local_attr.set', + signature => { + desc => 'Sets a lineitem_local_attr value', + params => [ + {desc => 'Authentication token', type => 'string'}, + {desc => 'Lineitem ID', type => 'number'}, + {desc => 'Attr name', type => 'string'}, + {desc => 'Attr value', type => 'string'} + ], + return => {desc => 'ID of the attr object on success, Event on error'} + } +); + + +sub set_lineitem_attr { + my($self, $conn, $auth, $li_id, $attr_name, $attr_value) = @_; + my $e = new_editor(xact=>1, authtoken=>$auth); + return $e->die_event unless $e->checkauth; + + # XXX perm + + my $attr_type = $self->api_name =~ /local_attr/ ? + 'lineitem_local_attr_definition' : 'lineitem_usr_attr_definition'; + + my $attr = $e->search_acq_lineitem_attr({ + lineitem => $li_id, + attr_type => $attr_type, + attr_name => $attr_name})->[0]; + + my $find = "search_acq_$attr_type"; + + if($attr) { + $attr->attr_value($attr_value); + $e->update_acq_lineitem_attr($attr) or return $e->die_event; + } else { + $attr = Fieldmapper::acq::lineitem_attr->new; + $attr->lineitem($li_id); + $attr->attr_type($attr_type); + $attr->attr_name($attr_name); + $attr->attr_value($attr_value); + + my $attr_def_id = $e->$find({code => $attr_name}, {idlist=>1})->[0] + or return $e->die_event; + $attr->definition($attr_def_id); + $e->create_acq_lineitem_attr($attr) or return $e->die_event; + } + + $e->commit; + return $attr->id; +} + +__PACKAGE__->register_method( + method => 'get_lineitem_attr_defs', + api_name => 'open-ils.acq.lineitem_attr_definition.retrieve.all', + signature => { + desc => 'Retrieve lineitem attr definitions', + params => [ + {desc => 'Authentication token', type => 'string'}, + ], + return => {desc => 'List of attr definitions'} + } +); + +sub get_lineitem_attr_defs { + my($self, $conn, $auth) = @_; + my $e = new_editor(authtoken=>$auth); + return $e->event unless $e->checkauth; + my %results; + for my $type (qw/generated marc local usr provider/) { + my $call = "retrieve_all_acq_lineitem_${type}_attr_definition"; + $results{$type} = $e->$call; + } + return \%results; +} + + +1; diff --git a/Open-ILS/src/perlmods/OpenILS/Application/Acq/Picklist.pm b/Open-ILS/src/perlmods/OpenILS/Application/Acq/Picklist.pm new file mode 100644 index 0000000000..3176133a45 --- /dev/null +++ b/Open-ILS/src/perlmods/OpenILS/Application/Acq/Picklist.pm @@ -0,0 +1,461 @@ +package OpenILS::Application::Acq::Picklist; +use base qw/OpenILS::Application/; +use strict; use warnings; + +use OpenSRF::Utils::Logger qw(:logger); +use OpenILS::Utils::Fieldmapper; +use OpenILS::Utils::CStoreEditor q/:funcs/; +use OpenILS::Const qw/:const/; +use OpenSRF::Utils::SettingsClient; +use OpenILS::Event; +use OpenILS::Application::AppUtils; +my $U = 'OpenILS::Application::AppUtils'; + + +__PACKAGE__->register_method( + method => 'create_picklist', + api_name => 'open-ils.acq.picklist.create', + signature => { + desc => 'Creates a new picklist', + params => [ + {desc => 'Authentication token', type => 'string'}, + {desc => 'Picklist object to create', type => 'object'} + ], + return => {desc => 'The ID of the new picklist'} + } +); + +sub create_picklist { + my($self, $conn, $auth, $picklist) = @_; + my $e = new_editor(xact=>1, authtoken=>$auth); + return $e->die_event unless $e->checkauth; + $picklist->org_unit($e->requestor->ws_ou) unless $picklist->org_unit; + return $e->die_event unless $e->allowed('CREATE_PICKLIST', $picklist->org_unit); + return OpenILS::Event->new('BAD_PARAMS') + unless $e->requestor->id == $picklist->owner; + $e->create_acq_picklist($picklist) or return $e->die_event; + $e->commit; + return $picklist->id; +} + + +__PACKAGE__->register_method( + method => 'update_picklist', + api_name => 'open-ils.acq.picklist.update', + signature => { + desc => 'Updates a new picklist', + params => [ + {desc => 'Authentication token', type => 'string'}, + {desc => 'Picklist object to update', type => 'object'} + ], + return => {desc => '1 on success, Event on error'} + } +); + +sub update_picklist { + my($self, $conn, $auth, $picklist) = @_; + my $e = new_editor(xact=>1, authtoken=>$auth); + return $e->die_event unless $e->checkauth; + + # don't let them change the owner + my $o_picklist = $e->retrieve_acq_picklist($picklist->id) + or return $e->die_event; + if($o_picklist->owner != $e->requestor->id) { + return $e->die_event unless + $e->allowed('UPDATE_PICKLIST', $o_picklist->org_unit); + } + return OpenILS::Event->new('BAD_PARAMS') unless $o_picklist->org_unit == $picklist->org_unit; + + $e->update_acq_picklist($picklist) or return $e->die_event; + $e->commit; + return 1; +} + +__PACKAGE__->register_method( + method => 'retrieve_picklist', + api_name => 'open-ils.acq.picklist.retrieve', + signature => { + desc => 'Retrieves a picklist', + params => [ + {desc => 'Authentication token', type => 'string'}, + {desc => 'Picklist ID to retrieve', type => 'number'}, + {desc => 'Options hash, including "flesh_lineitem_count" to get the count of attached entries', type => 'hash'}, + ], + return => {desc => 'Picklist object on success, Event on error'} + } +); + +sub retrieve_picklist { + my($self, $conn, $auth, $picklist_id, $options) = @_; + my $e = new_editor(authtoken=>$auth); + return $e->event unless $e->checkauth; + + my $picklist = $e->retrieve_acq_picklist($picklist_id) + or return $e->event; + + $picklist->entry_count(retrieve_lineitem_count($e, $picklist_id)) + if $$options{flesh_lineitem_count}; + + if($e->requestor->id != $picklist->owner) { + return $e->event unless + $e->allowed('VIEW_PICKLIST', $picklist->org_unit, $picklist); + } + + $picklist->owner($e->retrieve_actor_user($picklist->owner)->usrname) + if($$options{flesh_username}); + + return $picklist; +} + + +# Returns the number of entries associated with this picklist +sub retrieve_lineitem_count { + my($e, $picklist_id) = @_; + my $count = $e->json_query({ + select => { + jub => [{transform => 'count', column => 'id', alias => 'count'}] + }, + from => 'jub', + where => {picklist => $picklist_id}} + ); + return $count->[0]->{count}; +} + + + +__PACKAGE__->register_method( + method => 'retrieve_picklist_name', + api_name => 'open-ils.acq.picklist.name.retrieve', + signature => { + desc => 'Retrieves a picklist by name. Owner is implied by the caller', + params => [ + {desc => 'Authentication token', type => 'string'}, + {desc => 'Picklist name to retrieve', type => 'strin'}, + ], + return => {desc => 'Picklist object on success, null on not found'} + } +); + +sub retrieve_picklist_name { + my($self, $conn, $auth, $name) = @_; + my $e = new_editor(authtoken=>$auth); + return $e->event unless $e->checkauth; + my $picklist = $e->search_acq_picklist( + {name => $name, owner => $e->requestor->id})->[0]; + if($e->requestor->id != $picklist->owner) { + return $e->event unless + $e->allowed('VIEW_PICKLIST', $picklist->org_unit, $picklist); + } + return $picklist; +} + + + +__PACKAGE__->register_method( + method => 'retrieve_user_picklist', + api_name => 'open-ils.acq.picklist.user.retrieve', + stream => 1, + signature => { + desc => 'Retrieves a user\'s picklists', + params => [ + {desc => 'Authentication token', type => 'string'}, + {desc => 'Options, including "idlist", whch forces the return + of a list of IDs instead of objects', type => 'hash'}, + ], + return => {desc => 'Picklist object on success, Event on error'} + } +); + +sub retrieve_user_picklist { + my($self, $conn, $auth, $options) = @_; + my $e = new_editor(authtoken=>$auth); + return $e->die_event unless $e->checkauth; + + # don't grab the PL with name == "", because that is the designated temporary picklist + my $list = $e->search_acq_picklist( + {owner=>$e->requestor->id, name=>{'!='=>''}}, + {idlist=>1} + ); + + for my $id (@$list) { + if($$options{idlist}) { + $conn->respond($id); + } else { + my $pl = $e->retrieve_acq_picklist($id); + $pl->entry_count(retrieve_lineitem_count($e, $id)) if $$options{flesh_lineitem_count}; + $pl->owner($e->retrieve_actor_user($pl->owner)->usrname) if $$options{flesh_username}; + $conn->respond($pl); + } + } + + return undef; +} + + +__PACKAGE__->register_method( + method => 'retrieve_all_user_picklist', + api_name => 'open-ils.acq.picklist.user.all.retrieve', + stream => 1, + signature => { + desc => 'Retrieves all of the picklists a user is allowed to see', + params => [ + {desc => 'Authentication token', type => 'string'}, + {desc => 'Options, including "idlist", whch forces the return + of a list of IDs instead of objects', type => 'hash'}, + ], + return => {desc => 'Picklist objects on success, Event on error'} + } +); + +sub retrieve_all_user_picklist { + my($self, $conn, $auth, $options) = @_; + my $e = new_editor(authtoken=>$auth); + return $e->event unless $e->checkauth; + + my $my_list = $e->search_acq_picklist( + {owner=>$e->requestor->id, name=>{'!='=>''}}, {idlist=>1}); + + my $picklist_ids = $e->objects_allowed('VIEW_PICKLIST', 'acqpl'); + my $p_orgs = $U->find_highest_work_orgs($e, 'VIEW_PICKLIST', {descendants =>1}); + my $picklist_ids_2 = $e->search_acq_picklist( + {name=>{'!='=>''}, org_unit => $p_orgs}, {idlist=>1}); + + return undef unless @$my_list or @$picklist_ids or @$picklist_ids_2; + + my @list = (@$my_list, @$picklist_ids, @$picklist_ids_2); + my %dedup; + $dedup{$_} = 1 for @list; + @list = keys %dedup; + + return \@list if $$options{idlist}; + + for my $pl (@list) { + my $picklist = $e->retrieve_acq_picklist($pl) or return $e->event; + $picklist->entry_count(retrieve_lineitem_count($e, $picklist->id)) + if($$options{flesh_lineitem_count}); + $picklist->owner($e->retrieve_actor_user($picklist->owner)->usrname) + if $$options{flesh_username}; + $conn->respond($picklist); + } + + return undef; +} + + +__PACKAGE__->register_method( + method => 'delete_picklist', + api_name => 'open-ils.acq.picklist.delete', + signature => { + desc => q/Deletes a picklist. It also deletes any lineitems in the "new" state. + Other attached lineitems are detached'/, + params => [ + {desc => 'Authentication token', type => 'string'}, + {desc => 'Picklist ID to delete', type => 'number'} + ], + return => {desc => '1 on success, Event on error'} + } +); + +sub delete_picklist { + my($self, $conn, $auth, $picklist_id) = @_; + my $e = new_editor(xact=>1, authtoken=>$auth); + return $e->die_event unless $e->checkauth; + + my $picklist = $e->retrieve_acq_picklist($picklist_id) + or return $e->die_event; + # don't let anyone delete someone else's picklist + if($picklist->owner != $e->requestor->id) { + return $e->die_event unless + $e->allowed('DELETE_PICKLIST', $picklist->org_unit, $picklist); + } + + # delete all 'new' lineitems + my $lis = $e->search_acq_lineitem({picklist => $picklist->id, state => 'new'}); + for my $li (@$lis) { + $e->delete_acq_lineitem($li) or return $e->die_event; + } + + # detach all non-'new' lineitems + $lis = $e->search_acq_lineitem({picklist => $picklist->id, state => {'!=' => 'new'}}); + for my $li (@$lis) { + $li->clear_picklist; + $e->update_acq_lineitem($li) or return $e->die_event; + } + + # remove any picklist-specific object perms + my $ops = $e->search_permission_usr_object_perm_map({object_type => 'acqpl', object_id => $picklist->id}); + for my $op (@$ops) { + $e->delete_usr_object_perm_map($op) or return $e->die_event; + } + + + $e->delete_acq_picklist($picklist) or return $e->die_event; + $e->commit; + return 1; +} + +__PACKAGE__->register_method( + method => 'retrieve_pl_lineitem', + api_name => 'open-ils.acq.lineitem.picklist.retrieve', + stream => 1, + signature => { + desc => 'Retrieves lineitem objects according to picklist', + params => [ + {desc => 'Authentication token', type => 'string'}, + {desc => 'Picklist ID whose entries to retrieve', type => 'number'}, + {desc => q/Options, including + "sort_attr", which defines the attribute to sort on; + "sort_attr_type", which defines the attribute type sort on; + "sort_dir", which defines the sort order between "asc" and "desc"; + "limit", retrieval limit; + "offset", retrieval offset; + "idlist", return a list of IDs instead of objects + "flesh_attrs", additionaly return the list of flattened attributes + "clear_marc", discards the raw MARC data to reduce data size + /, + type => 'hash'} + ], + return => {desc => 'Array of lineitem objects or IDs, on success, Event on error'} + } +); + + +my $PL_ENTRY_JSON_QUERY = { + select => {jub => ["id"], "acqlia" => ["attr_value"]}, + "from" => { + "jub" => { + "acqlia" => { + "fkey" => "id", + "field" => "lineitem", + "type" => "left", + "filter" => { + "attr_type" => "lineitem_marc_attr_definition", + "attr_name" => "author" + } + } + } + }, + "order_by" => {"acqlia" => {"attr_value" => {"direction"=>"asc"}}}, + "limit" => 10, + "where" => {"+jub" => {"picklist"=>2}}, + "offset" => 0 +}; + +sub retrieve_pl_lineitem { + my($self, $conn, $auth, $picklist_id, $options) = @_; + my $e = new_editor(authtoken=>$auth); + return $e->event unless $e->checkauth; + + # collect the retrieval options + my $sort_attr = $$options{sort_attr} || 'title'; + my $sort_attr_type = $$options{sort_attr_type} || 'lineitem_marc_attr_definition'; + my $sort_dir = $$options{sort_dir} || 'asc'; + my $limit = $$options{limit} || 10; + my $offset = $$options{offset} || 0; + + $PL_ENTRY_JSON_QUERY->{where}->{'+jub'}->{picklist} = $picklist_id; + $PL_ENTRY_JSON_QUERY->{from}->{jub}->{acqlia}->{filter}->{attr_name} = $sort_attr; + $PL_ENTRY_JSON_QUERY->{from}->{jub}->{acqlia}->{filter}->{attr_type} = $sort_attr_type; + $PL_ENTRY_JSON_QUERY->{order_by}->{acqlia}->{attr_value}->{direction} = $sort_dir; + $PL_ENTRY_JSON_QUERY->{limit} = $limit; + $PL_ENTRY_JSON_QUERY->{offset} = $offset; + + my $entries = $e->json_query($PL_ENTRY_JSON_QUERY); + + my @ids; + push(@ids, $_->{id}) for @$entries; + + for my $id (@ids) { + if($$options{idlist}) { + $conn->respond($id); + next; + } + + my $entry; + my $flesh = ($$options{flesh_attrs}) ? + {flesh => 1, flesh_fields => {jub => ['attributes']}} : {}; + + $entry = $e->retrieve_acq_lineitem([$id, $flesh]); + my $details = $e->search_acq_lineitem_detail({lineitem => $id}, {idlist=>1}); + $entry->item_count(scalar(@$details)); + $entry->clear_marc if $$options{clear_marc}; + $conn->respond($entry); + } + + return undef; +} + +=head comment +request open-ils.cstore open-ils.cstore.json_query.atomic {"select":{"jub":[{"transform":"count", "attregate":1, "column":"id","alias":"count"}]}, "from":"jub","where":{"picklist":1}} +=cut + +__PACKAGE__->register_method( + method => 'zsearch', + api_name => 'open-ils.acq.picklist.search.z3950', + stream => 1, + signature => { + desc => 'Performs a z3950 federated search and creates a picklist and associated lineitems', + params => [ + {desc => 'Authentication token', type => 'string'}, + {desc => 'Search definition', type => 'object'}, + {desc => 'Picklist name, optional', type => 'string'}, + ] + } +); + +sub zsearch { + my($self, $conn, $auth, $search, $name) = @_; + my $e = new_editor(authtoken=>$auth, xact=>1); + return $e->die_event unless $e->checkauth; + return $e->die_event unless $e->allowed('CREATE_PICKLIST'); + + $search->{limit} ||= 10; + + $name ||= ''; + my $picklist = $e->search_acq_picklist({owner=>$e->requestor->id, name=>$name})->[0]; + if($name eq '' and $picklist) { + my $evt = delete_picklist($self, $conn, $auth, $picklist->id); + return $evt unless $evt == 1; + $picklist = undef; + } + + unless($picklist) { + $picklist = Fieldmapper::acq::picklist->new; + $picklist->owner($e->requestor->id); + $picklist->name($name); + $picklist->org_unit($e->requestor->ws_ou); + $e->create_acq_picklist($picklist) or return $e->die_event; + } + + my $ses = OpenSRF::AppSession->create('open-ils.search'); + my $req = $ses->request('open-ils.search.z3950.search_class', $auth, $search); + + while(my $resp = $req->recv(timeout=>60)) { + + my $result = $resp->content; + #use Data::Dumper; + #$logger->info("results = ".Dumper($resp)); + my $count = $result->{count}; + my $total = (($count < $search->{limit}) ? $count : $search->{limit})+1; + my $ctr = 0; + $conn->respond({total=>$total, progress=>++$ctr}); + + for my $rec (@{$result->{records}}) { + my $li = Fieldmapper::acq::lineitem->new; + $li->picklist($picklist->id); + $li->source_label($result->{service}); + $li->selector($e->requestor->id); + $li->marc($rec->{marcxml}); + $li->eg_bib_id($rec->{bibid}) if $rec->{bibid}; + $e->create_acq_lineitem($li) or return $e->die_event; + $conn->respond({total=>$total, progress=>++$ctr}); + } + } + + $e->commit; + return {complete=>1, picklist_id=>$picklist->id}; +} + + + +1; diff --git a/Open-ILS/src/perlmods/OpenILS/Application/Acq/Provider.pm b/Open-ILS/src/perlmods/OpenILS/Application/Acq/Provider.pm new file mode 100644 index 0000000000..6f22eb15bb --- /dev/null +++ b/Open-ILS/src/perlmods/OpenILS/Application/Acq/Provider.pm @@ -0,0 +1,183 @@ +package OpenILS::Application::Acq::Provider; +use base qw/OpenILS::Application/; +use strict; use warnings; + +use OpenILS::Event; +use OpenILS::Const qw/:const/; +use OpenSRF::Utils::Logger qw(:logger); +use OpenILS::Utils::Fieldmapper; +use OpenILS::Utils::CStoreEditor q/:funcs/; +use OpenSRF::Utils::SettingsClient; +use OpenILS::Application::AppUtils; + +my $U = 'OpenILS::Application::AppUtils'; + +__PACKAGE__->register_method( + method => 'create_provider', + api_name => 'open-ils.acq.provider.create', + signature => { + desc => 'Creates a new provider', + params => [ + {desc => 'Authentication token', type => 'string'}, + {desc => 'provider object to create', type => 'object'} + ], + return => {desc => 'The ID of the new provider'} + } +); + +sub create_provider { + my($self, $conn, $auth, $provider) = @_; + my $e = new_editor(authtoken=>$auth, xact=>1); + return $e->die_event unless $e->checkauth; + return $e->die_event unless $e->allowed('ADMIN_PROVIDER', $provider->owner); + $e->create_acq_provider($provider) or return $e->die_event; + $e->commit; + return $provider->id; +} + + + +__PACKAGE__->register_method( + method => 'retrieve_provider', + api_name => 'open-ils.acq.provider.retrieve', + signature => { + desc => 'Retrieves a new provider', + params => [ + {desc => 'Authentication token', type => 'string'}, + {desc => 'provider ID', type => 'number'} + ], + return => {desc => 'The provider object on success, Event on failure'} + } +); + +sub retrieve_provider { + my($self, $conn, $auth, $provider_id) = @_; + my $e = new_editor(authtoken=>$auth); + return $e->event unless $e->checkauth; + my $provider = $e->retrieve_acq_provider($provider_id) or return $e->event; + return $e->event unless $e->allowed( + ['ADMIN_PROVIDER', 'MANAGE_PROVIDER', 'VIEW_PROVIDER'], $provider->owner, $provider); + return $provider; +} + + +__PACKAGE__->register_method( + method => 'retrieve_org_providers', + api_name => 'open-ils.acq.provider.org.retrieve', + stream => 1, + signature => { + desc => 'Retrieves all the providers associated with an org unit that the requestor has access to see', + params => [ + {desc => 'Authentication token', type => 'string'}, + {desc => 'List of org Unit IDs. If no IDs are provided, this method returns the + full set of funding sources this user has permission to view', type => 'number'}, + {desc => q/Limiting permission. this permission is used find the work-org tree from which + the list of orgs is generated if no org ids are provided. + The default is ADMIN_PROVIDER/, type => 'string'}, + ], + return => {desc => 'The provider objects on success, empty array otherwise'} + } +); + +sub retrieve_org_providers { + my($self, $conn, $auth, $org_id_list, $options) = @_; + my $e = new_editor(authtoken=>$auth); + return $e->event unless $e->checkauth; + + my $limit_perm = ($$options{limit_perm}) ? $$options{limit_perm} : 'ADMIN_PROVIDER'; + + return OpenILS::Event->new('BAD_PARAMS') + unless $limit_perm =~ /(ADMIN|MANAGE|VIEW)_PROVIDER/; + + my $org_ids = ($org_id_list and @$org_id_list) ? $org_id_list : + $U->find_highest_work_orgs($e, $limit_perm, {descendants =>1}); + + return [] unless @$org_ids; + $conn->respond($_) for @{$e->search_acq_provider({owner => $org_ids})}; + + return undef; +} + +__PACKAGE__->register_method( + method => 'retrieve_provider_attr_def', + api_name => 'open-ils.acq.lineitem_provider_attr_definition.provider.retrieve', + stream => 1, + signature => { + desc => 'Retrieves all of the lineitem_provider_attr_definition for a given provider', + params => [ + {desc => 'Authentication token', type => 'string'}, + {desc => 'Provider ID', type => 'number'} + ], + return => {desc => 'Streams a of lineitem_provider_attr_definition objects'} + } +); + +sub retrieve_provider_attr_def { + my($self, $conn, $auth, $prov_id) = @_; + my $e = new_editor(authtoken=>$auth); + return $e->event unless $e->checkauth; + my $provider = $e->retrieve_acq_provider($prov_id) + or return $e->event; + return $e->event unless $e->allowed('ADMIN_PROVIDER', $provider->owner); + for my $id (@{$e->search_acq_lineitem_provider_attr_definition({provider=>$prov_id},{idlist=>1})}) { + $conn->respond($e->retrieve_acq_lineitem_provider_attr_definition($id)); + } + + return undef; +} + +__PACKAGE__->register_method( + method => 'create_provider_attr_def', + api_name => 'open-ils.acq.lineitem_provider_attr_definition.create', + signature => { + desc => 'Retrieves all of the lineitem_provider_attr_definition for a given provider', + params => [ + {desc => 'Authentication token', type => 'string'}, + {desc => 'Provider ID', type => 'number'} + ], + return => {desc => 'Streams a of lineitem_provider_attr_definition objects'} + } +); + +sub create_provider_attr_def { + my($self, $conn, $auth, $attr_def) = @_; + my $e = new_editor(authtoken=>$auth, xact=>1); + return $e->die_event unless $e->checkauth; + my $provider = $e->retrieve_acq_provider($attr_def->provider) + or return $e->die_event; + return $e->die_event unless $e->allowed('ADMIN_PROVIDER', $provider->owner); + $e->create_acq_lineitem_provider_attr_definition($attr_def) + or return $e->die_event; + $e->commit; + return $attr_def->id; +} + +__PACKAGE__->register_method( + method => 'delete_provider_attr_def', + api_name => 'open-ils.acq.lineitem_provider_attr_definition.delete', + signature => { + desc => 'Deletes a lineitem_provider_attr_definition', + params => [ + {desc => 'Authentication token', type => 'string'}, + {desc => 'ID', type => 'number'} + ], + return => {desc => '1 on success, event on failure'} + } +); + +sub delete_provider_attr_def { + my($self, $conn, $auth, $id) = @_; + my $e = new_editor(authtoken=>$auth, xact=>1); + return $e->die_event unless $e->checkauth; + my $attr_def = $e->retrieve_acq_lineitem_provider_attr_definition($id) + or return $e->die_event; + my $provider = $e->retrieve_acq_provider($attr_def->provider) + or return $e->die_event; + return $e->die_event unless $e->allowed('ADMIN_PROVIDER', $provider->owner); + $e->delete_acq_lineitem_provider_attr_definition($attr_def) + or return $e->die_event; + $e->commit; + return 1; +} + +1; diff --git a/Open-ILS/src/perlmods/OpenILS/Const.pm b/Open-ILS/src/perlmods/OpenILS/Const.pm index 145060a8b0..687640b518 100644 --- a/Open-ILS/src/perlmods/OpenILS/Const.pm +++ b/Open-ILS/src/perlmods/OpenILS/Const.pm @@ -97,6 +97,9 @@ econst OILS_BILLING_TYPE_DEPOSIT => 'System: Deposit'; econst OILS_BILLING_TYPE_RENTAL => 'System: Rental'; econst OILS_BILLING_NOTE_SYSTEM => 'SYSTEM GENERATED'; +econst OILS_ACQ_DEBIT_TYPE_PURCHASE => 'purchase'; +econst OILS_ACQ_DEBIT_TYPE_TRANSFER => 'xfer'; + # --------------------------------------------------------------------- diff --git a/Open-ILS/src/perlmods/OpenILS/Utils/MFHD.pm b/Open-ILS/src/perlmods/OpenILS/Utils/MFHD.pm new file mode 100755 index 0000000000..98a1d1e485 --- /dev/null +++ b/Open-ILS/src/perlmods/OpenILS/Utils/MFHD.pm @@ -0,0 +1,58 @@ +package MFHD; +use strict; +use integer; +use Carp; + +use MARC::Record; +use MFHD::Caption; +use MFHD::Holding; + +sub new { + my $proto = shift; + my $class = ref($proto) || $proto; + my $self = {}; + my $rec = shift; + + $self->{CAPTIONS} = {}; + foreach my $caption ($rec->field('853')) { + my $cap_id; + $cap_id = $caption->subfield('8') || '0'; + if (exists $self->{CAPTIONS}->{$cap_id}) { + carp "Multiple unlabelled MFHD captions"; + } + $self->{CAPTIONS}->{$cap_id} = new MFHD::Caption($caption); + } + + $self->{HOLDINGS} = {}; + foreach my $holding ($rec->field('863')) { + my $linkage; + my ($link_id, $seqno); + + $linkage = $holding->subfield('8'); + ($link_id, $seqno) = split(/\./, $linkage); + + if (!exists $self->{HOLDINGS}->{$link_id}) { + $self->{HOLDINGS}->{$link_id} = {}; + } + $self->{HOLDINGS}->{$link_id}->{$seqno} = + new MFHD::Holding($seqno, $holding, $self->{CAPTIONS}->{$link_id}); + } + + bless ($self, $class); + return $self; +} + +sub captions { + my $self = shift; + + return sort keys %{$self->{CAPTIONS}} +} + +sub holdings { + my $self = shift; + my $capid = shift; + + return sort {$a->{SEQNO} cmp $b->{SEQNO}} values %{$self->{HOLDINGS}->{$capid}}; +} + +1; diff --git a/Open-ILS/src/perlmods/OpenILS/Utils/MFHD/Caption.pm b/Open-ILS/src/perlmods/OpenILS/Utils/MFHD/Caption.pm new file mode 100755 index 0000000000..b1abc20f54 --- /dev/null +++ b/Open-ILS/src/perlmods/OpenILS/Utils/MFHD/Caption.pm @@ -0,0 +1,78 @@ +package MFHD::Caption; +use strict; +use integer; +use Carp; + +use MARC::Record; + +sub new +{ + my $proto = shift; + my $class = ref($proto) || $proto; + my $caption = shift; + my $self = {}; + my $last_enum = undef; + + $self->{CAPTION} = $caption; + $self->{ENUMS} = {}; + $self->{CHRONS} = {}; + $self->{PATTERN} = {}; + $self->{COPY} = undef; + $self->{UNIT} = undef; + + foreach my $subfield ($caption->subfields) { + my ($key, $val) = @$subfield; + if ($key eq '8') { + $self->{LINK} = $val; + } elsif ($key =~ /[a-h]/) { + # Enumeration Captions + $self->{ENUMS}->{$key} = {CAPTION => $val, + COUNT => undef, + RESTART => undef}; + if ($key =~ /[ag]/) { + $last_enum = undef; + } else { + $last_enum = $key; + } + } elsif ($key =~ /[i-m]/) { + # Chronology captions + $self->{CHRONS}->{$key} = $val; + } elsif ($key eq 'u') { + # Bib units per next higher enumeration level + carp('$u specified for top-level enumeration') + unless defined($last_enum); + $self->{ENUMS}->{$last_enum}->{COUNT} = $val; + } elsif ($key eq 'v') { + carp '$v specified for top-level enumeration' + unless defined($last_enum); + $self->{ENUMS}->{$last_enum}->{RESTART} = ($val eq 'r'); + } elsif ($key =~ /[npw-z]/) { + # Publication Pattern ('o' == type of unit, 'q'..'t' undefined) + $self->{PATTERN}->{$key} = $val; + } elsif ($key eq 'o') { + # Type of unit + $self->{UNIT} = $val; + } elsif ($key eq 't') { + $self->{COPY} = $val; + } else { + carp "Unknown caption subfield '$key'"; + } + } + + bless ($self, $class); + return $self; +} + +sub caption { + my $self = shift; + my $key; + + if (@_) { + $key = shift; + return $self->{ENUMS}->{$key}->{CAPTION}; + } else { + return $self->{CAPTION}; + } +} + +1; diff --git a/Open-ILS/src/perlmods/OpenILS/Utils/MFHD/Holding.pm b/Open-ILS/src/perlmods/OpenILS/Utils/MFHD/Holding.pm new file mode 100755 index 0000000000..0ce0fe008d --- /dev/null +++ b/Open-ILS/src/perlmods/OpenILS/Utils/MFHD/Holding.pm @@ -0,0 +1,90 @@ +package MFHD::Holding; +use strict; +use integer; +use Carp; + +use MARC::Record; + +sub new { + my $proto = shift; + my $class = ref($proto) || $proto; + my $seqno = shift; + my $holding = shift; + my $caption = shift; + my $self = {}; + my $last_enum = undef; + + $self->{SEQNO} = $seqno; + $self->{HOLDING} = $holding; + $self->{CAPTION} = $caption; + $self->{ENUMS} = {}; + $self->{CHRON} = {}; + $self->{DESCR} = {}; + $self->{COPY} = undef; + $self->{BREAK} = undef; + $self->{NOTES} = {}; + $self->{COPYRIGHT} = []; + + foreach my $subfield ($holding->subfields) { + my ($key, $val) = @$subfield; + if ($key =~ /[a-h]/) { + # Enumeration details of holdings + $self->{ENUMS}->{$key} = {HOLDINGS => $val, + UNIT => undef,}; + $last_enum = $key; + } elsif ($key =~ /[i-m]/) { + $self->{CHRON}->{$key} = $val; + if (!exists $caption->{CHRONS}->{$key}) { + carp "Holding specified enumeration level '$key' not included in caption $caption->{LINK}"; + } + } elsif ($key eq 'o') { + carp '$o specified prior to first enumeration' + unless defined($last_enum); + $self->{ENUMS}->{$last_enum}->{UNIT} = $val; + $last_enum = undef; + } elsif ($key =~ /[npq]/) { + $self->{DESCR}->{$key} = $val; + } elsif ($key eq 's') { + push @{$self->{COPYRIGHT}}, $val; + } elsif ($key eq 't') { + $self->{COPY} = $val; + } elsif ($key eq 'w') { + carp "Unrecognized break indicator '$val'" + unless $val =~ /^[gn]$/; + $self->{BREAK} = $val; + } + } + + bless ($self, $class); + return $self; +} + +sub format { + my $self = shift; + my $caption = $self->{CAPTION}; + my $str = ""; + + foreach my $key ('a'..'f') { + last if !exists $caption->{ENUMS}->{$key}; +# printf("fmt %s: '%s'\n", $key, $caption->caption($key)); + + $str .= ($key eq 'a' ? "" : ':') . $caption->caption($key) . $self->{ENUMS}->{$key}->{HOLDINGS}; + } + + if (exists $caption->{ENUMS}->{'g'}) { + # There's at least one level of alternative enumeration + $str .= ' '; + foreach my $key ('g', 'h') { + $str .= ($key eq 'g' ? '' : ':') . $caption->enum($key) . $self->{ENUMS}->{$key}->{HOLDINGS}; + } + } + + return $str; +} + +sub next { + my $self = shift; + my $caption = $self->{CAPTION}; +} + +1; diff --git a/Open-ILS/src/perlmods/OpenILS/Utils/MFHDParser.pm b/Open-ILS/src/perlmods/OpenILS/Utils/MFHDParser.pm new file mode 100644 index 0000000000..5995de911d --- /dev/null +++ b/Open-ILS/src/perlmods/OpenILS/Utils/MFHDParser.pm @@ -0,0 +1,168 @@ +#!/usr/bin/perl -w +use strict; +use Date::Manip; + +# Parse MFHD patterns for http://www.loc.gov/marc/holdings/hd853855.html + +# Primary goal: +# Expected input: a chunk of MFHD, a start date, and # of issuances to project +# Expected output: a set of issuances projected forward from the start date, +# with issue/volume/dates/captions conforming to what the MFHD actually says + +# The thought had been to use Date::Manip to generate the actual dates for +# each issuance, like: +# +# # To find the 2nd Tuesday of every month +# @date = ParseRecur("0:1*2:2:0:0:0",$base,$start,$stop); + +# Secondary goal: generate automatic summary holdings +# (a la http://www.loc.gov/marc/holdings/hd863865.html) + +# Compressability comes from first indicator +sub parse_compressability { + my $c = shift || return undef; + + my %compressability = ( + '0' => 'Cannot compress or expand', + '1' => 'Can compress but cannot expand', + '2' => 'Can compress or expand', + '3' => 'Unknown', + '#' => 'Undefined' + ); + + if (exists $compressability{$c}) { + return $compressability{$c}; + } + # 'Unknown compressability indicator - expected one of (0,1,2,3,#)'; + return undef; +} + +# Caption evaluation comes from second indicator +sub caption_evaluation { + my $ce = shift || return undef; + + my %caption_evaluation = ( + '0' => 'Captions verified; all levels present', + '1' => 'Captions verified; all levels may not be present', + '2' => 'Captions unverified; all levels present', + '3' => 'Captions unverified; all levels may not be present', + '#' => 'Undefined', + ); + + if (exists $caption_evaluation{$ce}) { + return $caption_evaluation{$ce}; + } + # 'Unknown caption evaluation indicator - expected one of (0,1,2,3,#)'; + return undef; +} + +# Start with frequency ($w) +# then overlay number of pieces of issuance ($p) +# then regularity pattern ($y) +my %frequency = ( + 'a' => 'annual', + 'b' => 'bimonthly', + 'c' => 'semiweekly', + 'd' => 'daily', + 'e' => 'biweekly', + 'f' => 'semiannual', + 'g' => 'biennial', + 'h' => 'triennial', + 'i' => 'three times a week', + 'j' => 'three times a month', + 'k' => 'continuously updated', + 'm' => 'monthly', + 'q' => 'quarterly', + 's' => 'semimonthly', + 't' => 'three times a year', + 'w' => 'weekly', + 'x' => 'completely irregular', +); + +sub parse_frequency { + my $freq = shift || return undef; + + if ($freq =~ m/^\d+$/) { + return "$freq times a year"; + } elsif (exists $frequency{$freq}) { + return $frequency{$freq}; + } + # unrecognized frequency specification + return undef; +} + +# $x - Point at which the highest level increments or changes +# Interpretation of two-digit numbers in the 01-12 range depends on the publishing frequency +# More than one change can be passed in the subfield and are delimited by commas +sub chronology_change { + my $chronology_change = shift || return undef; + my @c_changes = split /,/, $chronology_change; + foreach my $change (@c_changes) { + if ($change == 21) { + + } + } + return undef; +} + +# Publication code : first character in regularity pattern ($y) +sub parse_publication_code { + my $c = shift || return undef; + + my %publication_code = ( + 'c' => 'combined', + 'o' => 'omitted', + 'p' => 'published', + '#' => 'undefined', + ); + + if (exists $publication_code{$c}) { + return $publication_code{$c}; + } + return undef; +} + +# Chronology code : part of regularity pattern ($y) +sub parse_chronology_code { + my $c = shift || return undef; + + my %chronology_code = ( + 'd' => 'Day', + 'm' => 'Month', + 's' => 'Season', + 'w' => 'Week', + 'y' => 'Year', + 'e' => 'Enumeration', + ); + + if (exists $chronology_code{$c}) { + return $chronology_code{$c}; + } + return undef; +} + +sub parse_regularity_pattern { + my $pattern = shift; + my ($pc, $cc, $cd) = $pattern =~ m{^(\w)(\w)(.+)$}; + + my $pub_code = parse_publication_code($pc); + my $chron_code = parse_chronology_code($cc); + my $chron_def = parse_chronology_definition($cd); + + return ($pub_code, $chron_code, $chron_def); +} + +sub parse_chronology_definition { + my $chron_def = shift || return undef; + # Well, this is where it starts to get hard, doesn't it? + return $chron_def; +} + +print parse_regularity_pattern("cddd"); +print "\n"; +print parse_regularity_pattern("38dd"); +print "\n"; + +1; + +# :vim:noet:ts=4:sw=4: diff --git a/Open-ILS/src/perlmods/OpenILS/WWW/EGWeb.pm b/Open-ILS/src/perlmods/OpenILS/WWW/EGWeb.pm new file mode 100644 index 0000000000..14146b9b3b --- /dev/null +++ b/Open-ILS/src/perlmods/OpenILS/WWW/EGWeb.pm @@ -0,0 +1,150 @@ +package OpenILS::WWW::EGWeb; +use strict; use warnings; +use Template; +use XML::Simple; +use File::stat; +use Apache2::Const -compile => qw(OK DECLINED HTTP_INTERNAL_SERVER_ERROR); +use Apache2::Log; + +use constant OILS_HTTP_COOKIE_SKIN => 'oils:skin'; +use constant OILS_HTTP_COOKIE_THEME => 'oils:theme'; +use constant OILS_HTTP_COOKIE_LOCALE => 'oils:locale'; + +my $web_config; +my $web_config_file; +my $web_config_edit_time; + +sub import { + my $self = shift; + $web_config_file = shift; + unless(-r $web_config_file) { + warn "Invalid web config $web_config_file"; + return; + } + check_web_config(); +} + + +sub handler { + my $r = shift; + check_web_config($r); # option to disable this + my $ctx = load_context($r); + my $base = $ctx->{base_uri}; + my($template, $page_args) = find_template($r, $base); + return Apache2::Const::DECLINED unless $template; + + $template = $ctx->{skin} . "/$template"; + $ctx->{page_args} = $page_args; + $r->content_type('text/html; encoding=utf8'); + + my $tt = Template->new({ + OUTPUT => $r, + INCLUDE_PATH => $ctx->{template_paths}, + }); + + unless($tt->process($template, {ctx => $ctx})) { + $r->log->warn('Template error: ' . $tt->error); + return Apache2::Const::HTTP_INTERNAL_SERVER_ERROR; + } + + return Apache2::Const::OK; +} + +sub load_context { + my $r = shift; + my $cgi = CGI->new; + my $ctx = $web_config->{ctx}; + $ctx->{skin} = $cgi->cookie(OILS_HTTP_COOKIE_SKIN) || 'default'; + $ctx->{theme} = $cgi->cookie(OILS_HTTP_COOKIE_THEME) || 'default'; + $ctx->{locale} = + $r->headers_in->get('Accept-Language') || # this will need some trimming + $cgi->cookie(OILS_HTTP_COOKIE_LOCALE) || 'en-US'; + $r->log->debug('skin = ' . $ctx->{skin} . ' : theme = ' . + $ctx->{theme} . ' : locale = ' . $ctx->{locale}); + return $ctx; +} + +# Given a URI, finds the configured template and any extra page +# arguments (trailing path info). Any extra data is returned +# as page arguments, in the form of an array, one item per +# /-separated URI component +sub find_template { + my $r = shift; + my $base = shift; + my $path = $r->uri; + $path =~ s/$base//og; + my @parts = split('/', $path); + my $template = ''; + my $page_args = []; + my $handler = $web_config->{handlers}; + while(@parts) { + my $part = shift @parts; + next unless $part; + my $t = $handler->{$part}; + if(ref $t) { + $handler = $t; + } else { + $template = $t; + $page_args = [@parts]; + last; + } + } + + unless($template) { + $r->log->warn("No template configured for path $path"); + return (); + } + + $r->log->debug("template = $template : page args = @$page_args"); + return ($template, $page_args); +} + +# if the web configuration file has never been loaded or has +# changed since the last load, reload it +sub check_web_config { + my $r = shift; + my $epoch = stat($web_config_file)->mtime; + unless($web_config_edit_time and $web_config_edit_time == $epoch) { + $r->log->debug("Reloading web config after edit...") if $r; + $web_config_edit_time = $epoch; + $web_config = parse_config($web_config_file); + } +} + +sub parse_config { + my $cfg_file = shift; + my $data = XML::Simple->new->XMLin($cfg_file); + my $ctx = {}; + my $handlers = {}; + + $ctx->{media_prefix} = (ref $data->{media_prefix}) ? '' : $data->{media_prefix}; + $ctx->{base_uri} = (ref $data->{base_uri}) ? '' : $data->{base_uri}; + $ctx->{template_paths} = []; + + my $tpaths = $data->{template_paths}->{path}; + $tpaths = [$tpaths] unless ref $tpaths; + push(@{$ctx->{template_paths}}, $_) for @$tpaths; + + for my $handler (@{$data->{handlers}->{handler}}) { + my @parts = split('/', $handler->{path}); + my $h = $handlers; + my $pcount = scalar(@parts); + for(my $i = 0; $i < $pcount; $i++) { + my $p = $parts[$i]; + unless(defined $h->{$p}) { + if($i == $pcount - 1) { + $h->{$p} = $handler->{template}; + last; + } else { + $h->{$p} = {}; + } + } + $h = $h->{$p}; + } + } + + return {ctx => $ctx, handlers => $handlers}; +} + + +1; diff --git a/Open-ILS/src/sql/Pg/200.schema.acq.sql b/Open-ILS/src/sql/Pg/200.schema.acq.sql new file mode 100644 index 0000000000..5926f1e3dd --- /dev/null +++ b/Open-ILS/src/sql/Pg/200.schema.acq.sql @@ -0,0 +1,440 @@ +DROP SCHEMA acq CASCADE; + +BEGIN; + +CREATE SCHEMA acq; + + +-- Tables + + +CREATE TABLE acq.currency_type ( + code TEXT PRIMARY KEY, + label TEXT +); + +-- Use the ISO 4217 abbreviations for currency codes +INSERT INTO acq.currency_type (code, label) VALUES ('USD','US Dollars'); +INSERT INTO acq.currency_type (code, label) VALUES ('CAN','Canadian Dollars'); +INSERT INTO acq.currency_type (code, label) VALUES ('EUR','Euros'); + +CREATE TABLE acq.exchange_rate ( + id SERIAL PRIMARY KEY, + from_currency TEXT NOT NULL REFERENCES acq.currency_type (code), + to_currency TEXT NOT NULL REFERENCES acq.currency_type (code), + ratio NUMERIC NOT NULL, + CONSTRAINT exchange_rate_from_to_once UNIQUE (from_currency,to_currency) +); + +INSERT INTO acq.exchange_rate (from_currency,to_currency,ratio) VALUES ('USD','CAN',1.2); +INSERT INTO acq.exchange_rate (from_currency,to_currency,ratio) VALUES ('USD','EUR',0.5); + +CREATE TABLE acq.provider ( + id SERIAL PRIMARY KEY, + name TEXT NOT NULL, + owner INT NOT NULL REFERENCES actor.org_unit (id), + currency_type TEXT NOT NULL REFERENCES acq.currency_type (code), + code TEXT UNIQUE, + CONSTRAINT provider_name_once_per_owner UNIQUE (name,owner) +); + +CREATE TABLE acq.funding_source ( + id SERIAL PRIMARY KEY, + name TEXT NOT NULL, + owner INT NOT NULL REFERENCES actor.org_unit (id), + currency_type TEXT NOT NULL REFERENCES acq.currency_type (code), + code TEXT UNIQUE, + CONSTRAINT funding_source_name_once_per_owner UNIQUE (name,owner) +); + +CREATE TABLE acq.funding_source_credit ( + id SERIAL PRIMARY KEY, + funding_source INT NOT NULL REFERENCES acq.funding_source (id), + amount NUMERIC NOT NULL, + note TEXT +); + +CREATE TABLE acq.fund ( + id SERIAL PRIMARY KEY, + org INT NOT NULL REFERENCES actor.org_unit (id) ON UPDATE CASCADE ON DELETE CASCADE, + name TEXT NOT NULL, + year INT NOT NULL DEFAULT EXTRACT( YEAR FROM NOW() ), + currency_type TEXT NOT NULL REFERENCES acq.currency_type (code), + code TEXT UNIQUE, + CONSTRAINT name_once_per_org_year UNIQUE (org,name,year) +); + +CREATE TABLE acq.fund_debit ( + id SERIAL PRIMARY KEY, + fund INT NOT NULL REFERENCES acq.fund (id), + origin_amount NUMERIC NOT NULL, -- pre-exchange-rate amount + origin_currency_type TEXT NOT NULL REFERENCES acq.currency_type (code), + amount NUMERIC NOT NULL, + encumbrance BOOL NOT NULL DEFAULT TRUE, + debit_type TEXT NOT NULL, + xfer_destination INT REFERENCES acq.fund (id) +); + +CREATE TABLE acq.fund_allocation ( + id SERIAL PRIMARY KEY, + funding_source INT NOT NULL REFERENCES acq.funding_source (id) ON UPDATE CASCADE ON DELETE CASCADE, + fund INT NOT NULL REFERENCES acq.fund (id) ON UPDATE CASCADE ON DELETE CASCADE, + amount NUMERIC, + percent NUMERIC CHECK (percent IS NULL OR percent BETWEEN 0.0 AND 100.0), + allocator INT NOT NULL REFERENCES actor.usr (id), + note TEXT, + CONSTRAINT allocation_amount_or_percent CHECK ((percent IS NULL AND amount IS NOT NULL) OR (percent IS NOT NULL AND amount IS NULL)) +); + + +CREATE TABLE acq.picklist ( + id SERIAL PRIMARY KEY, + owner INT NOT NULL REFERENCES actor.usr (id), + org_unit INT NOT NULL REFERENCES actor.org_unit (id), + name TEXT NOT NULL, + create_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(), + edit_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(), + CONSTRAINT name_once_per_owner UNIQUE (name,owner) +); + +CREATE TABLE acq.purchase_order ( + id SERIAL PRIMARY KEY, + owner INT NOT NULL REFERENCES actor.usr (id), + ordering_agency INT NOT NULL REFERENCES actor.org_unit (id), + create_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(), + edit_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(), + provider INT NOT NULL REFERENCES acq.provider (id), + state TEXT NOT NULL DEFAULT 'new' +); +CREATE INDEX po_owner_idx ON acq.purchase_order (owner); +CREATE INDEX po_provider_idx ON acq.purchase_order (provider); +CREATE INDEX po_state_idx ON acq.purchase_order (state); + +CREATE TABLE acq.po_note ( + id SERIAL PRIMARY KEY, + purchase_order INT NOT NULL REFERENCES acq.purchase_order (id), + creator INT NOT NULL REFERENCES actor.usr (id), + editor INT NOT NULL REFERENCES actor.usr (id), + create_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(), + edit_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(), + value TEXT NOT NULL +); +CREATE INDEX po_note_po_idx ON acq.po_note (purchase_order); + +CREATE TABLE acq.lineitem ( + id BIGSERIAL PRIMARY KEY, + selector INT NOT NULL REFERENCES actor.org_unit (id), + provider INT REFERENCES acq.provider (id), + purchase_order INT REFERENCES acq.purchase_order (id), + picklist INT REFERENCES acq.picklist (id), + expected_recv_time TIMESTAMP WITH TIME ZONE, + create_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(), + edit_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(), + marc TEXT NOT NULL, + eg_bib_id INT REFERENCES biblio.record_entry (id), + source_label TEXT, + item_count INT NOT NULL DEFAULT 0, + state TEXT NOT NULL DEFAULT 'new', + CONSTRAINT picklist_or_po CHECK (picklist IS NOT NULL OR purchase_order IS NOT NULL) +); +CREATE INDEX li_po_idx ON acq.lineitem (purchase_order); +CREATE INDEX li_pl_idx ON acq.lineitem (picklist); + +CREATE TABLE acq.lineitem_note ( + id SERIAL PRIMARY KEY, + lineitem INT NOT NULL REFERENCES acq.lineitem (id), + creator INT NOT NULL REFERENCES actor.usr (id), + editor INT NOT NULL REFERENCES actor.usr (id), + create_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(), + edit_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(), + value TEXT NOT NULL +); +CREATE INDEX li_note_li_idx ON acq.lineitem_note (lineitem); + +CREATE TABLE acq.lineitem_detail ( + id BIGSERIAL PRIMARY KEY, + lineitem INT NOT NULL REFERENCES acq.lineitem (id), + fund INT REFERENCES acq.fund (id), + fund_debit INT REFERENCES acq.fund_debit (id), + eg_copy_id BIGINT REFERENCES asset.copy (id) ON DELETE SET NULL, + barcode TEXT, + cn_label TEXT, + owning_lib INT REFERENCES actor.org_unit (id) ON DELETE SET NULL, + location INT REFERENCES asset.copy_location (id) ON DELETE SET NULL, + recv_time TIMESTAMP WITH TIME ZONE +); + +CREATE INDEX li_detail_li_idx ON acq.lineitem_detail (lineitem); + +CREATE TABLE acq.lineitem_attr_definition ( + id BIGSERIAL PRIMARY KEY, + code TEXT NOT NULL, + description TEXT NOT NULL, + remove TEXT NOT NULL DEFAULT '', + ident BOOL NOT NULL DEFAULT FALSE +); + +CREATE TABLE acq.lineitem_marc_attr_definition ( + id BIGINT PRIMARY KEY DEFAULT NEXTVAL('acq.lineitem_attr_definition_id_seq'), + xpath TEXT NOT NULL +) INHERITS (acq.lineitem_attr_definition); + +CREATE TABLE acq.lineitem_provider_attr_definition ( + id BIGINT PRIMARY KEY DEFAULT NEXTVAL('acq.lineitem_attr_definition_id_seq'), + xpath TEXT NOT NULL, + provider INT NOT NULL REFERENCES acq.provider (id) +) INHERITS (acq.lineitem_attr_definition); + +CREATE TABLE acq.lineitem_generated_attr_definition ( + id BIGINT PRIMARY KEY DEFAULT NEXTVAL('acq.lineitem_attr_definition_id_seq'), + xpath TEXT NOT NULL +) INHERITS (acq.lineitem_attr_definition); + +CREATE TABLE acq.lineitem_usr_attr_definition ( + id BIGINT PRIMARY KEY DEFAULT NEXTVAL('acq.lineitem_attr_definition_id_seq'), + usr INT NOT NULL REFERENCES actor.usr (id) +) INHERITS (acq.lineitem_attr_definition); + +CREATE TABLE acq.lineitem_local_attr_definition ( + id BIGINT PRIMARY KEY DEFAULT NEXTVAL('acq.lineitem_attr_definition_id_seq') +) INHERITS (acq.lineitem_attr_definition); + +CREATE TABLE acq.lineitem_attr ( + id BIGSERIAL PRIMARY KEY, + definition BIGINT NOT NULL, + lineitem BIGINT NOT NULL REFERENCES acq.lineitem (id), + attr_type TEXT NOT NULL, + attr_name TEXT NOT NULL, + attr_value TEXT NOT NULL +); + +CREATE INDEX li_attr_li_idx ON acq.lineitem_attr (lineitem); +CREATE INDEX li_attr_value_idx ON acq.lineitem_attr (attr_value); +CREATE INDEX li_attr_definition_idx ON acq.lineitem_attr (definition); + + +-- Seed data + + +INSERT INTO acq.lineitem_marc_attr_definition ( code, description, xpath ) VALUES ('title','Title of work','//*[@tag="245"]/*[contains("abcmnopr",@code)]'); +INSERT INTO acq.lineitem_marc_attr_definition ( code, description, xpath ) VALUES ('author','Author of work','//*[@tag="100" or @tag="110" or @tag="113"]/*[contains("ad",@code)]'); +INSERT INTO acq.lineitem_marc_attr_definition ( code, description, xpath ) VALUES ('language','Lanuage of work','//*[@tag="240"]/*[@code="l"][1]'); +INSERT INTO acq.lineitem_marc_attr_definition ( code, description, xpath ) VALUES ('pagination','Pagination','//*[@tag="300"]/*[@code="a"][1]'); +INSERT INTO acq.lineitem_marc_attr_definition ( code, description, xpath, remove ) VALUES ('isbn','ISBN','//*[@tag="020"]/*[@code="a"]', $r$(?:-|\s.+$)$r$); +INSERT INTO acq.lineitem_marc_attr_definition ( code, description, xpath, remove ) VALUES ('issn','ISSN','//*[@tag="022"]/*[@code="a"]', $r$(?:-|\s.+$)$r$); +INSERT INTO acq.lineitem_marc_attr_definition ( code, description, xpath ) VALUES ('price','Price','//*[@tag="020" or @tag="022"]/*[@code="c"][1]'); +INSERT INTO acq.lineitem_marc_attr_definition ( code, description, xpath ) VALUES ('identifier','Identifier','//*[@tag="001"]'); +INSERT INTO acq.lineitem_marc_attr_definition ( code, description, xpath ) VALUES ('publisher','Publisher','//*[@tag="260"]/*[@code="b"][1]'); +INSERT INTO acq.lineitem_marc_attr_definition ( code, description, xpath ) VALUES ('pubdate','Publication Date','//*[@tag="260"]/*[@code="c"][1]'); +INSERT INTO acq.lineitem_marc_attr_definition ( code, description, xpath ) VALUES ('edition','Edition','//*[@tag="250"]/*[@code="a"][1]'); + + +-- Functions + + +CREATE OR REPLACE FUNCTION public.extract_acq_marc_field ( BIGINT, TEXT, TEXT) RETURNS TEXT AS $$ + SELECT public.extract_marc_field('acq.lineitem', $1, $2, $3); +$$ LANGUAGE SQL; + +/* +CREATE OR REPLACE FUNCTION public.extract_bib_marc_field ( BIGINT, TEXT ) RETURNS TEXT AS $$ + SELECT public.extract_marc_field('biblio.record_entry', $1, $2); +$$ LANGUAGE SQL; + +CREATE OR REPLACE FUNCTION public.extract_authority_marc_field ( BIGINT, TEXT ) RETURNS TEXT AS $$ + SELECT public.extract_marc_field('authority.record_entry', $1, $2); +$$ LANGUAGE SQL; +*/ +-- For example: +-- INSERT INTO acq.lineitem_provider_attr_definition ( provider, code, description, xpath ) VALUES (1,'price','Price','//*[@tag="020" or @tag="022"]/*[@code="a"][1]'); + +/* +Suggested vendor fields: + vendor_price + vendor_currency + vendor_avail + vendor_po + vendor_identifier +*/ + +CREATE OR REPLACE FUNCTION public.ingest_acq_marc ( ) RETURNS TRIGGER AS $$ +DECLARE + value TEXT; + atype TEXT; + prov INT; + adef RECORD; + xpath_string TEXT; +BEGIN + FOR adef IN SELECT *,tableoid FROM acq.lineitem_attr_definition LOOP + + SELECT relname::TEXT INTO atype FROM pg_class WHERE oid = adef.tableoid; + + IF (atype NOT IN ('lineitem_usr_attr_definition','lineitem_local_attr_definition')) THEN + IF (atype = 'lineitem_provider_attr_definition') THEN + SELECT provider INTO prov FROM acq.lineitem_provider_attr_definition WHERE id = adef.id; + CONTINUE WHEN NEW.provider IS NULL OR prov <> NEW.provider; + END IF; + + IF (atype = 'lineitem_provider_attr_definition') THEN + SELECT xpath INTO xpath_string FROM acq.lineitem_provider_attr_definition WHERE id = adef.id; + ELSIF (atype = 'lineitem_marc_attr_definition') THEN + SELECT xpath INTO xpath_string FROM acq.lineitem_marc_attr_definition WHERE id = adef.id; + ELSIF (atype = 'lineitem_generated_attr_definition') THEN + SELECT xpath INTO xpath_string FROM acq.lineitem_generated_attr_definition WHERE id = adef.id; + END IF; + + SELECT extract_acq_marc_field(id, xpath_string, adef.remove) INTO value FROM acq.lineitem WHERE id = NEW.id; + + IF (value IS NOT NULL AND value <> '') THEN + INSERT INTO acq.lineitem_attr (lineitem, definition, attr_type, attr_name, attr_value) + VALUES (NEW.id, adef.id, atype, adef.code, value); + END IF; + + END IF; + + END LOOP; + + RETURN NULL; +END; +$$ LANGUAGE PLPGSQL; + +CREATE OR REPLACE FUNCTION public.cleanup_acq_marc ( ) RETURNS TRIGGER AS $$ +BEGIN + IF TG_OP = 'UPDATE' THEN + DELETE FROM acq.lineitem_attr + WHERE lineitem = OLD.id AND attr_type IN ('lineitem_provider_attr_definition', 'lineitem_marc_attr_definition','lineitem_generated_attr_definition'); + RETURN NEW; + ELSE + DELETE FROM acq.lineitem_attr WHERE lineitem = OLD.id; + RETURN OLD; + END IF; +END; +$$ LANGUAGE PLPGSQL; + +CREATE TRIGGER cleanup_lineitem_trigger + BEFORE UPDATE OR DELETE ON acq.lineitem + FOR EACH ROW EXECUTE PROCEDURE public.cleanup_acq_marc(); + +CREATE TRIGGER ingest_lineitem_trigger + AFTER INSERT OR UPDATE ON acq.lineitem + FOR EACH ROW EXECUTE PROCEDURE public.ingest_acq_marc(); + +CREATE OR REPLACE FUNCTION acq.exchange_ratio ( from_ex TEXT, to_ex TEXT ) RETURNS NUMERIC AS $$ +DECLARE + rat NUMERIC; +BEGIN + IF from_ex = to_ex THEN + RETURN 1.0; + END IF; + + SELECT ratio INTO rat FROM acq.exchange_rate WHERE from_currency = from_ex AND to_currency = to_ex; + + IF FOUND THEN + RETURN rat; + ELSE + SELECT ratio INTO rat FROM acq.exchange_rate WHERE from_currency = to_ex AND to_currency = from_ex; + IF FOUND THEN + RETURN 1.0/rat; + END IF; + END IF; + + RETURN NULL; + +END; +$$ LANGUAGE PLPGSQL; + +CREATE OR REPLACE FUNCTION acq.exchange_ratio ( TEXT, TEXT, NUMERIC ) RETURNS NUMERIC AS $$ + SELECT $3 * acq.exchange_ratio($1, $2); +$$ LANGUAGE SQL; + +CREATE OR REPLACE VIEW acq.funding_source_credit_total AS + SELECT funding_source, + SUM(amount) AS amount + FROM acq.funding_source_credit + GROUP BY 1; + +CREATE OR REPLACE VIEW acq.funding_source_allocation_total AS + SELECT funding_source, + SUM(amount)::NUMERIC(100,2) AS amount + FROM ( + SELECT funding_source, + SUM(a.amount)::NUMERIC(100,2) AS amount + FROM acq.fund_allocation a + WHERE a.percent IS NULL + GROUP BY 1 + UNION ALL + SELECT funding_source, + SUM( (SELECT SUM(amount) FROM acq.funding_source_credit c WHERE c.funding_source = a.funding_source) * (a.percent/100.0) )::NUMERIC(100,2) AS amount + FROM acq.fund_allocation a + WHERE a.amount IS NULL + GROUP BY 1 + ) x + GROUP BY 1; + +CREATE OR REPLACE VIEW acq.funding_source_balance AS + SELECT COALESCE(c.funding_source, a.funding_source) AS funding_source, + SUM(COALESCE(c.amount,0.0) - COALESCE(a.amount,0.0))::NUMERIC(100,2) AS amount + FROM acq.funding_source_credit_total c + FULL JOIN acq.funding_source_allocation_total a USING (funding_source) + GROUP BY 1; + +CREATE OR REPLACE VIEW acq.fund_allocation_total AS + SELECT fund, + SUM(amount)::NUMERIC(100,2) AS amount + FROM ( + SELECT fund, + SUM(a.amount * acq.exchange_ratio(s.currency_type, f.currency_type))::NUMERIC(100,2) AS amount + FROM acq.fund_allocation a + JOIN acq.fund f ON (a.fund = f.id) + JOIN acq.funding_source s ON (a.funding_source = s.id) + WHERE a.percent IS NULL + GROUP BY 1 + UNION ALL + SELECT fund, + SUM( (SELECT SUM(amount) FROM acq.funding_source_credit c WHERE c.funding_source = a.funding_source) * acq.exchange_ratio(s.currency_type, f.currency_type) * (a.percent/100.0) )::NUMERIC(100,2) AS amount + FROM acq.fund_allocation a + JOIN acq.fund f ON (a.fund = f.id) + JOIN acq.funding_source s ON (a.funding_source = s.id) + WHERE a.amount IS NULL + GROUP BY 1 + ) x + GROUP BY 1; + +CREATE OR REPLACE VIEW acq.fund_debit_total AS + SELECT id AS fund, + encumbrance, + SUM(amount) AS amount + FROM acq.fund_debit + GROUP BY 1,2; + +CREATE OR REPLACE VIEW acq.fund_encumbrance_total AS + SELECT fund, + SUM(amount) AS amount + FROM acq.fund_debit_total + WHERE encumbrance IS TRUE + GROUP BY 1; + +CREATE OR REPLACE VIEW acq.fund_spent_total AS + SELECT fund, + SUM(amount) AS amount + FROM acq.fund_debit_total + WHERE encumbrance IS FALSE + GROUP BY 1; + +CREATE OR REPLACE VIEW acq.fund_combined_balance AS + SELECT c.fund, + c.amount - COALESCE(d.amount,0.0) AS amount + FROM acq.fund_allocation_total c + LEFT JOIN acq.fund_debit_total d USING (fund); + +CREATE OR REPLACE VIEW acq.fund_spent_balance AS + SELECT c.fund, + c.amount - COALESCE(d.amount,0.0) AS amount + FROM acq.fund_allocation_total c + LEFT JOIN acq.fund_spent_total d USING (fund); + +COMMIT; + + + + diff --git a/Open-ILS/src/sql/Pg/210.schema.serials.sql b/Open-ILS/src/sql/Pg/210.schema.serials.sql new file mode 100644 index 0000000000..56a4391760 --- /dev/null +++ b/Open-ILS/src/sql/Pg/210.schema.serials.sql @@ -0,0 +1,64 @@ + + +DROP SCHEMA serial CASCADE; + +CREATE TABLE asset.uri ( + id SERIAL PRIMARY KEY, + href TEXT NOT NULL, + label TEXT, + use TEXT, + active BOOL NOT NULL DEFAULT TRUE +); + +ALTER TABLE asset.call_number ADD COLUMN uri INT REFERENCES asset.uri (id); + +BEGIN; + +CREATE SCHEMA serial; + +CREATE TABLE serial.subscription ( + id SERIAL PRIMARY KEY, + callnumber BIGINT REFERENCES asset.call_number (id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED, + uri INT REFERENCES asset.uri (id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED, + start_date DATE NOT NULL, + end_date DATE NOT NULL +); + +CREATE TABLE serial.binding_unit ( + id SERIAL PRIMARY KEY, + subscription INT NOT NULL REFERENCES serial.subscription (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, + label TEXT NOT NULL, + CONSTRAINT bu_label_once_per_sub UNIQUE (subscription, label) +); + +CREATE TABLE serial.issuance ( + id SERIAL PRIMARY KEY, + subscription INT NOT NULL REFERENCES serial.subscription (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, + target_copy BIGINT REFERENCES asset.copy (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, + binding_unit INT REFERENCES serial.binding_unit (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, + label TEXT +); + +CREATE TABLE serial.bib_summary ( + id SERIAL PRIMARY KEY, + call_number INT UNIQUE NOT NULL REFERENCES serial.subscription (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, + generated_coverage TEXT NOT NULL, + textual_holdings TEXT +); + +CREATE TABLE serial.sup_summary ( + id SERIAL PRIMARY KEY, + call_number INT UNIQUE NOT NULL REFERENCES serial.subscription (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, + generated_coverage TEXT NOT NULL, + textual_holdings TEXT +); + +CREATE TABLE serial.index_summary ( + id SERIAL PRIMARY KEY, + call_number INT UNIQUE NOT NULL REFERENCES serial.subscription (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, + generated_coverage TEXT NOT NULL, + textual_holdings TEXT +); + +COMMIT; + diff --git a/Open-ILS/src/sql/Pg/950.data.seed-values.sql b/Open-ILS/src/sql/Pg/950.data.seed-values.sql index f1a13049f6..ec55e730dc 100644 --- a/Open-ILS/src/sql/Pg/950.data.seed-values.sql +++ b/Open-ILS/src/sql/Pg/950.data.seed-values.sql @@ -1123,9 +1123,36 @@ INSERT INTO permission.perm_list VALUES INSERT INTO permission.perm_list VALUES (151, 'DELETE_CONTAINER_ITEM', oils_i18n_gettext(151, 'Allow a user to delete an item out of another user''s container', 'ppl', 'description')); INSERT INTO permission.perm_list VALUES + (153, 'CREATE_FUNDING_SOURCE', oils_i18n_gettext(153, 'Allow a user to create a new funding source', 'ppl', 'description')), + (154, 'DELETE_FUNDING_SOURCE', oils_i18n_gettext(154, 'Allow a user to delete a funding source', 'ppl', 'description')), + (155, 'VIEW_FUNDING_SOURCE', oils_i18n_gettext(155, 'Allow a user to view a funding source', 'ppl', 'description')), + (156, 'UPDATE_FUNDING_SOURCE', oils_i18n_gettext(156, 'Allow a user to update a funding source', 'ppl', 'description')), + (157, 'CREATE_FUND', oils_i18n_gettext(157, 'Allow a user to create a new fund', 'ppl', 'description')), + (158, 'DELETE_FUND', oils_i18n_gettext(158, 'Allow a user to delete a fund', 'ppl', 'description')), + (159, 'VIEW_FUND', oils_i18n_gettext(159, 'Allow a user to view a fund', 'ppl', 'description')), + (160, 'UPDATE_FUND', oils_i18n_gettext(160, 'Allow a user to update a fund', 'ppl', 'description')), + (161, 'CREATE_FUND_ALLOCATION', oils_i18n_gettext(161, 'Allow a user to create a new fund allocation', 'ppl', 'description')), + (162, 'DELETE_FUND_ALLOCATION', oils_i18n_gettext(162, 'Allow a user to delete a fund allocation', 'ppl', 'description')), + (163, 'VIEW_FUND_ALLOCATION', oils_i18n_gettext(163, 'Allow a user to view a fund allocation', 'ppl', 'description')), + (164, 'UPDATE_FUND_ALLOCATION', oils_i18n_gettext(164, 'Allow a user to update a fund allocation', 'ppl', 'description')), + (165, 'GENERAL_ACQ', oils_i18n_gettext(165, 'Lowest level permission required to access the ACQ interface', 'ppl', 'description')), + (166, 'CREATE_PROVIDER', oils_i18n_gettext(166, 'Allow a user to create a new provider', 'ppl', 'description')), + (167, 'DELETE_PROVIDER', oils_i18n_gettext(167, 'Allow a user to delate a provider', 'ppl', 'description')), + (168, 'VIEW_PROVIDER', oils_i18n_gettext(168, 'Allow a user to view a provider', 'ppl', 'description')), + (169, 'UPDATE_PROVIDER', oils_i18n_gettext(169, 'Allow a user to update a provider', 'ppl', 'description')), + (170, 'ADMIN_FUNDING_SOURCE', oils_i18n_gettext(170, 'Allow a user to create/view/update/delete a funding source', 'ppl', 'description')), + (171, 'ADMIN_FUND', oils_i18n_gettext(171, 'Allow a user to create/view/update/delete a fund', 'ppl', 'description')), + (172, 'MANAGE_FUNDING_SOURCE', oils_i18n_gettext(172, 'Allow a user to view/credit/debit a funding source', 'ppl', 'description')), + (173, 'MANAGE_FUND', oils_i18n_gettext(173, 'Allow a user to view/credit/debit a fund', 'ppl', 'description')), + (174, 'CREATE_PICKLIST', oils_i18n_gettext(174, 'Allows a user to create a picklist', 'ppl', 'description')), + (175, 'ADMIN_PROVIDER', oils_i18n_gettext(175, 'Allow a user to create/view/update/delete a provider', 'ppl', 'description')), + (176, 'MANAGE_PROVIDER', oils_i18n_gettext(176, 'Allow a user to view and purchase from a provider', 'ppl', 'description')), + (177, 'VIEW_PICKLIST', oils_i18n_gettext(177, 'Allow a user to view another users picklist', 'ppl', 'description')), (152, 'ASSIGN_WORK_ORG_UNIT', oils_i18n_gettext(152, 'Allow a staff member to define where another staff member has their permissions', 'ppl', 'description')); INSERT INTO permission.perm_list VALUES - (153, 'DELETE_RECORD', oils_i18n_gettext(153, 'Allow a staff member to directly remove a bibliographic record', 'ppl', 'description')); + (178, 'DELETE_RECORD', oils_i18n_gettext(178, 'Allow a staff member to directly remove a bibliographic record', 'ppl', 'description')); +INSERT INTO permission.perm_list VALUES + (179, 'ADMIN_CURRENCY_TYPE', oils_i18n_gettext(179, 'Allow a user to create/view/update/delete a currency_type', 'ppl', 'description')); SELECT SETVAL('permission.perm_list_id_seq'::TEXT, (SELECT MAX(id) FROM permission.perm_list)); diff --git a/Open-ILS/src/sql/Pg/build-db.sh b/Open-ILS/src/sql/Pg/build-db.sh index 9ad12ee452..816ab74793 100755 --- a/Open-ILS/src/sql/Pg/build-db.sh +++ b/Open-ILS/src/sql/Pg/build-db.sh @@ -102,6 +102,9 @@ ordered_file_list=" 100.circ_matrix.sql 110.hold_matrix.sql + 200.schema.acq.sql + 210.schema.serials.sql + 300.schema.staged_search.sql 500.view.cross-schema.sql diff --git a/Open-ILS/src/support-scripts/test-scripts/acq_fund.py b/Open-ILS/src/support-scripts/test-scripts/acq_fund.py new file mode 100644 index 0000000000..17f16b5a59 --- /dev/null +++ b/Open-ILS/src/support-scripts/test-scripts/acq_fund.py @@ -0,0 +1,41 @@ +#!/usr/bin/python +import sys +import oils.system, oils.utils.utils +import osrf.net_obj, osrf.ses + +# --------------------------------------------------------------- +# Usage: python acq_fund_source.py +# --------------------------------------------------------------- + +oils.system.System.connect(config_file='/openils/conf/opensrf_core.xml', config_context='config.opensrf') +auth_info = oils.utils.utils.login(sys.argv[1], sys.argv[2], 'staff', sys.argv[3]) +authtoken = auth_info['payload']['authtoken'] + +ses = osrf.ses.ClientSession('open-ils.acq') +ses.connect() # not required, but faster for batches of request + +# XXX This loop assumes the existence of orgs with IDs 1-6 and a USD currency +ids = [] +for i in range(0,5): + fund_source = osrf.net_obj.NetworkObject.acqfs() + fund_source.name("test-fund_source-%d" % i) + fund_source.owner(i+1) + fund_source.currency_type('USD') + req = ses.request('open-ils.acq.funding_source.create', authtoken, fund_source) + id = req.recv().content() + print 'created fund_source ' + str(id) + ids.append(id) + +req = ses.request('open-ils.acq.funding_source.org.retrieve', authtoken, 1, {"children":1}) +resp = req.recv().content() +for fund_source in resp: + print 'fetched fund_source ' + str(fund_source.name()) + +for i in ids: + req = ses.request('open-ils.acq.funding_source.delete', authtoken, i) + print 'delete returned ' + str(req.recv().content()) + + +ses.disconnect() # only required if a connect() call was made + + diff --git a/Open-ILS/web/css/skin/default.css b/Open-ILS/web/css/skin/default.css new file mode 100644 index 0000000000..cda9b27d08 --- /dev/null +++ b/Open-ILS/web/css/skin/default.css @@ -0,0 +1,62 @@ +/* import the default css for the install applications */ +@import "default/acq.css"; +@import "default/admin.css"; +/* import the dojo CSS */ +@import "/js/dojo/dojo/resources/dojo.css"; +@import "/js/dojo/dijit/themes/tundra/tundra.css"; +@import "/js/dojo/dojox/grid/_grid/Grid.css"; + + +html, body, #oils-base-body-block { + width:100%; + height:100%; + border:0; + margin:0; + padding:0; +} +table { border-collapse: collapse; } +/* use this for divs whose contents should be entirely contained within the div */ +.container:after {content: ""; display: block; height: 0; clear: both; } + +.invisible { visibility: hidden; } +.hidden { display: none; visibility: hidden; } +.display { display: block; visibility: visible; } +.oils-login-dialog td { padding: 5px; } + +/* main layout blocks */ +#oils-base-main-block { width: 100%; margin-top: 0px; padding-top: 0px;} +#oils-base-navigate-block { width: 12%; vertical-align: top; float:left;} +#oils-base-content-block { width: 87%; vertical-align: top; float:right; padding-top: 0px;} +#oils-base-sidebar-block { width: 12%; vertical-align: top; float:left;} + +#oils-base-header-auto-login { padding-right: 20px; } +#oils-base-header-block { width: 100%; text-align: right; margin-top: 0px; padding-bottom: 0px;} +#oils-base-header-menu-block { float:left; text-align: left; width: 50%; } +#oils-base-header-auto-login-block { float:right; text-align: right; width: 47%;} + +#oils-base-footer-block { width: 100%; text-align: center; vertical-align: bottom;} + +#oils-base-navigate-list { width: 100%; } +.oils-base-navigate-sub-list { padding-left: 4px; } +.oils-base-navigate-item {} + +/* general purpose form table */ +.oils-admin-table { width: 100%; } +.oils-admin-table td { padding: 4px; } +.oils-admin-table textarea { width: 400px; height: 40px; overflow:auto;} +.oils-admin-label { width: auto; } + +.label { margin: 1px; } + + +/* local dojo style enhancements ----------------------------------- */ +/* +.dojoxGrid {border: 1px solid #333; height: 90%;} +*/ +/* +.dojoxGrid {height: 90%;} +.dojoxGrid-cell {padding: 8px;} +*/ +.dijitTooltipTable td {padding: 3px;} /* custom class for handling dialog tables */ +/* ----------------------------------------------------------------- */ + diff --git a/Open-ILS/web/css/skin/default/acq.css b/Open-ILS/web/css/skin/default/acq.css new file mode 100644 index 0000000000..8b47cd1409 --- /dev/null +++ b/Open-ILS/web/css/skin/default/acq.css @@ -0,0 +1,94 @@ +.spacer {clear: both} + +#oils-acq-index-block { font-weight:bold; } +/* +.oils-sub-navigate-block { width: 100%; text-align: left; padding: 3px;} +.oils-sub-navigate-block span { padding: 3px; } +*/ + +.oils-acq-detail-content-pane {height:600px;width:100%} +.oils-acq-basic-form-table td {padding:4px;} + +/* bib search */ +#oils-acq-search-container { width:100%; } +#oils-acq-search-sources-block { width:32%; vertical-align: top; float: left; margin-right: 10px;} +#oils-acq-search-form-block { width:63%; vertical-align: top; float:right; } +#oils-acq-search-sources-selector { padding: 2px; } +#oils-acq-search-sources-selector option { margin-bottom: 2px; } +.oils-acq-search-form-row { width: 100%; } +.oils-acq-search-form-label {} +.oils-acq-search-form-input {} +#oils-acq-search-sources-list { padding: 1px; } +#oils-acq-search-sources-list li { list-style-type: none; padding-left: 0px; } +.oils-acq-search-sources-sublist { padding: 1px; list-style-type: none;} +.oils-acq-search-sources-sublist li { margin-left: 10px; } +.oils-acq-search-subsources-label { margin-top: 5px; } +#oils-acq-search-sources-label { margin-bottom: 10px; } +#oils-acq-search-fields-label { margin-bottom: 10px; } +#oils-acq-search-fields-submit-block { margin: 5px; text-align: center;} +#oils-acq-search-progress {width: 100%; text-align: center;} +#oils-acq-search-source-select option {padding: 5px;} +#oils-acq-search-fields-tbody td {padding: 3px;} + +/* list of picklists */ +#oils-acq-picklist-list-table {width: 100%;} +#oils-acq-picklist-list-table td {padding: 3px;} + +/* a single picklist */ +#oils-acq-picklist-table { width: 100%; } +#oils-acq-picklist-header { padding: 4px; margin-bottom: 20px; } +.oils-acq-picklist-records-jacket-td { width: 46px; height: 54px; } +.oils-acq-picklist-records-jacket { width: 42px; height: 54px; padding-left: 0px; } +.oils-acq-picklist-records-title {vertical-align: top} +.oils-acq-picklist-records-copies {text-align: right; width: 200px} +#oils-acq-picklist-paging-block { width: 25%; float: left; position: relative; } +#oils-acq-picklist-actions-block { width: 75%; text-align: right; float: right; position: relative; } +#oils-acq-picklist-header-subtable { width: 100%; } +#oils-acq-picklist-header-block { width: 100%; display: block } + +/* list of pos */ +#oils-acq-po-list-table {width: 100%;} +#oils-acq-po-list-table td {padding: 3px;} + +/* a single po */ +#oils-acq-po-table { width: 100%; } +#oils-acq-po-header { padding: 4px; margin-bottom: 20px; } +.oils-acq-po-records-jacket-td { width: 46px; } +.oils-acq-po-records-jacket { width: 42px; height: 54px; padding-left: 0px; } +.oils-acq-po-records-title-row {} +.oils-acq-po-records-author-row td { padding-left: 30px; } +.oils-acq-po-records-phys_desc-row td { padding-left: 30px; } +.oils-acq-po-records-phys_desc-row {} + +#oils-acq-po-paging-block { width: 50%; text-align: left;} +#oils-acq-po-actions-block { width: 50%; text-align: right;} +#oils-acq-po-header-subtable { width: 100%; } + +#oils-acq-list-header { margin: 10px; width: 98%;} +#oils-acq-list-header-label { float: left; } +#oils-acq-list-header-actions { float: right; } + +/* purchase order line item detail page */ +#oils-acq-po-li-header { padding: 4px; margin-bottom: 20px; } +#oils-acq-po-li-summary {} +#oils-acq-po-li-summary td {padding: 2px;} +.oils-acq-po-li-attr {} +.oils-acq-po-li-attr-type {} +.oils-acq-po-li-attr-name {} +.oils-acq-po-li-attr-value {} +#oils-acq-po-li-marc-block { margin-top: 10px; padding: 6px; } +#oils-acq-po-li-details-table { width: 100%; } +.oils-acq-po-li-detail-row {} + +/* picklist entry page */ +#oils-acq-lineitem-header { padding: 4px; margin-bottom: 20px; } +#oils-acq-lineitem-summary {} +#oils-acq-lineitem-summary td {padding: 2px;} +.oils-acq-lineitem-attr {} +.oils-acq-lineitem-attr-type {} +.oils-acq-lineitem-attr-name {} +.oils-acq-lineitem-attr-value {} +#oils-acq-lineitem-marc-block { margin-top: 10px; padding: 6px; } + + + diff --git a/Open-ILS/web/css/skin/default/admin.css b/Open-ILS/web/css/skin/default/admin.css new file mode 100644 index 0000000000..c10a384e5b --- /dev/null +++ b/Open-ILS/web/css/skin/default/admin.css @@ -0,0 +1,3 @@ +#oils-admin-object-actions { width: 100%; padding: 2px; margin: 2px; text-align: right;} +#oils-admin-object-table { width: 100%; } +#oils-admin-object-table td { padding: 3px; } diff --git a/Open-ILS/web/css/theme/default.css b/Open-ILS/web/css/theme/default.css new file mode 100644 index 0000000000..83043ea459 --- /dev/null +++ b/Open-ILS/web/css/theme/default.css @@ -0,0 +1,58 @@ +/* import the default css for the install applications */ +@import "default/acq.css"; +@import "default/admin.css"; + +body { font-size: 80%; } + +#oils-base-body-block {} +/* +#oils-base-navigate-block {border: 2px solid #85C777; background: #6BA160;} +#oils-base-navigate-block {border: 2px solid #f8f7f1; background: #fffdf1;} +*/ +#oils-base-navigate-block {background: #d9e8f9;} + +#oils-base-navigate-block a { color: #000000; } +#oils-base-content-block {} +#oils-base-sidebar-block {} +#oils-base-footer-block {padding: 3px; margin-top: 20px; border-top: 1px solid #5E5E5E;} +/* +#oils-base-header-block {border-bottom: 1px solid #5E5E5E; border: 2px solid #85C777; background: #6BA160;} +*/ +#oils-base-header-block {border-bottom: 1px solid #d9e8f9;} + + +#oils-base-navigate-list { width: 100%; } +.oils-base-navigate-sub-list { margin-left: 14px; } +/*.oils-base-navigate-item {border: 2px solid #85C777; background: #6BA160;}*/ +.oils-base-navigate-item:hover { background: #85C777; } + +/* +#oils-base-navigate-table td:hover { background: #85C777; } +.oils-base-sub-navigate-block { border: 2px solid #6BA160; background: #85C777;} +.oils-base-sub-navigate-block a { color: #000000; } +*/ + +#oils-base-header-user-info { font-weight: bold; margin-right: 8px;} + + +.label { font-weight: bold; } + +/* +.oils-admin-table td { border-bottom: 1px solid #5E5E5E; } +*/ +.oils-admin-table td { border-bottom: 1px solid #d9e8f9; } +.oils-admin-label { font-weight: bold; } + +.oils-acq-nav-link { + padding:0px 3px 3px 3px; + border-top:1px solid #F8F7F1; + border-bottom:1px solid #F8F7F1; + font-size: 105%; +} +.oils-acq-nav-link a { text-decoration:none; } +.oils-acq-nav-link-active { + background:#FFFDF3; + border-top:1px solid #ACA899; + border-bottom:1px solid #ACA899; +} + diff --git a/Open-ILS/web/css/theme/default/acq.css b/Open-ILS/web/css/theme/default/acq.css new file mode 100644 index 0000000000..488d738f94 --- /dev/null +++ b/Open-ILS/web/css/theme/default/acq.css @@ -0,0 +1,53 @@ + +#oils-acq-index-div { font-weight:bold; } + +#oils-acq-search-container { width:100%; } +#oils-acq-search-sources-div { width:20%; float:left; } +#oils-acq-search-form-div { width:80%; float:right; } +#oils-acq-search-z39-sources-table thead td { font-weight: bold; } +#oils-acq-search-z39-sources-table tbody td { width: 33%; } +#oils-acq-search-sources-label { font-weight: bold; border-bottom: 1px solid #6BA160;} +#oils-acq-search-fields-label { font-weight: bold; border-bottom: 1px solid #6BA160;} +#oils-acq-search-subsources-label { font-weight: bold; } +#oils-acq-search-fields-submit-block { border: 2px solid #A1A1A1; } + +/* list of picklists */ +#oils-acq-picklist-list-table {width: 100%;} +#oils-acq-picklist-list-table thead td {font-weight:bold;} + +/* picklist display */ +#oils-acq-picklist-table thead tr { border: 1px solid #A1A1A1; } +#oils-acq-picklist-header {border: 1px solid #85C777;} +#oils-acq-lineitem-header {border: 1px solid #85C777;} +#oils-acq-picklist-name { font-weight: bold; font-style: italic; } +.oils-acq-picklist-attributes { font-size: 90%; margin-left: 15px;} +.oils-acq-lineitem-attributes { font-size: 90%; margin-left: 15px;} +.oils-acq-picklist-records-phys_desc-row { border-bottom: 1px solid #6BA160; } +.oils-acq-picklist-picklist-td { border-style: solid; border-color: #A1A1A1; border-width: 0px 1px 0px 1px; } +.oils-acq-picklist-records-service-td { font-size: 85%; } +.oils-acq-lineitem-delete-link { font-size: 85%; } +#oils-acq-picklist-header-subtable tr { border: none; } + +/* po display */ +#oils-acq-po-table thead tr { border: 1px solid #A1A1A1; } +#oils-acq-po-header {border: 1px solid #85C777;} +#oils-acq-po-li-header {border: 1px solid #85C777;} +#oils-acq-po-name { font-weight: bold; font-style: italic; } +.oils-acq-po-attributes { font-size: 90%; margin-left: 15px;} +.oils-acq-po-li-attributes { font-size: 90%; margin-left: 15px;} +.oils-acq-po-records-phys_desc-row { border-bottom: 1px solid #6BA160; } +.oils-acq-po-po-td { border-style: solid; border-color: #A1A1A1; border-width: 0px 1px 0px 1px; } +.oils-acq-po-records-service-td { font-size: 85%; } +.oils-acq-po-li-delete-link { font-size: 85%; } +#oils-acq-po-header-subtable tr { border: none; } + +/* +#oils-acq-list-header {border-bottom: 1px solid #6BA160;} +*/ +#oils-acq-list-header {border-bottom: 2px solid #d9e8f9;} +#oils-acq-list-header-label { font-weight: bold; font-size: 110%; } +#oils-acq-list-header-actions { font-weight: bold; } + + +/* entry display */ +#oils-acq-lineitem-marc-block { border: 1px solid #6BA160; } diff --git a/Open-ILS/web/css/theme/default/admin.css b/Open-ILS/web/css/theme/default/admin.css new file mode 100644 index 0000000000..a79d2024bb --- /dev/null +++ b/Open-ILS/web/css/theme/default/admin.css @@ -0,0 +1 @@ +#oils-admin-object-table tr { border-bottom: 1px solid #6BA160; } diff --git a/Open-ILS/web/images/eg_logo.jpg b/Open-ILS/web/images/eg_logo.jpg new file mode 100644 index 0000000000000000000000000000000000000000..633b7a75f907159c7f6b19b714aa54e8599ab66b GIT binary patch literal 8571 zcmbVx2UHZxy7i2N0m+iHBngt4AqSBRl0h;^QidT9IZ8&NfPj)gGDBvF5>*ByXUQ1^ z0m(^$H=c9PdH4Qn-S@xys#kS=-BtZn?XKFpx~gx#-Yx@(RTWhf0YD%SkcvJ4w<}oQ z3f|D?0D!tWfC~Ts-~d3tdjJfy1w=N4^9zgfi;4>h+((}wg5nSe0DzGT{IlNMTL2*U&R_m9!g4YGvH@Xe{{R3s5aS;$ z#QtX#K)U}%0c8DW6nB7sc>r?X`P(z5{2$%`0A+OgyXAkRxBvVmf8R&v+|B{y065s# zAZ#og5D0{ei*pyBi~t`G51;xTDG?bx4I=|R4ISNmR$lh|%-k$=bQ~g_-24JyFqn~D zOiENxl2-^U_}d8(7Z(>F51)#FfJ%^wj!E!84!4~E5*z?NKnDZJ47fuA#2^9QegZI} z+lhtl_upOrj{v-bfr*6;!oj_Zhc1952HXK+VBEpPz{0{rcN{PPoe#hy!MevJAd5|^ zZ3$v_B@+xz%)?=MT+>CaGkU-(WaSoudzXTeikgP)0XqjL7Z@TeA}S^>C$FHWq^zQ< ztEd0ez|hFp+Q#|%iG5{G%P$KGU`=yQu3RW)U>zn((~UJ6c!bil$O=j z)gv1ko0?m?KYse$gX-=3GB!RjIW_%tW_D$DZGB^N>)ZCu;nDHQ>Dl?k<<)Oo=n?TJ ztiO@{Ke$NHxb9$LVqk)P;{x9CLPrb|Oe`h=?0d4>AWK(LX2D<_vd4*eHC?zYLOKWJ zR&JwrDOkZPY=^(0{ekR%2Q1|OgzRr%|BY)FfR6!0cOC`_Kn8Gb2*HD;c)lv&dYRPo z0FYp%BonTjrqw&jrA+lDLLPOFO3PM1=hUk13v1z1+*KkHCvh_E`1v3#;4No%`bJ#>RH7G zqDss9@@5JShw?r}f0>~ItW5wDzSQyV7uFm?3d4A!c#DrMq-1B^fb{Vao56}Y(Gv?I zXWhrU5sx1X@$0RL4hhA+pj!iXIEL;8+4jFv(qgd!QE$`)Urys4S7MtAecP zwKbGRPIqL)OCjGC*4&ZApSm9v`(ZQ(>W*i6p5#{9-2zDcE#{DG#-(tstcs|&WtmQ0 z4$oYa0#7;|8;&0&44EC&B4QTQtQoTv;4p8i!}W#`s<$flt(ib>H|y?O^le<%do3T* zgd%Jc`K+idR&|)a01y^74knsAXx8Zw1Ac48 z9c(O2Tw>fmr-^#t&UPi!O9w~0U=5G zUEQN&E36OX(w!;65XhdxCO9|D;rTsLs{^`T72z{M#qlz)t zer;+NQOTvmxdF@fgG4@JTw(Rbja|GOpWq6YaUZw|`b4Sv_8y*uGyM-p|G%%|-xLVq z`jYM|Ze(9pWW)cs`;yhC6Q%@xs8O0jG>&koHPyDQf>CgojFb^Z=MUEzqYpY`( zi|kJ%)vOGD2R-ZEXJHw{6+Cx?2EIv#MUS^gU7eMek9eh*FwjTinxepG8Yi-l(6U-0Ctqsnxel|sIUmWQIv%RbKh08Tr+A2QZSTvE>RnvB8C@ct3Wu)KE)#!$ zd|F!!+hFW&-|AzS0ypmy*zK_{^NomU?Kyfj?<>|jB~)uzBXvGpJ=v2kkI@v#Att&n zF$}RojtPg%smsqWvOrfHWPgakLmyE~FDh#*YF3GHRCPDO7EYx&YNmf~lzo1*Glz87 ztB&tJ9z=D$ug`gA$b(nK5xs9l z+|w77MWwDKtLzVh#r3l|2NfpSs-8xNFq47r@cJ*%>mv{nQPkQlX``HljtblYSBd=; zJ|N&x>nuF`vv=)0XJtbfEQR)BxinV!<1Q1<;W1M{as>~aUv|CxCCe{%2yeXkEPHOxG{}r?XTW3&ARM=__8`soR6<*Ol3J7sE;D4k2-U7upkbC}lb zoCngJmsD5mb6(Ffb&?o^d|pQ2sJ`irmK&^Q^jq_6tJeNvJWuxiPTF_~5JF%x%4iAq zlV;LuXtWrEaTzfsk*K*9+X;3P8gJg{YDU&I6X`k9?Bx`V$F>SNH*4#f+Og*$qSQ1c z@ZI=tgp|9C+jwW}d_A!nx-he67`AdU5sSqW!WAM8BD8Iq+5nelAJt+q{QwvpN$I8{ z4Y<$w4Z(Z1@Zkq7N-kTMJeYakxptLRd}m>6PaV#sN=C_RUeD>4n$2JNWR6L`V%aoy z{OEA{^v%0zdV+v#EwOZi*Cvfp{tZVGn1=SAY1Vu=!RHl3Fi!B!=SldMY>GyhT zn^?~SU6q`7pYeaPhf8x2_!W#sjZKJtfk2Xgh=kFZ;AatT^4OD4?IdT@)or|$yt)b!-8^1v>^5mUdP_6KcZ;t1w(gIK7R4t`{vYH0-i{yptZ|ct{iv$-UY6Isrq7{a% z)tu$m;wGmRljpxzGs;a4KT$Fx%xw4wUp@J@ZEY(Hzvz1n2Ho?!+)AfEtS$6|POYu? z<}*`Z8aI5OdTMfZvu7l$%_j9kyS~ak#idLXAzvV=+;m= zBNUW+m*d6yRvC{zN3$?;*?o-Hw<5=SfOX9}yloH8?KvGT*VsllR(Q{jwo1+uCN ze7`&&sLQfz*98-bc|*YoHBy+r#RC@c(wd`%*PFqLi}<*SJYci~o=2`&w)ECZ_xNEj z$?{dk*~d70B5Y-4>fU@D`RP`Jtp_`5?rHXAC@sOfh+Od&kvjv&y@Y)m^A&bRaj*B) zn?x^hqQaRdMPUot20E43iC)m|v=q^>2)O;v?ExdW_by#_GGKhGzKZ;g_sy>Ga3ZKO|Nb4()k7aDjRnAO11|2hWZRhbk^HT%#jdUT@#c)Gb#CIhIMX?tj zB}Yce#o_5OWoqV#l6+=*Y(`~%={UKd z@5yC$8ap!VYLHxNtR;$#bo^!;qt)b~ z-~aW;Aq%`jx{@P*#`B-OJLHj5UcK&{85ONVvc&RgG(q%R$tIgFZD+Hye&OxeEs3kj z>~Xboe(((#2Dg zH1X^BHQIgCZ9}_BUiVB6HvL1s<$XoAHH;^22e7L+06cz4*{VgZDeu2nnc2s)kvo2| z<}bx5?HqGNe8x=1E0$qpETb>(8sDRtu$eM0nfA~z7BWfGQ-)`L%kSo)-4|% z85X)Q+~$ZIc2VH z0oZ#b`T6M|E$F)t9xSCx)1Wh#gUm|LZ#OeT8{_GLycN}|wx|~%qM)3+H&UYD0?9L?kHZ(ACUOU5ISUB_|Dk z2`l?my?N8q=UX?&=WZV9nIBP0c>kae`w>fZ)wW}UNkw%O&k1-UG5FD z#A!bb_KbUrAhrQ z;Q48Q?UMH5&O<0`%1{XQy=h3nI)8E|a3-Q+aj+1)k*Z2LIxL$dy~P2*aCzS9g$QII-lwj}F{Rii4y zl91uzPv_tLO|``Y8g^v{Vb=pm*Tk({PQ{eer+ziNc*i=`LKAv3mQ`S!7C zti%$vD$m72(1SSapXA_ltJHJn{G=t%Y6Ic?vd9>oxC9qTZH9~fj8FW^bQ(sEKKkV5 zhu`MHoCrgTxO`ZoK8dX-6!29;3DgS3XX+=-c#CUQQA$#H^LPu?myf(`ZYD0tqb5jn z6|Be7TBvQ|4(BpT>73~%5DEO?-FVs6d6MkIFqWiDtMk-^&>;3fcYm?%uM6Ue3l>W6 zOZQwwBj>p=OhHg0Yid$)j*yai@RxSYL7A;)up9xB!fxCUO`*I`ew9J+8<)5fM{ z32!HYiff1I9>$#Bk3pq`CeLRchg01Y=c&%@)#c?k>Mrq}RFqb}%U8}zX|^Gd3X4DY z`blnE0eeIM0QLgnI_)s(=+Y+mdU~oD?h?x-xfw_7?nLjg6j`@u>5dOSEHfc-SWaCI zf%nGyrwtZOl3%r|tGGAmj(4%xo8dSByPAN-l6{AA$E&c@Hpa<;c@mPl1WMjLo@19e z7l2!UO8Mh&tyhyjk8`gW8uwOy(OAfYCLkyxN8MLbuYayH%8j5pu2TXT=j%nmAvl;` z8*T#!p9VLHXI_#t9F5{6R0oXM5WR3tn)lLmcRrY!+}@$}+rtX0Rk0CE8I;;X-N*T{ zwcbFVI`;+kgn!2*>|n}3osnEdZ=Yio7HKE7fB)>93B?7I{x)f_?c4aZET5USOVwAM z=u~*o?{injd*5aApBgaMqp{IK4;2-abNO_cu<-C24`%qvA$3PZ(a!2kDu8j3(ZZ065gMem8cB&EK2Kd+zT(}8W!v1oESi)4ru{b}5a zkKQayC?WpXUqtz$ht!nLVt%^eg}LUlrhqbgN@*_hY82kaI<#Q8SDJu*IUxFK3}*Z6P-2h?=Owx z`?dJB*v2SHIc8DBd>Aa?W|N>^B6H{V4@KFiJ`0*fi= zf)3tBw#a;)TM3BE0P;sgG!F+nX#d5t^m*+h!GM4*0coOnz!8|M!~2>BE$2RyZ#*c< z6&xM7^I&urR=jpzv_sOEd;~40A12(jM#pD%KJg3GRExi~0pnvz2e#hO^3JI86wgrp zDrUnGPxkHXtDE%E!m8OT+;W2HX?Wb5#-Z;0Hk^+v<;DWM*4?(S)gI`N1y-4~N*E(La9mlQ&19%KN)64yy zkDYFQxZpgq)b}{+X*F1>+PR{=%gl6=M`Xnpf5`?Mq$R!33q5APy1N})XZqkqu% zQ?rq9&4uy3Z@+t>*qo~Nie!gj6}z&U!qj2W-o#l(fVx90V$F}q(iJ24Bl~J?29r}2 z(vE{;Uk|RMC+wpV|MZ~g&c5mE-dNikRh{vO9|x9RX^u025o#N$_xsCf-&r{`>5NEs zL?&2G3x~p4bCaYXr5kW7=uYX5&AOEs3gdWTp2#bx_Io(Tq8ZY0Q_OjaL(WN^PPcpX zOD^Hz&!9qVbDlTDtFD))W?$;)@MJNGeL^IrCq=)u1X!pQ4~Pe6nc-lJyc3q+Wx(_j z`6&4-g`R-UFTj3ccHBJn$gC3lv$~ zYmjcER`3UUKnPy?*ZEckzO@ESDPVk|AFjt=&tW z)4Q}%dY>lV0)q8MiN5X**ba$z1hRS-+nA`Z7Z^eywyYmBp2ThvZ3$ud`~cI2D_ED; z*-Q9A=qo*+P3kb92l?i7{G+DO&=ASVQvS%aI!=`cafFB7yhW2YRBOcQG*Xx*-QmgBXVX#N%RXW@GI}?I7QB8HbV1fJ?^G#(vkc*eMvIH&ITsZSNYIkz*W!jJDP!FbIhld?+A$E1MF?h}SC85EXN zV`~IZHd)ThIA?b6CozA#W-K-ATh0S|j#6x1id-^=Vjni{r ziS2!`J{g>*kAWI&8_&bzL`dXiPj|R?dVH;1l=?$YYm|gwO?y!&uC6`s0CX~2FC9~G zYOu2K88mV#yWlJS<+`I)dqh`7SC?BIKU#QW=ydL@)Qqv05{!Ozpue}(gsbF95kpmF zEP7RC)Dy6RAG5|@4@=Di2f^*;ukn-+jyCZ)ye{=u5Jj2X9~3$7lpB5;Ei{~+^X7DX z6v@REwT(J<2OZ6BWB?@y(PQzOfF(G&e0_g-9c!%AO7o&%Ai%xWzZN8%F z0qX5bCTcGE(Y4W@nwmm#Yl)FeomKnqY02%Uh5ah@*=&VM5d(f1U=J}ycltx`<2|V` zKmANkCUdr<_ChfwMY7_8=|x-g7sIZ}l^D5^y14ADQ(Bggth#;U#MGBBBj>^tloX9< zk`IK5?U8N7fYangM}IJuR)`$DYOl>(=Y@|Q?@whR8)ocl4V0cti>*}U5q3!q3)Xfx zWX#+GI7)8;)%GOTjyRJ~EIHKl>etOKnWJVr2oKc#^5R>jpMFuAP@L(@1z=MWvShk# zq&VT&t=TFu2`3wS8Dq*nPJV}kf&y*2u2Vy|t+rn`cUV9xO!vMtoAY=+46AT_mX7-l z2v>0?#5ttK<6lr|^Jk%mt`f)2J8trkA+T*dNTax9K76AEFBL&}&g^`sSg!bCw{JYQ zJeM{X&ONaaA*1-6^OMmqC56D;*9q|UEOCSVT|wh>#jB_&bLh_@*9MV}#dlYV6>{%^ zjIi_ITY%yCHK0*_?meQ(ac?R*B;7duondG1duP(DbH(J~{V!AEl17zNFooHN$d;m4 zM|uJSq>9F-qOScMHgnWLiJ@pQJkpC=&zsxIrPpLlul-D7H%#xKeZ(zuXmTCINbYL! zj;fDp6TNV6vh0ivktA;9qo9T~scr1KwO3Ci@5DWX<~2o%uQ(^JjC-kej{s#FHU`}( z#%~CAWx8kzB$AN^s%DBa;Ln&TdXP%Cn!4+Sh2`Y0OBm}F;&)~Yo{u)Pnz=kHEq*EZ t2xWtjycqFxpzLCOaJ~gv6=-2C9u?*MZ~q7V>(2LovP=FiWkI(y{{^)7Un~Fs literal 0 HcmV?d00001 diff --git a/Open-ILS/web/images/eg_tiny_logo.jpg b/Open-ILS/web/images/eg_tiny_logo.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a570a371707ef486c7f7a4f66f24e6dfd9c12674 GIT binary patch literal 1722 zcmbW0c~DbF9LIO_j=YdWcsU@(kdOm{6qFo*Fgg|xNT^VW2eno$s8kfepcGrREg+>x ztxCBpQwJ3kbu2|eg#nc+a*TDvQbe3u0d3W45o7Uar|BjfqJL@k&A#97y|??@{p|Pq ztlicdz$r4^7!DwWpa@;S+KZhI+mM(5KvWd)0sv5e33&iR8AKO=VnEn)0OH}Kv3wQu z92vtvV>AHWfh+2%P;1WxDBqEj2=I})2fh znkaVnP^lqJUABBh!Uro8SFKy0_F?*lk3P=Y zlKpwk7r9&Wzb+^&DlXae&E9?G75fiV9z1mPSWWHm6DRBHe>i)t`Fu-jTl?joI)CoE z(tWk3x3B-^t>1pXedq4rL(8MTh8{n8`pm|K95IYFM)ndHjkp*%j^Ruj7i6TPj?p+F z*O0v67-pQAuh3>tf{>kis+(9!o#}xvKDEQaS*7oFAGD#_$zBaC^M8?zfj#Hy0UQh> z@h}?91Valwid&anpZUw&$J+uEirIf(xqJHDX3xD@I_p)t?FbAlp;8O-h4sWS$ayoX;H>MeAiU0-85ncG+sGOzK3F*fvB z+^p1^(tXM0o!)26-1Ei4XzrawE8Z_SoU*^U+vV3MQuB9Y`ozwLx~ckhQ&m{^qkL2T z+pA;njO&LbizD(IOK}g+lJZku`AqN64H5RQHC29cDNoo{y3rV{^Cu1OGZ(f&XRSU<8>!CQeD%cE;SblBMbhyJ~aXwdK?1Td)2HIIIDb literal 0 HcmV?d00001 diff --git a/Open-ILS/web/js/dojo/openils/Util.js b/Open-ILS/web/js/dojo/openils/Util.js new file mode 100644 index 0000000000..d5e0c442b4 --- /dev/null +++ b/Open-ILS/web/js/dojo/openils/Util.js @@ -0,0 +1,82 @@ +/* --------------------------------------------------------------------------- + * Copyright (C) 2008 Georgia Public Library Service + * Bill Erickson + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * --------------------------------------------------------------------------- + */ + + +/** + * General purpose, static utility functions + */ + +if(!dojo._hasResource["openils.Util"]) { + dojo._hasResource["openils.Util"] = true; + dojo.provide("openils.Util"); + dojo.require('openils.Event'); + dojo.declare('openils.Util', null, {}); + + + /** + * Wrapper for dojo.addOnLoad that verifies a valid login session is active + * before adding the function to the onload set + */ + openils.Util.addOnLoad = function(func, noSes) { + if(func) { + if(!noSes) { + dojo.require('openils.User'); + if(!openils.User.authtoken) + return; + } + console.log("adding onload " + func.name); + dojo.addOnLoad(func); + } + }; + + /** + * Returns true if the provided array contains the specified value + */ + openils.Util.arrayContains = function(arr, val) { + for(var i = 0; arr && i < arr.length; i++) { + if(arr[i] == val) + return true; + } + return false; + }; + + /** + * Parses opensrf response objects to see if they contain + * data and/or an ILS event. This only calls request.recv() + * once, so in a streaming context, it's necessary to loop on + * this method. + * @param r The OpenSRF Request object + * @param eventOK If true, any found events will be returned as responses. + * If false, they will be treated as error conditions and their content will + * be alerted if openils.Util.alertEvent is set to true. Also, if eventOk is + * false, the response content will be null when an event is encountered. + */ + openils.Util.alertEvent = true; + openils.Util.extractResponse = function(r, eventOk) { + var msg = r.recv(); + if(msg == null) return msg; + var val = msg.content(); + if(e = openils.Event.parse(val)) { + if(eventOk) return e; + console.log(e.toString()); + if(openils.Util.alertEvent) + alert(e); + return null; + } + return val; + }; + +} diff --git a/Open-ILS/web/js/dojo/openils/acq/CurrencyType.js b/Open-ILS/web/js/dojo/openils/acq/CurrencyType.js new file mode 100644 index 0000000000..62451fde68 --- /dev/null +++ b/Open-ILS/web/js/dojo/openils/acq/CurrencyType.js @@ -0,0 +1,55 @@ +/* --------------------------------------------------------------------------- + * Copyright (C) 2008 Georgia Public Library Service + * Bill Erickson + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * --------------------------------------------------------------------------- + */ + +if(!dojo._hasResource["openils.acq.CurrencyType"]) { + + dojo._hasResource["openils.acq.CurrencyType"] = true; + dojo.provide("openils.acq.CurrencyType"); + dojo.require('openils.User'); + + dojo.declare('openils.acq.CurrencyType', null, { + }); + + openils.acq.CurrencyType.cache = {}; + + /** + * Retrieves all of the currency types + */ + openils.acq.CurrencyType.fetchAll = function(onComplete) { + var req = new OpenSRF.ClientSession('open-ils.acq').request( + 'open-ils.acq.currency_type.all.retrieve', openils.User.authtoken); + + req.oncomplete = function(r) { + var msg = r.recv(); + var types = msg.content(); + for(var i in types) + openils.acq.CurrencyType.cache[types[i].code()] = types[i]; + onComplete(types); + } + req.send(); + } + + openils.acq.CurrencyType.loadSelectWidget = function(selector) { + openils.acq.CurrencyType.fetchAll( + function(ctypes) { + selector.store = new dojo.data.ItemFileReadStore( + {data:acqct.toStoreData(ctypes, 'code', {identifier:'code'})}); + selector.setValue(ctypes[0].code()); /* XXX get from setting */ + } + ); + } +} + diff --git a/Open-ILS/web/js/dojo/openils/acq/Fund.js b/Open-ILS/web/js/dojo/openils/acq/Fund.js new file mode 100644 index 0000000000..9ed523bd3f --- /dev/null +++ b/Open-ILS/web/js/dojo/openils/acq/Fund.js @@ -0,0 +1,213 @@ +/* --------------------------------------------------------------------------- + * Copyright (C) 2008 Georgia Public Library Service + * Bill Erickson + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * --------------------------------------------------------------------------- + */ + +if(!dojo._hasResource['openils.acq.Fund']) { +dojo._hasResource['openils.acq.Fund'] = true; +dojo.provide('openils.acq.Fund'); +dojo.require('fieldmapper.Fieldmapper'); +dojo.require('fieldmapper.dojoData'); +dojo.require('openils.Event'); + +/** Declare the Fund class with dojo */ +dojo.declare('openils.acq.Fund', null, { + /* add instance methods here if necessary */ +}); + +openils.acq.Fund.cache = {}; +openils.acq.Fund._cachecomplete = false; + +openils.acq.Fund.createStore = function(onComplete, limitPerm) { + /** Fetches the list of funds and builds a grid from them */ + + function mkStore(r) { + var msg; + var items = []; + while(msg = r.recv()) { + var src = msg.content(); + if(e = openils.Event.parse(src)) + return alert(e); + openils.acq.Fund.cache[src.id()] = src; + items.push(src); + } + openils.acq.Fund._cachecomplete = true; + onComplete(acqf.toStoreData(items)); + } + + fieldmapper.standardRequest( + ['open-ils.acq', 'open-ils.acq.fund.org.retrieve'], + { async: true, + params: [openils.User.authtoken, null, {flesh_summary:1, limit_perm:limitPerm}], + oncomplete: mkStore + } + ); +}; + +/** + * Create a new fund + * @param fields Key/value pairs used to create the new fund + */ +openils.acq.Fund.create = function(fields, onCreateComplete) { + + var fund = new acqf() + for(var field in fields) + fund[field](fields[field]); + + fieldmapper.standardRequest( + ['open-ils.acq', 'open-ils.acq.fund.create'], + { async: true, + params: [openils.User.authtoken, fund], + oncomplete: function(r) { + var msg = r.recv(); + var id = msg.content(); + if(onCreateComplete) + onCreateComplete(id); + } + } + ); +}; + + +openils.acq.Fund.createAllocation = function(fields, onCreateComplete) { + var alloc = new acqfa() + for(var field in fields) + alloc[field](fields[field]); + fieldmapper.standardRequest( + ['open-ils.acq', 'open-ils.acq.fund_allocation.create'], + { + async: true, + params: [openils.User.authtoken, alloc], + oncomplete: function(r) { + var msg = r.recv(); + var id = msg.content(); + if(onCreateComplete) + onCreateComplete(id); + } + } + ); +}; + + +/** + * Synchronous fund retrievel method + */ +openils.acq.Fund.retrieve = function(id) { + if(openils.acq.Fund.cache[id]) + return openils.acq.Fund.cache[id]; + openils.acq.Fund.cache[id] = fieldmapper.standardRequest( + ['open-ils.acq', 'open-ils.acq.fund.retrieve'], + [openils.User.authtoken, id] + ); + return openils.acq.Fund.cache[id]; +}; + + +openils.acq.Fund.deleteFromGrid = function(grid, onComplete) { + var list = [] + var selected = grid.selection.getSelected(); + for(var rowIdx in selected) + list.push(grid.model.getDatum(selected[rowIdx], 0)); + openils.acq.Fund.deleteList(list, onComplete); +}; + +openils.acq.Fund.deleteList = function(list, onComplete) { + openils.acq.Fund._deleteList(list, 0, onComplete); +} + +openils.acq.Fund._deleteList = function(list, idx, onComplete) { + if(idx >= list.length) + return onComplete(); + + var fundId = list[idx]; + delete openils.acq.Fund.cache[list[idx]]; + + fieldmapper.standardRequest( + ['open-ils.acq', 'open-ils.acq.fund.delete'], + { async: true, + params: [openils.User.authtoken, fundId], + oncomplete: function(r) { + stat = r.recv().content(); + /* XXX CHECH FOR EVENT */ + openils.acq.Fund._deleteList(list, ++idx, onComplete); + } + } + ); +}; + +openils.acq.Fund.nameMapping = function(oncomplete) { + var ids = []; + var names = []; + var buildMap = function() { + for (var i in openils.acq.Fund.cache) { + var item = openils.acq.Fund.cache[i]; + ids.push(item.id()); + names.push(item.name()); + oncomplete(ids, names); + } + }; + + if (openils.acq.Fund._cachecomplete) { + buildMap(oncomplete); + } else { + openils.acq.Fund.createStore(buildMap); + } +}; + +/** + * Sets the store for an existing openils.widget.FundFilteringSelect + * using the funds where the user has the requested permission. + * @param perm The permission to check + * @param selector The pre-created dijit.form.FilteringSelect object. + */ + +openils.acq.Fund.storeCache = []; + +openils.acq.Fund.buildPermFundSelector = function(perm, selector) { + dojo.require('dojo.data.ItemFileReadStore'); + + function hookupStore(store) { + selector.store = store; + selector.startup(); + } + + function buildPicker(r) { + var msg; + var fundList = []; + while (msg = r.recv()) { + var fund = msg.content(); + fundList.push(fund); + } + + var store = new dojo.data.ItemFileReadStore({data:acqf.toStoreData(fundList)}); + + hookupStore(store); + openils.acq.Fund.storeCache[perm] = store; + } + + if (openils.acq.Fund.storeCache[perm]) { + hookupStore(openils.acq.Fund.storeCache[perm]); + } else { + fieldmapper.standardRequest( + ['open-ils.acq', 'open-ils.acq.fund.org.retrieve'], + { params: [openils.User.authtoken, null, + {flesh_summary:1, limit_perm:perm}], + oncomplete: buildPicker, + async: true + } + ) + } +} + +} diff --git a/Open-ILS/web/js/dojo/openils/acq/FundingSource.js b/Open-ILS/web/js/dojo/openils/acq/FundingSource.js new file mode 100644 index 0000000000..d5cf3f6653 --- /dev/null +++ b/Open-ILS/web/js/dojo/openils/acq/FundingSource.js @@ -0,0 +1,140 @@ +/* --------------------------------------------------------------------------- + * Copyright (C) 2008 Georgia Public Library Service + * Bill Erickson + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * --------------------------------------------------------------------------- + */ + +if(!dojo._hasResource['openils.acq.FundingSource']) { +dojo._hasResource['openils.acq.FundingSource'] = true; +dojo.provide('openils.acq.FundingSource'); +dojo.require('fieldmapper.Fieldmapper'); +dojo.require('fieldmapper.dojoData'); + +/** Declare the FundingSource class with dojo */ +dojo.declare('openils.acq.FundingSource', null, { + /* add instance methods here if necessary */ +}); + +/** cached funding_source objects */ +openils.acq.FundingSource.cache = {}; + +openils.acq.FundingSource.createStore = function(onComplete) { + /** Fetches the list of funding_sources and builds a grid from them */ + var ses = new OpenSRF.ClientSession('open-ils.acq'); + var req = ses.request('open-ils.acq.funding_source.org.retrieve', + openils.User.authtoken, null, {flesh_summary:1}); + + req.oncomplete = function(r) { + var msg + var items = []; + var src = null; + while(msg = r.recv()) { + src = msg.content(); + openils.acq.FundingSource.cache[src.id()] = src; + items.push(src); + } + onComplete(acqfs.toStoreData(items)); + }; + + req.send(); +}; + + + +/** + * Create a new funding source object + * @param fields Key/value pairs used to create the new funding source + */ +openils.acq.FundingSource.create = function(fields, onCreateComplete) { + + var fs = new acqfs() + for(var field in fields) + fs[field](fields[field]); + + var ses = new OpenSRF.ClientSession('open-ils.acq'); + var req = ses.request('open-ils.acq.funding_source.create', openils.User.authtoken, fs); + + req.oncomplete = function(r) { + var msg = r.recv(); + var id = msg.content(); + if(onCreateComplete) + onCreateComplete(id); + }; + req.send(); +}; + +/** + * Synchronous funding_source retrievel method + */ +openils.acq.FundingSource.retrieve = function(id) { + if(openils.acq.FundingSource.cache[id]) + return openils.acq.FundingSource.cache[id]; + openils.acq.FundingSource.cache[id] = fieldmapper.standardRequest( + ['open-ils.acq', 'open-ils.acq.funding_source.retrieve'], + [openils.User.authtoken, id] + ); + return openils.acq.FundingSource.cache[id]; +}; + + +openils.acq.FundingSource.createCredit = function(fields, onCreateComplete) { + + var fsc = new acqfscred() + for(var field in fields) + fsc[field](fields[field]); + + var ses = new OpenSRF.ClientSession('open-ils.acq'); + var req = ses.request( + 'open-ils.acq.funding_source_credit.create', openils.User.authtoken, fsc); + + req.oncomplete = function(r) { + var msg = r.recv(); + var id = msg.content(); + if(onCreateComplete) + onCreateComplete(id); + }; + req.send(); +}; + + +openils.acq.FundingSource.deleteFromGrid = function(grid, onComplete) { + var list = [] + var selected = grid.selection.getSelected(); + for(var rowIdx in selected) + list.push(grid.model.getDatum(selected[rowIdx], 0)); + openils.acq.FundingSource.deleteList(list, onComplete); +}; + +openils.acq.FundingSource.deleteList = function(list, onComplete) { + openils.acq.FundingSource._deleteList(list, 0, onComplete); +} + +openils.acq.FundingSource._deleteList = function(list, idx, onComplete) { + if(idx >= list.length) + return onComplete(); + + var ses = new OpenSRF.ClientSession('open-ils.acq'); + var req = ses.request('open-ils.acq.funding_source.delete', openils.User.authtoken, list[idx]); + delete openils.acq.FundingSource.cache[list[idx]]; + + req.oncomplete = function(r) { + msg = r.recv() + stat = msg.content(); + /* XXX CHECH FOR EVENT */ + openils.acq.FundingSource._deleteList(list, ++idx, onComplete); + } + req.send(); +}; + + +} /* end dojo._hasResource[] */ diff --git a/Open-ILS/web/js/dojo/openils/acq/Lineitem.js b/Open-ILS/web/js/dojo/openils/acq/Lineitem.js new file mode 100644 index 0000000000..8325517ffb --- /dev/null +++ b/Open-ILS/web/js/dojo/openils/acq/Lineitem.js @@ -0,0 +1,239 @@ +/* --------------------------------------------------------------------------- + * Copyright (C) 2008 Georgia Public Library Service + * David J. Fiander + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * --------------------------------------------------------------------------- + */ + +if(!dojo._hasResource['openils.acq.Lineitem']) { +dojo._hasResource['openils.acq.Lineitem'] = true; +dojo.provide('openils.acq.Lineitem'); + +dojo.require('dojo.data.ItemFileWriteStore'); +dojo.require('dojox.grid.Grid'); +dojo.require('dojox.grid._data.model'); +dojo.require('fieldmapper.dojoData'); +dojo.require('openils.User'); +dojo.require('openils.Event'); + +/** Declare the Lineitem class with dojo */ +dojo.declare('openils.acq.Lineitem', null, { + /* add instance methods here if necessary */ + + constructor: function(args) { + this.lineitem = args.lineitem; + }, + + findAttr: function(name, type) { + var attrs = this.lineitem.attributes(); + if(!attrs) return null; + for(var i = 0; i < attrs.length; i++) { + var attr = attrs[i]; + if (attr.attr_type() == type && attr.attr_name() == name) + return attr.attr_value(); + } + }, + + // returns the actual price if available, otherwise estimated price, otherwise null + // priority is given to local attrs, then provider attrs, then MARC attrs + getPrice: function() { + return this.getActualPrice() || this.getEstimatedPrice(); + }, + + // returns the actual price, null if none + getActualPrice : function() { + return this._getPriceAttr('actual_price'); + }, + + // returns the estimated price, null if none + getEstimatedPrice : function() { + return this._getPriceAttr('estimated_price'); + }, + + _getPriceAttr : function(attr) { + var types = [ + 'lineitem_local_attr_definition', + 'lineitem_provider_attr_definition', + 'lineitem_marc_attr_definition' + ]; + + for(var t in types) { + if(price = this.findAttr(attr, types[t])) + return {price:price, source_type: attr, source_attr: types[t]}; + } + + return null; + }, + + update: function(oncomplete) { + fieldmapper.standardRequest( + ['open-ils.acq', 'open-ils.acq.lineitem.update'], + { async: true, + params: [openils.User.authtoken, this.lineitem], + oncomplete: function(r) { + oncomplete(openils.Event.parse(r.recv().content())); + } + } + ); + }, + + approve: function(oncomplete) { + fieldmapper.standardRequest( + ['open-ils.acq', 'open-ils.acq.lineitem.approve'], + { async: true, + params: [openils.User.authtoken, this.lineitem.id()], + oncomplete: function(r) { + oncomplete(openils.Event.parse(r.recv().content())); + } + }); + }, + + id: function() { + return this.lineitem.id(); + }, +}); + +openils.acq.Lineitem.ModelCache = {}; +openils.acq.Lineitem.acqlidCache = {}; + +openils.acq.Lineitem.createLIDStore = function(li_id, onComplete) { + // Fetches the details of a lineitem and builds a grid + + function mkStore(r) { + var msg; + var items = []; + while (msg = r.recv()) { + var data = msg.content(); + for (i in data.lineitem_details()) { + var lid = data.lineitem_details()[i]; + items.push(lid); + openils.acq.Lineitem.acqlidCache[lid.id()] = lid; + } + } + + onComplete(acqlid.toStoreData(items)); + } + + fieldmapper.standardRequest( + ['open-ils.acq', 'open-ils.acq.lineitem.retrieve'], + { async: true, + params: [openils.User.authtoken, li_id, + {flesh_attrs:1, flesh_li_details:1}], + oncomplete: mkStore + }); +}; + +openils.acq.Lineitem.alertOnLIDSet = function(griditem, attr, oldVal, newVal) { + var item; + var updateDone = function(r) { + var stat = r.recv().content(); + var evt = openils.Event.parse(stat); + + if (evt) { + alert("Error: "+evt.desc); + console.dir(evt); + if (attr == "fund") { + item.fund(oldVal); + griditem.fund = oldVal; + } else if (attr == "owning_lib") { + item.owning_lib(oldVal); + griditem.owning_lib = oldVal; + } + } + }; + + if (oldVal == newVal) { + return; + } + + item = openils.acq.Lineitem.acqlidCache[griditem.id]; + + if (attr == "fund") { + item.fund(newVal); + } else if (attr == "owning_lib") { + item.owning_lib(newVal); + } else if (attr == "cn_label") { + item.cn_label(newVal); + } else if (attr == "barcode") { + item.barcode(newVal); + } else if (attr == "location") { + item.location(newVal); + } else { + alert("Unexpected attr in Lineitem.alertOnSet: '"+attr+"'"); + return; + } + + fieldmapper.standardRequest( + ["open-ils.acq", "open-ils.acq.lineitem_detail.update"], + { params: [openils.User.authtoken, item], + oncomplete: updateDone + }); +}; + +openils.acq.Lineitem.deleteLID = function(id, onComplete) { + fieldmapper.standardRequest( + ['open-ils.acq', 'open-ils.acq.lineitem_detail.delete'], + { async: true, + params: [openils.User.authtoken, id], + oncomplete: function(r) { + msg = r.recv() + stat = msg.content(); + onComplete(openils.Event.parse(stat)); + } + }); +}; + +openils.acq.Lineitem.createLID = function(fields, onCreateComplete) { + var lid = new acqlid() + for (var field in fields) { + lid[field](fields[field]); + } + + fieldmapper.standardRequest( + ['open-ils.acq', 'open-ils.acq.lineitem_detail.create'], + { async: true, + params: [openils.User.authtoken, lid, {return_obj:1}], + oncomplete: function(r) { + var msg = r.recv(); + var obj = msg.content(); + openils.Event.parse_and_raise(obj); + if (onCreateComplete) { + onCreateComplete(obj); + } + } + }); +}; + +openils.acq.Lineitem.loadLIDGrid = function(domNode, id, layout) { + if (!openils.acq.Lineitem.ModelCache[id]) { + openils.acq.Lineitem.createLIDStore(id, + function(storeData) { + var store = new dojo.data.ItemFileWriteStore({data:storeData}); + var model = new dojox.grid.data.DojoData(null, store, + {rowsPerPage: 20, clientSort:true, query:{id:'*'}}); + + dojo.connect(store, "onSet", + openils.acq.Lineitem.alertOnLIDSet); + openils.acq.Lineitem.ModelCache[id] = model; + + domNode.setStructure(layout); + domNode.setModel(model); + domNode.update(); + }); + } else { + domNode.setModel(openils.acq.Lineitem.ModelCache[id]); + domNode.setStructure(layout); + domNode.update(); + domNode.refresh(); + } +}; +} diff --git a/Open-ILS/web/js/dojo/openils/acq/LineitemAttr.js b/Open-ILS/web/js/dojo/openils/acq/LineitemAttr.js new file mode 100644 index 0000000000..655914a5f7 --- /dev/null +++ b/Open-ILS/web/js/dojo/openils/acq/LineitemAttr.js @@ -0,0 +1,55 @@ +/* --------------------------------------------------------------------------- + * Copyright (C) 2008 Georgia Public Library Service + * Bill Erickson + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * --------------------------------------------------------------------------- + */ +if(!dojo._hasResource['openils.acq.LineitemAttr']) { +dojo._hasResource['openils.acq.LineitemAttr'] = true; +dojo.provide('openils.acq.LineitemAttr'); +dojo.require('fieldmapper.dojoData'); +dojo.require('openils.User'); +dojo.require('openils.Event'); + +/** Declare the LineitemAttr class with dojo */ +dojo.declare('openils.acq.LineitemAttr', null, {}); + +/** Pile of static methods for handling the different types of + * lineitem attributes and definitions + */ + + +/** + * Creates a set of attr definition stores, one per definition type. + */ +openils.acq.LineitemAttr.createAttrDefStores = function(onload) { + function process(r) { + var res = r.recv().content(); + openils.Event.parse_and_raise(res); + var stores = {}; + stores.marc = acqlimad.toStoreData(res.marc); + stores.usr = acqliuad.toStoreData(res.usr); + stores.local = acqlilad.toStoreData(res.local); + stores.generated = acqligad.toStoreData(res.generated); + stores.provider = acqlipad.toStoreData(res.provider); + onload(stores); + } + + fieldmapper.standardRequest( + ['open-ils.acq', 'open-ils.acq.lineitem_attr_definition.retrieve.all'], + { async: true, + params: [openils.User.authtoken], + oncomplete: process + } + ); +} +} diff --git a/Open-ILS/web/js/dojo/openils/acq/PO.js b/Open-ILS/web/js/dojo/openils/acq/PO.js new file mode 100644 index 0000000000..26c859fb13 --- /dev/null +++ b/Open-ILS/web/js/dojo/openils/acq/PO.js @@ -0,0 +1,76 @@ +/* --------------------------------------------------------------------------- + * Copyright (C) 2008 Georgia Public Library Service + * Bill Erickson + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * --------------------------------------------------------------------------- + */ + +if(!dojo._hasResource['openils.acq.PO']) { + + dojo._hasResource['openils.acq.PO'] = true; + dojo.provide('openils.acq.PO'); + dojo.require('fieldmapper.Fieldmapper'); + dojo.require('fieldmapper.dojoData'); + dojo.require('openils.Util'); + + /** Declare the PO class with dojo */ + dojo.declare('openils.acq.PO', null, { + /* add instance methods here if necessary */ + }); + + openils.acq.PO.cache = {}; + + openils.acq.PO.retrieve = function(id, oncomplete, args) { + + var req = ['open-ils.acq', 'open-ils.acq.purchase_order.retrieve']; + var par = [openils.User.authtoken, id, args]; + + if(oncomplete) { + fieldmapper.standardRequest( + req, + { params:par, + async: true, + oncomplete:function(r) { + var po = openils.Util.extractResponse(r) + if(po) { + openils.acq.PO.cache[po.id()] = po; + oncomplete(po); + } + } + } + ); + } else { + return openils.acq.PO.cache[po.id()] = + fieldmapper.standardRequest(req, par); + } + } + + openils.acq.PO.create = function(po, oncomplete) { + var req = ['open-ils.acq', 'open-ils.acq.purchase_order.create']; + var par = [openils.User.authtoken, po]; + + fieldmapper.standardRequest( + req, + { params: par, + async: true, + oncomplete: function(r) { + var po_id = r.recv().content(); + po.id(po_id); + openils.acq.PO.cache[po_id] = po; + oncomplete(po_id); + } + } + ); + } +}; + + diff --git a/Open-ILS/web/js/dojo/openils/acq/Picklist.js b/Open-ILS/web/js/dojo/openils/acq/Picklist.js new file mode 100644 index 0000000000..ea8446556f --- /dev/null +++ b/Open-ILS/web/js/dojo/openils/acq/Picklist.js @@ -0,0 +1,166 @@ +/* --------------------------------------------------------------------------- + * Copyright (C) 2008 Georgia Public Library Service + * David J. Fiander + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * --------------------------------------------------------------------------- + */ + +if(!dojo._hasResource['openils.acq.Picklist']) { +dojo._hasResource['openils.acq.Picklist'] = true; +dojo.provide('openils.acq.Picklist'); + +dojo.require('dojo.data.ItemFileWriteStore'); +dojo.require('dojox.grid.Grid'); +dojo.require('dojox.grid._data.model'); +dojo.require('fieldmapper.Fieldmapper'); +dojo.require('fieldmapper.dojoData'); +dojo.require('openils.Event'); + +/** Declare the Picklist class with dojo */ +dojo.declare('openils.acq.Picklist', null, { + + constructor: function (pl_id, onComplete, args) { + + var pl_this = this; // 'this' doesn't exist inside callbacks + var liArgs = (args && args.liArgs) ? args.liArgs : {flesh_attrs:1, clear_marc:1}; + var mkStore = function (r) { + var storeData; + var msg; + pl_this._items = []; + + while (msg = r.recv()) { + var data = msg.content(); + pl_this._data[data.id()] = data; + pl_this._items.push(data); + } + + storeData = jub.toStoreData(pl_this._items, null, {virtualFields:['estimated_price', 'actual_price']}); + pl_this._store = new dojo.data.ItemFileWriteStore({data:storeData}); + pl_this._model = new dojox.grid.data.DojoData(null, pl_this._store, + {rowsPerPage:20, clientSort:true, + query:{id:'*'}}); + onComplete(pl_this._model); + }; + + this._id = pl_id; + this._data = {}; + this._plist = null; + // + // Fetch the picklist information + fieldmapper.standardRequest( + ['open-ils.acq', 'open-ils.acq.picklist.retrieve'], + { async: false, + params: [openils.User.authtoken, pl_id, {flesh_lineitem_count:1}], + oncomplete: function(r) { + var pl = r.recv().content(); + if(e = openils.Event.parse(pl)) + return alert(pl); + pl_this._plist = pl; + } + }); + + // Fetch the title list for the picklist, asynchronously + fieldmapper.standardRequest( + ['open-ils.acq', 'open-ils.acq.lineitem.picklist.retrieve'], + { async: true, + params: [openils.User.authtoken, pl_id, liArgs], + oncomplete: mkStore + }); + }, + + id: function () { + return this._id; + }, + name: function() { + return this._plist.name(); + }, + owner: function() { + return this._plist.owner(); + }, + create_time: function() { + return this._plist.create_time(); + }, + edit_time: function() { + return this._plist.edit_time(); + }, + + find_attr: function(id, at_name, at_type) { + attr_list = this._data[id].attributes(); + for (var i in attr_list) { + var attr = attr_list[i]; + if (attr.attr_type() == at_type && attr.attr_name() == at_name) { + return attr.attr_value(); + } + } + return ''; + }, +}); + +/** Creates a new picklist. fields.name is required */ +openils.acq.Picklist.create = function(fields, oncomplete) { + var picklist = new acqpl(); + picklist.owner(fields.owner || new openils.User().user.id()); + picklist.name(fields.name); + + fieldmapper.standardRequest( + ['open-ils.acq', 'open-ils.acq.picklist.create'], + { async: true, + params: [openils.User.authtoken, picklist], + oncomplete: function(r) { + // XXX event/error handling + oncomplete(r.recv().content()); + } + } + ); +} + +/** Creates a new picklist. fields.name is required */ +openils.acq.Picklist.update = function(picklist, oncomplete) { + fieldmapper.standardRequest( + ['open-ils.acq', 'open-ils.acq.picklist.update'], + { async: true, + params: [openils.User.authtoken, picklist], + oncomplete: function(r) { + // XXX event/error handling + oncomplete(r.recv().content()); + } + } + ); +} + +/** Deletes a list of picklists + * @param list Array of picklist IDs + */ +openils.acq.Picklist.deleteList = function(list, onComplete) { + openils.acq.Picklist._deleteList(list, 0, onComplete); +} + +/* iterate through the list of IDs deleting asynchronously as we go... */ +openils.acq.Picklist._deleteList = function(list, idx, onComplete) { + if(idx >= list.length) + return onComplete(); + fieldmapper.standardRequest( + ['open-ils.acq', 'open-ils.acq.picklist.delete'], + { async: true, + params: [openils.User.authtoken, list[idx]], + oncomplete: function(r) { + msg = r.recv() + stat = msg.content(); + /* XXX CHECH FOR EVENT */ + openils.acq.Picklist._deleteList(list, ++idx, onComplete); + } + } + ); +} + +} + diff --git a/Open-ILS/web/js/dojo/openils/acq/Provider.js b/Open-ILS/web/js/dojo/openils/acq/Provider.js new file mode 100644 index 0000000000..9803bd00e3 --- /dev/null +++ b/Open-ILS/web/js/dojo/openils/acq/Provider.js @@ -0,0 +1,174 @@ +/* --------------------------------------------------------------------------- + * Copyright (C) 2008 Georgia Public Library Service + * Bill Erickson + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * --------------------------------------------------------------------------- + */ + +if(!dojo._hasResource['openils.acq.Provider']) { +dojo._hasResource['openils.acq.Provider'] = true; +dojo.provide('openils.acq.Provider'); +dojo.require('fieldmapper.Fieldmapper'); +dojo.require('fieldmapper.dojoData'); + +/** Declare the Provider class with dojo */ +dojo.declare('openils.acq.Provider', null, { + /* add instance methods here if necessary */ +}); + +openils.acq.Provider.cache = {}; + +/* define some static provider methods ------- */ + +openils.acq.Provider.createStore = function(onComplete, limitPerm) { + /** Fetches the list of funding_sources and builds a grid from them */ + + function mkStore(r) { + var msg; + var items = []; + while(msg = r.recv()) { + var provider = msg.content(); + openils.acq.Provider.cache[provider.id()] = provider; + items.push(provider); + } + onComplete(acqpro.toStoreData(items)); + } + + fieldmapper.standardRequest( + ['open-ils.acq', 'open-ils.acq.provider.org.retrieve'], + { async: true, + params: [openils.User.authtoken], + oncomplete: mkStore + } + ); +}; + + +/** + * Synchronous provider retrievel method + */ +openils.acq.Provider.retrieve = function(id) { + if(openils.acq.Provider.cache[id]) + return openils.acq.Provider.cache[id]; + + openils.acq.Provider.cache[id] = + fieldmapper.standardRequest( + ['open-ils.acq', 'open-ils.acq.provider.retrieve'], + [openils.User.authtoken, id] + ); + return openils.acq.Provider.cache[id]; +}; + +openils.acq.Provider.create = function(fields, oncomplete) { + var provider = new acqpro() + for(var field in fields) + provider[field](fields[field]); + + fieldmapper.standardRequest( + ['open-ils.acq', 'open-ils.acq.provider.create'], + { async: true, + params: [openils.User.authtoken, provider], + oncomplete: function(r) { + var msg = r.recv(); + var id = msg.content(); + if(oncomplete) + oncomplete(id); + } + } + ); +}; + + +openils.acq.Provider.retrieveLineitemProviderAttrDefs = function(providerId, oncomplete) { + fieldmapper.standardRequest( + ['open-ils.acq', 'open-ils.acq.lineitem_provider_attr_definition.provider.retrieve.atomic'], + { async: true, + params: [openils.User.authtoken, providerId], + oncomplete: function(r) {oncomplete(r.recv().content());} + } + ); +} + +openils.acq.Provider.createLineitemProviderAttrDef = function(fields, oncomplete) { + var attr = new acqlipad(); + for(var field in fields) + attr[field](fields[field]); + + fieldmapper.standardRequest( + ['open-ils.acq', 'open-ils.acq.lineitem_provider_attr_definition.create'], + { async: true, + params: [openils.User.authtoken, attr], + oncomplete: function(r) {oncomplete(r.recv().content());} + } + ); +} + + +openils.acq.Provider.lineitemProviderAttrDefDeleteList = function(list, oncomplete) { + openils.acq.Provider._lineitemProviderAttrDefDeleteList(list, 0, oncomplete); +} + +openils.acq.Provider._lineitemProviderAttrDefDeleteList = function(list, idx, oncomplete) { + if(idx >= list.length) + return oncomplete(); + fieldmapper.standardRequest( + ['open-ils.acq', 'open-ils.acq.lineitem_provider_attr_definition.delete'], + { async: true, + params: [openils.User.authtoken, list[idx]], + oncomplete: function(r) { + msg = r.recv() + stat = msg.content(); + /* XXX CHECH FOR EVENT */ + openils.acq.Provider._lineitemProviderAttrDefDeleteList(list, ++idx, oncomplete); + } + } + ); +} + +openils.acq.Provider.storeCache = []; + +openils.acq.Provider.buildPermProviderSelector = function(perm, selector) { + dojo.require('dojo.data.ItemFileReadStore'); + + function hookupStore(store) { + selector.store = store; + selector.startup(); + } + + function buildPicker(r) { + var msg; + var providerList = []; + while (msg = r.recv()) { + var provider = msg.content(); + providerList.push(provider); + } + + var store = new dojo.data.ItemFileReadStore({data:acqpro.toStoreData(providerList)}); + + hookupStore(store); + openils.acq.Provider.storeCache[perm] = store; + } + + if (openils.acq.Provider.storeCache[perm]) { + hookupStore(openils.acq.Provider.storeCache[perm]); + } else { + fieldmapper.standardRequest( + ['open-ils.acq', 'open-ils.acq.provider.org.retrieve'], + { params: [openils.User.authtoken, null, + {flesh_summary:1, limit_perm:perm}], + oncomplete: buildPicker, + async: true + } + ) + } +} +} diff --git a/Open-ILS/web/js/dojo/openils/editors.js b/Open-ILS/web/js/dojo/openils/editors.js new file mode 100644 index 0000000000..2b83304c66 --- /dev/null +++ b/Open-ILS/web/js/dojo/openils/editors.js @@ -0,0 +1,83 @@ +if(!dojo._hasResource["openils.editors"]){ +dojo._hasResource["openils.editors"] = true; +dojo.provide("openils.editors"); + +dojo.require("dojox.grid._data.dijitEditors"); +dojo.require("dojox.grid._data.editors"); +dojo.require("dijit.form.NumberSpinner"); +dojo.require('dijit.form.FilteringSelect'); + +dojo.require("openils.widget.FundSelector"); +dojo.require("openils.widget.ProviderSelector"); +dojo.require("openils.widget.OrgUnitFilteringSelect"); + +dojo.require("openils.acq.Fund"); + +dojo.declare("openils.editors.NumberSpinner", dojox.grid.editors.Dijit, { + editorClass: "dijit.form.NumberSpinner", + + getvalue: function() { + var e = this.editor; + // make sure to apply the displayed value + e.setDisplayedValue(e.getDisplayedValue()); + return e.getValue(); + }, + + getEditorProps: function(inDatum){ + return dojo.mixin({}, this.cell.editorProps||{}, { + constraints: dojo.mixin({}, this.cell.constraints) || {}, + value: inDatum + }); + }, +}); + +dojo.declare('openils.editors.FundSelectEditor', dojox.grid.editors.Dijit, { + editorClass: "openils.widget.FundSelector", + createEditor: function(inNode, inDatum, inRowIndex) { + var editor = new this.editorClass(this.getEditorProps(inDatum), inNode); + openils.acq.Fund.buildPermFundSelector(this.cell.perm || this.perm, + editor); + return editor; + }, +}); + +dojo.declare('openils.editors.ProviderSelectEditor', dojox.grid.editors.Dijit, { + editorClass: "openils.widget.ProviderSelector", + createEditor: function(inNode, inDatum, inRowIndex) { + console.log("openils.widget.ProviderSelectEditor"); + var editor = new this.editorClass(this.getEditorProps(inDatum), inNode); + openils.acq.Provider.buildPermProviderSelector(this.cell.perm || this.perm, + editor); + return editor; + }, +}); + +dojo.declare('openils.editors.OrgUnitSelectEditor', dojox.grid.editors.Dijit, { + editorClass: "openils.widget.OrgUnitFilteringSelect", + createEditor: function(inNode, inDatum, inRowIndex) { + var editor = new this.editorClass(this.getEditorProps(inDatum), inNode); + new openils.User().buildPermOrgSelector(this.cell.perm || this.perm, editor); + editor.setValue(inDatum); + return editor; + }, +}); + +dojo.declare('openils.editors.CopyLocationSelectEditor', dojox.grid.editors.Dijit, { + editorClass: "dijit.form.FilteringSelect", + createEditor: function(inNode, inDatum, inRowIndex) { + dojo.require('openils.CopyLocation'); + var editor = new this.editorClass(this.getEditorProps(inDatum), inNode); + openils.CopyLocation.createStore(1, /* XXX how do we propagate arguments to the editor?? */ + function(store) { + editor.store = new dojo.data.ItemFileReadStore({data:store}); + editor.startup(); + if(inDatum) + editor.setValue(inDatum); + } + ); + return editor; + }, +}); + +} + diff --git a/Open-ILS/web/js/dojo/openils/widget/FundSelector.js b/Open-ILS/web/js/dojo/openils/widget/FundSelector.js new file mode 100644 index 0000000000..38442cd494 --- /dev/null +++ b/Open-ILS/web/js/dojo/openils/widget/FundSelector.js @@ -0,0 +1,18 @@ +if(!dojo._hasResource["openils.widget.FundSelector"]){ + dojo._hasResource["openils.widget.FundSelector"] = true; + dojo.provide("openils.widget.FundSelector"); + + dojo.require('openils.acq.Fund'); + dojo.require('fieldmapper.Fieldmapper'); + dojo.require('fieldmapper.dojoData'); + + /** + * This widget provides a specific selector for selecting + * a fund. + */ + + dojo.declare( + "openils.widget.FundSelector", [dijit.form.FilteringSelect], + { + }); +} diff --git a/Open-ILS/web/js/dojo/openils/widget/ProviderSelector.js b/Open-ILS/web/js/dojo/openils/widget/ProviderSelector.js new file mode 100644 index 0000000000..460bf2b5d7 --- /dev/null +++ b/Open-ILS/web/js/dojo/openils/widget/ProviderSelector.js @@ -0,0 +1,20 @@ +if(!dojo._hasResource["openils.widget.ProviderSelector"]){ + dojo._hasResource["openils.widget.ProviderSelector"] = true; + dojo.provide("openils.widget.ProviderSelector"); + + dojo.require('openils.acq.Provider'); + dojo.require('fieldmapper.Fieldmapper'); + dojo.require('fieldmapper.dojoData'); + + /** + * This widget provides a specific selector for selecting + * a provider. + */ + + dojo.declare( + "openils.widget.ProviderSelector", [dijit.form.FilteringSelect], + { + labelAttr: 'code', + searchAttr: 'code' + }); +} diff --git a/Open-ILS/web/js/ui/base.js b/Open-ILS/web/js/ui/base.js new file mode 100644 index 0000000000..1c3c2491bf --- /dev/null +++ b/Open-ILS/web/js/ui/base.js @@ -0,0 +1,34 @@ +dojo.require('dijit.Dialog'); +dojo.require('fieldmapper.dojoData'); +dojo.require('openils.User'); +dojo.require('dojo.cookie'); +dojo.require('openils.CGI'); +dojo.require('openils.Event'); + +function oilsSetupUser() { + var authtoken = new openils.CGI().param('ses') || dojo.cookie('ses'); + var user; + if(authtoken) user = new openils.User({authtoken:authtoken}); + if(!authtoken || openils.Event.parse(user.user)) { + dojo.cookie('ses', openils.User.authtoken, {expires:-1, path:'/'}); + openils.User.authtoken = null; + dojo.addOnLoad(function(){oilsLoginDialog.show();}); + return; + } + dojo.cookie('ses', authtoken, {path : oilsCookieBase}); + openils.User.authtoken = authtoken; +} + +function oilsDoLogin() { + var user = new openils.User(); + user.login({ + username: dojo.byId('oils-login-username').value, + passwd: dojo.byId('oils-login-password').value, + type: 'staff' // hardcode for now + }); + dojo.cookie('ses', user.authtoken, {path : oilsCookieBase}); + return true; +} + +oilsSetupUser(); + diff --git a/Open-ILS/web/js/ui/default/acq/common/jubgrid.js b/Open-ILS/web/js/ui/default/acq/common/jubgrid.js new file mode 100644 index 0000000000..6a0c451aef --- /dev/null +++ b/Open-ILS/web/js/ui/default/acq/common/jubgrid.js @@ -0,0 +1,382 @@ +dojo.require('dojo.data.ItemFileReadStore'); +dojo.require('dijit.layout.SplitContainer'); +dojo.require('dijit.Dialog'); +dojo.require('dijit.form.FilteringSelect'); +dojo.require('dijit.form.Button'); +dojo.require('dojox.grid.Grid'); +dojo.require('dojo.date.locale'); +dojo.require('dojo.date.stamp'); + + +dojo.require("openils.User"); +dojo.require("openils.acq.Fund"); +dojo.require("openils.acq.Lineitem"); +dojo.require('openils.acq.Provider'); +dojo.require("openils.widget.FundSelector"); +dojo.require('openils.editors'); +dojo.require('openils.Event'); +dojo.require("openils.widget.OrgUnitFilteringSelect"); +dojo.require("fieldmapper.OrgUtils"); + +/* put all the accessors, etc. into a local object for namespacing */ +var JUBGrid = { + jubGrid : null, + lineitems : [], // full list of lineitem objects to display + getLi : function(id) { + // given an ID, returns the lineitem object from the list + for(var i in JUBGrid.lineitems) { + var li = JUBGrid.lineitems[i]; + if(li.id() == id) + return li; + } + }, + + _getMARCAttr : function(rowIndex, attr) { + var data = JUBGrid.jubGrid.model.getRow(rowIndex); + if (!data) return ''; + return new openils.acq.Lineitem( + {lineitem:JUBGrid.getLi(data.id)}).findAttr(attr, 'lineitem_marc_attr_definition') + }, + getJUBTitle : function(rowIndex) { + return JUBGrid._getMARCAttr(rowIndex, 'title'); + }, + getJUBAuthor : function(rowIndex) { + return JUBGrid._getMARCAttr(rowIndex, 'author'); + }, + getJUBIsbn : function(rowIndex) { + return JUBGrid._getMARCAttr(rowIndex, 'isbn'); + }, + getJUBActualPrice : function(rowIndex) { + var data = JUBGrid.jubGrid.model.getRow(rowIndex); + if (!data) return ''; + var price = new openils.acq.Lineitem( + {lineitem:JUBGrid.getLi(data.id)}).getActualPrice(); + if(price) return price.price; + return '' + }, + getJUBEstimatedPrice : function(rowIndex) { + var data = JUBGrid.jubGrid.model.getRow(rowIndex); + if (!data) return ''; + var price = new openils.acq.Lineitem( + {lineitem:JUBGrid.getLi(data.id)}).getEstimatedPrice(); + if(price) return price.price; + return '' + }, + getJUBPubdate : function(rowIndex) { + return JUBGrid._getMARCAttr(rowIndex, 'pubdate'); + }, + getProvider : function(rowIndex) { + data = JUBGrid.jubGrid.model.getRow(rowIndex); + if(!data || !data.provider) return; + return openils.acq.Provider.retrieve(data.provider).code(); + }, + getRecvTime : function(rowIndex) { + var data = JUBGrid.jubDetailGrid.model.getRow(rowIndex); + if (!(data && data.recv_time)) return ''; + var date = dojo.date.stamp.fromISOString(data.recv_time); + return dojo.date.locale.format(date, {formatLength:'medium'}); + }, + getCopyLocation : function(rowIndex) { + var data = JUBGrid.jubDetailGrid.model.getRow(rowIndex); + if(!data || !data.location) return ''; + return openils.CopyLocation.retrieve(data.location).name(); + }, + getLIDFundName : function(rowIndex) { + var data = JUBGrid.jubDetailGrid.model.getRow(rowIndex); + if (!data || !data.fund) return; + try { + return openils.acq.Fund.retrieve(data.fund).name(); + } catch (evt) { + return data.fund; + } + }, + getLIDFundCode : function(rowIndex) { + var data = JUBGrid.jubDetailGrid.model.getRow(rowIndex); + if (!data || !data.fund) return; + try { + return openils.acq.Fund.retrieve(data.fund).code(); + } catch (evt) { + return data.fund; + } + }, + getLIDLibName : function(rowIndex) { + var data = JUBGrid.jubDetailGrid.model.getRow(rowIndex); + if (!data || !data.owning_lib) return; + return fieldmapper.aou.findOrgUnit(data.owning_lib).shortname(); + }, + + gridDataChanged : function(newVal, rowIdx, cellIdx) { + // cellIdx == -1 if you are editing a column that + // is not represented in the data model. Khaaaaaaan!!! + }, + + populate : function(gridWidget, model, lineitems) { + for (var i in lineitems) { + JUBGrid.lineitems[lineitems[i].id()] = lineitems[i]; + } + JUBGrid.jubGrid = gridWidget; + JUBGrid.jubGrid.setModel(model); + if(JUBGrid.showDetails) { + dojo.connect(gridWidget, "onRowClick", + function(evt) { + var jub = model.getRow(evt.rowIndex); + var grid; + + JUBGrid.jubDetailGrid.lineitemID = jub.id; + + //if (jub.state == "approved") { + if (false) { // need finer grained control here + grid = JUBGrid.jubDetailGridLayoutReadOnly; + } else { + grid = JUBGrid.jubDetailGridLayout; + } + openils.acq.Lineitem.loadLIDGrid( + JUBGrid.jubDetailGrid, + JUBGrid.jubGrid.model.getRow(evt.rowIndex).id, grid); + } + ); + } + // capture changes to lineitems + dojo.connect(model.store, "onSet", JUBGrid.onJUBSet); + gridWidget.update(); + }, + + approveJUB: function(evt) { + var list = []; + var selected = JUBGrid.jubGrid.selection.getSelected(); + for (var idx = 0; idx < selected.length; idx++) { + var rowIdx = selected[idx]; + JUBGrid.approveSingleJUB(JUBGrid.jubGrid.model.getRow(rowIdx)); + } + }, + + approveSingleJUB: function(jub) { + var li = new openils.acq.Lineitem({lineitem:JUBGrid.getLi(jub.id)}); + var approveStore = function(evt) { + if (evt) { + // something bad happened + console.log("jubgrid.js: approveJUB: error:"); + console.dir(evt); + alert("Error: "+evt.desc); + } else { + var approveACQLI = function(jub, rq) { + JUBGrid.jubGrid.model.store.setValue(jub, "state", "approved"); + JUBGrid.jubGrid.model.refresh(); + JUBGrid.jubGrid.update(); + // Reload lineitem details, read-only + //openils.acq.Lineitem.loadLIDGrid(JUBGrid.jubDetailGrid, li.id(), JUBGrid.jubDetailGridLayout); + //JUBGrid.jubDetailGridLayoutReadOnly); + }; + JUBGrid.jubGrid.model.store.fetch({query:{id:jub.id}, onItem: approveACQLI}); + } + }; + + li.approve(approveStore); + }, + + + removeSelectedJUBs: function(evt) { + + function deleteList(list, idx, oncomplete) { + if(idx >= list.length) + return oncomplete(); + fieldmapper.standardRequest([ + 'open-ils.acq', + 'open-ils.acq.lineitem.delete'], + { async: true, + params: [openils.User.authtoken, list[idx].id()], + oncomplete: function(r) { + var res = r.recv().content(); + if(openils.Event.parse(res)) + alert(openils.Event.parse(res)); + deleteList(list, ++idx, oncomplete); + } + } + ); + } + + var lineitems = JUBGrid.lineitems; + var deleteMe = []; + var keepMe = []; + var selected = JUBGrid.jubGrid.selection.getSelected(); + + for(var id in lineitems) { + var deleted = false; + for(var i = 0; i < selected.length; i++) { + var rowIdx = selected[i]; + var jubid = JUBGrid.jubGrid.model.getRow(rowIdx).id; + if(jubid == id) { + if (lineitems[id].state() == 'new') { + deleteMe.push(lineitems[id]); + deleted = true; + } else { + alert("Can not delete line item "+id+ + ": item is "+lineitems[id].state()); + } + } + } + if(!deleted) + keepMe[id] = lineitems[id]; + } + + JUBGrid.lineitems = keepMe; + deleteList(deleteMe, 0, function(){ + JUBGrid.jubGrid.model.store = + new dojo.data.ItemFileReadStore({data:jub.toStoreData(keepMe)}); + JUBGrid.jubGrid.model.refresh(); + JUBGrid.jubGrid.update(); + }); + }, + + deleteLID: function(evt) { + var list =[]; + var selected = JUBGrid.jubDetailGrid.selection.getSelected(); + for (var idx = 0; idx < selected.length; idx++) { + var rowIdx = selected[idx]; + var lid = JUBGrid.jubDetailGrid.model.getRow(rowIdx); + var deleteFromStore = function (evt) { + + if (evt) { + // something bad happened + alert("Error: "+evt.desc); + } else { + var deleteItem = function(item, rq) { + JUBGrid.jubDetailGrid.model.store.deleteItem(item); + }; + var updateCount = function(item) { + var newval = JUBGrid.jubGrid.model.store.getValue(item, "item_count"); + JUBGrid.jubGrid.model.store.setValue(item, "item_count", newval-1); + JUBGrid.jubGrid.update(); + }; + + JUBGrid.jubDetailGrid.model.store.fetch({query:{id:lid.id}, + onItem: deleteItem}); + JUBGrid.jubGrid.model.store.fetch({query:{id:JUBGrid.jubDetailGrid.lineitemID}, + onItem: updateCount}); + } + JUBGrid.jubDetailGrid.update(); + }; + + openils.acq.Lineitem.deleteLID(lid.id, deleteFromStore); + } + }, + + createLID: function(fields) { + fields['lineitem'] = JUBGrid.jubDetailGrid.lineitemID; + var addToStore = function (lid) { + JUBGrid.jubDetailGrid.model.store.newItem(acqlid.toStoreData([lid]).items[0]); + JUBGrid.jubDetailGrid.refresh(); + JUBGrid.jubGrid.update(); + JUBGrid.jubGrid.refresh(); + } + openils.acq.Lineitem.createLID(fields, addToStore); + }, + + receiveLID: function(evt) { + var list =[]; + var selected = JUBGrid.jubDetailGrid.selection.getSelected(); + for (var idx = 0; idx < selected.length; idx++) { + var rowIdx = selected[idx]; + var lid = JUBGrid.jubDetailGrid.model.getRow(rowIdx); + list.push(lid.id); + } + if(lid != null) { // is at least one selected? + JUBGrid._receiveLIDList(list, 0, + function() { + delete openils.acq.Lineitem.ModelCache[lid.lineitem]; + openils.acq.Lineitem.loadLIDGrid( + JUBGrid.jubDetailGrid, lid.lineitem, JUBGrid.jubDetailGridLayout); + } + ); + } + }, + + // loop through the list of LIDs and mark them as received + _receiveLIDList: function(list, idx, callback) { + if(idx >= list.length) + return callback(); + fieldmapper.standardRequest( + ['open-ils.acq', 'open-ils.acq.lineitem_detail.receive'], + { asnync: true, + params: [openils.User.authtoken, list[idx++]], + oncomplete: function(r) { + var res = r.recv().content(); + if(e = openils.Event.parse(res)) + return alert(e); + JUBGrid._receiveLIDList(list, idx, callback); + } + } + ); + }, + + + // called when a lineitem is edited + onJUBSet: function (griditem, attr, oldVal,newVal) { + var item; + + var updateDone = function(r) { + var stat = r.recv().content(); + if(e = openils.Event.parse(stat)) + console.dir(e); + }; + + // after an attribute has been updated + var attrUpdateDone = function(r, attr) { + var res = r.recv().content(); + if(e = openils.Event.parse(res)) + return alert(e); + + var oldVal = new openils.acq.Lineitem( + {lineitem:item}).findAttr(attr, 'lineitem_local_attr_definition'); + + if(oldVal) { + // if this attr already exists on the object, just update the value + for(var i = 0; i < item.attributes().length; i++) { + var attrobj = item.attributes()[i]; + if(attrobj.attr_type() == 'lineitem_local_attr_definition' && attrobj.attr_name() == attr) { + attrobj.attr_value(newVal); + } + } + } else { + // if this is a new attribute, create a new object to match reality + liad = new acqlia(); + liad.attr_type('lineitem_local_attr_definition'); + liad.attr_value(newVal); + liad.attr_name(attr); + liad.id(res); + item.attributes().push(liad); + } + } + + if (oldVal == newVal) { + return; + } + + item = JUBGrid.lineitems[griditem.id]; + if (attr == "provider") { + if(newVal == '') + newVal = null; + item.provider(newVal); + + } else if(attr == 'estimated_price' || attr == 'actual_price') { + fieldmapper.standardRequest( + ['open-ils.acq', 'open-ils.acq.lineitem_local_attr.set'], + { async: true, + params: [openils.User.authtoken, item.id(), attr, newVal], + oncomplete: function(r) {attrUpdateDone(r, attr); } + } + ); + } else { + //alert("Unexpected attr in Picklist.onSet: '"+attr+"'"); + return; + } + + fieldmapper.standardRequest( + ["open-ils.acq", "open-ils.acq.lineitem.update"], + {params: [openils.User.authtoken, item], + oncomplete: updateDone + } + ); + }, +}; + diff --git a/Open-ILS/web/js/ui/default/acq/financial/list_currency_types.js b/Open-ILS/web/js/ui/default/acq/financial/list_currency_types.js new file mode 100644 index 0000000000..f548788fce --- /dev/null +++ b/Open-ILS/web/js/ui/default/acq/financial/list_currency_types.js @@ -0,0 +1,25 @@ +dojo.require("dijit.Dialog"); +dojo.require('dijit.form.Button'); +dojo.require('dojox.grid.Grid'); +dojo.require('openils.acq.CurrencyType'); +dojo.require('openils.Event'); +dojo.require('openils.Util'); +dojo.require('fieldmapper.dojoData'); + +var currencyTypes = []; + +function loadCTypesGrid() { + openils.acq.CurrencyType.fetchAll( + function(types) { + var store = new dojo.data.ItemFileReadStore( + {data:acqct.toStoreData(types, 'code', {identifier:'code'})}); + var model = new dojox.grid.data.DojoData(null, store, + {rowsPerPage: 20, clientSort: true, query:{code:'*'}}); + currencyTypeListGrid.setModel(model); + currencyTypeListGrid.update(); + } + ); +} + + +openils.Util.addOnLoad(loadCTypesGrid); diff --git a/Open-ILS/web/js/ui/default/acq/financial/list_funding_sources.js b/Open-ILS/web/js/ui/default/acq/financial/list_funding_sources.js new file mode 100644 index 0000000000..d773f0b478 --- /dev/null +++ b/Open-ILS/web/js/ui/default/acq/financial/list_funding_sources.js @@ -0,0 +1,35 @@ +dojo.require("dijit.Dialog"); +dojo.require("dijit.form.FilteringSelect"); +dojo.require('openils.acq.FundingSource'); +dojo.require('openils.acq.CurrencyType'); +dojo.require('openils.widget.OrgUnitFilteringSelect'); +dojo.require('dijit.form.Button'); +dojo.require('dojox.grid.Grid'); +dojo.require('openils.Event'); +dojo.require('openils.Util'); + +function getOrgInfo(rowIndex) { + data = fundingSourceListGrid.model.getRow(rowIndex); + if(!data) return; + return fieldmapper.aou.findOrgUnit(data.owner).shortname(); +} + +function getBalanceInfo(rowIndex) { + data = fundingSourceListGrid.model.getRow(rowIndex); + if(!data) return; + return new String(openils.acq.FundingSource.cache[data.id].summary().balance); +} + +function loadFSGrid() { + openils.acq.FundingSource.createStore( + function(storeData) { + var store = new dojo.data.ItemFileReadStore({data:storeData}); + var model = new dojox.grid.data.DojoData(null, store, + {rowsPerPage: 20, clientSort: true, query:{id:'*'}}); + fundingSourceListGrid.setModel(model); + fundingSourceListGrid.update(); + } + ); +} + +openils.Util.addOnLoad(loadFSGrid); diff --git a/Open-ILS/web/js/ui/default/acq/financial/list_funds.js b/Open-ILS/web/js/ui/default/acq/financial/list_funds.js new file mode 100644 index 0000000000..43acbb5657 --- /dev/null +++ b/Open-ILS/web/js/ui/default/acq/financial/list_funds.js @@ -0,0 +1,63 @@ +dojo.require("dijit.Dialog"); +dojo.require("dijit.form.FilteringSelect"); +dojo.require('dijit.form.Button'); +dojo.require('dojox.grid.Grid'); + +dojo.require('openils.widget.OrgUnitFilteringSelect'); +dojo.require('openils.acq.CurrencyType'); +dojo.require('openils.Event'); +dojo.require('openils.Util'); +dojo.require('openils.acq.Fund'); + +function getOrgInfo(rowIndex) { + data = fundListGrid.model.getRow(rowIndex); + if(!data) return; + return fieldmapper.aou.findOrgUnit(data.org).shortname(); +} + +function getBalanceInfo(rowIndex) { + data = fundListGrid.model.getRow(rowIndex); + if(!data) return; + return new String(openils.acq.Fund.cache[data.id].summary().combined_balance); +} + + +function loadFundGrid() { + openils.acq.Fund.createStore( + function(storeData) { + var store = new dojo.data.ItemFileReadStore({data:storeData}); + var model = new dojox.grid.data.DojoData(null, store, + {rowsPerPage: 20, clientSort: true, query:{id:'*'}}); + fundListGrid.setModel(model); + fundListGrid.update(); + + var yearStore = {identifier:'year', name:'year', items:[]}; + + var added = []; + for(var i = 0; i < storeData.items.length; i++) { + var year = storeData.items[i].year; + if(added.indexOf(year) == -1) { + yearStore.items.push({year:year}); + added.push(year); + } + } + yearStore.items = yearStore.items.sort().reverse(); + fundFilterYearSelect.store = new dojo.data.ItemFileReadStore({data:yearStore}); + var today = new Date().getFullYear().toString(); + fundFilterYearSelect.setValue((added.indexOf(today != -1)) ? today : added[0]); + } + ); +} + +function filterGrid() { + var year = fundFilterYearSelect.getValue(); + if(year) + fundListGrid.model.query = {year:year}; + else + fundListGrid.model.query = {id:'*'}; + fundListGrid.model.refresh(); + fundListGrid.update(); +} + +openils.Util.addOnLoad(loadFundGrid); + diff --git a/Open-ILS/web/js/ui/default/acq/financial/list_providers.js b/Open-ILS/web/js/ui/default/acq/financial/list_providers.js new file mode 100644 index 0000000000..cf41b397c8 --- /dev/null +++ b/Open-ILS/web/js/ui/default/acq/financial/list_providers.js @@ -0,0 +1,35 @@ +dojo.require("dijit.Dialog"); +dojo.require("dijit.form.FilteringSelect"); +dojo.require('dijit.form.Button'); +dojo.require('dojox.grid.Grid'); + +dojo.require('openils.acq.CurrencyType'); +dojo.require('openils.Event'); +dojo.require('openils.Util'); +dojo.require('openils.acq.Provider'); +dojo.require("fieldmapper.OrgUtils"); +dojo.require('openils.widget.OrgUnitFilteringSelect'); + +function getOrgInfo(rowIndex) { + data = providerListGrid.model.getRow(rowIndex); + if(!data) return; + return fieldmapper.aou.findOrgUnit(data.owner).shortname(); +} + +function loadProviderGrid() { + openils.acq.Provider.createStore( + function(storeData) { + var store = new dojo.data.ItemFileReadStore({data:storeData}); + var model = new dojox.grid.data.DojoData(null, store, + {rowsPerPage: 20, clientSort: true, query:{id:'*'}}); + providerListGrid.setModel(model); + providerListGrid.update(); + } + ); +} +function createProvider(fields) { + openils.acq.Provider.create(fields, function(){loadProviderGrid()}); +} + + +openils.Util.addOnLoad(loadProviderGrid); diff --git a/Open-ILS/web/js/ui/default/acq/financial/view_fund.js b/Open-ILS/web/js/ui/default/acq/financial/view_fund.js new file mode 100644 index 0000000000..1f73b44154 --- /dev/null +++ b/Open-ILS/web/js/ui/default/acq/financial/view_fund.js @@ -0,0 +1,86 @@ +dojo.require("dijit.Dialog"); +dojo.require('dijit.form.FilteringSelect'); +dojo.require('dijit.layout.TabContainer'); +dojo.require('dijit.layout.ContentPane'); +dojo.require('dojox.grid.Grid'); + +dojo.require("fieldmapper.OrgUtils"); +dojo.require('openils.acq.Fund'); +dojo.require('openils.acq.FundingSource'); +dojo.require('openils.Event'); +dojo.require('openils.User'); +dojo.require('openils.Util'); + +var fund = null; + +function getSummaryInfo(rowIndex) { + return new String(fund.summary()[this.field]); +} + +function createAllocation(fields) { + fields.fund = fundID; + if(isNaN(fields.percent)) fields.percent = null; + if(isNaN(fields.amount)) fields.amount = null; + //openils.acq.Fund.createAllocation(fields, resetPage); + openils.acq.Fund.createAllocation(fields, + function(r){location.href = location.href;}); +} + +function getOrgInfo(rowIndex) { + data = fundGrid.model.getRow(rowIndex); + if(!data) return; + return fieldmapper.aou.findOrgUnit(data.org).shortname(); +} + +function getXferDest(rowIndex) { + data = this.grid.model.getRow(rowIndex); + if(!(data && data.xfer_destination)) return ''; + return data.xfer_destination; +} + +function loadFundGrid() { + var store = new dojo.data.ItemFileReadStore({data:acqf.toStoreData([fund])}); + var model = new dojox.grid.data.DojoData( + null, store, {rowsPerPage: 20, clientSort: true, query:{id:'*'}}); + fundGrid.setModel(model); + fundGrid.update(); +} + +function loadAllocationGrid() { + if(fundAllocationGrid.isLoaded) return; + var store = new dojo.data.ItemFileReadStore({data:acqfa.toStoreData(fund.allocations())}); + var model = new dojox.grid.data.DojoData( + null, store, {rowsPerPage: 20, clientSort: true, query:{id:'*'}}); + fundAllocationGrid.setModel(model); + fundAllocationGrid.update(); + fundAllocationGrid.isLoaded = true; +} + +function loadDebitGrid() { + if(fundDebitGrid.isLoaded) return; + var store = new dojo.data.ItemFileReadStore({data:acqfa.toStoreData(fund.debits())}); + var model = new dojox.grid.data.DojoData( + null, store, {rowsPerPage: 20, clientSort: true, query:{id:'*'}}); + fundDebitGrid.setModel(model); + fundDebitGrid.update(); + fundDebitGrid.isLoaded = true; +} + +function fetchFund() { + fieldmapper.standardRequest( + ['open-ils.acq', 'open-ils.acq.fund.retrieve'], + { async: true, + params: [ + openils.User.authtoken, fundID, + {flesh_summary:1, flesh_allocations:1, flesh_debits:1} + /* TODO grab allocations and debits only on as-needed basis */ + ], + oncomplete: function(r) { + fund = r.recv().content(); + loadFundGrid(fund); + } + } + ); +} + +openils.Util.addOnLoad(fetchFund); diff --git a/Open-ILS/web/js/ui/default/acq/financial/view_funding_source.js b/Open-ILS/web/js/ui/default/acq/financial/view_funding_source.js new file mode 100644 index 0000000000..8f6a31e43e --- /dev/null +++ b/Open-ILS/web/js/ui/default/acq/financial/view_funding_source.js @@ -0,0 +1,100 @@ +dojo.require("dijit.Dialog"); +dojo.require('dijit.layout.TabContainer'); +dojo.require('dijit.layout.ContentPane'); +dojo.require("dijit.form.FilteringSelect"); +dojo.require("dijit.form.Textarea"); +dojo.require("dijit.form.CurrencyTextBox"); +dojo.require('dojox.grid.Grid'); + +dojo.require("fieldmapper.OrgUtils"); +dojo.require('openils.acq.FundingSource'); +dojo.require('openils.acq.Fund'); +dojo.require('openils.Event'); +dojo.require('openils.Util'); + +var ses = new OpenSRF.ClientSession('open-ils.acq'); +var fundingSource = null; + +function resetPage() { + fundingSource = null; + fsCreditGrid.isLoaded = false; + fsAllocationGrid.isLoaded = false; + loadFS(); +} + +/** creates a new funding_source_credit from the dialog ----- */ +function applyFSCredit(fields) { + fields.funding_source = fundingSourceID; + openils.acq.FundingSource.createCredit(fields, resetPage); +} + +function applyFSAllocation(fields) { + fields.funding_source = fundingSourceID; + if(isNaN(fields.percent)) fields.percent = null; + if(isNaN(fields.amount)) fields.amount = null; + openils.acq.Fund.createAllocation(fields, resetPage); +} + +/** fetch the fleshed funding source ----- */ +function loadFS() { + var req = ses.request( + 'open-ils.acq.funding_source.retrieve', + openils.User.authtoken, fundingSourceID, + {flesh_summary:1, flesh_credits:1,flesh_allocations:1} + ); + + req.oncomplete = function(r) { + var msg = req.recv(); + fundingSource = msg.content(); + var evt = openils.Event.parse(fundingSource); + if(evt) { + alert(evt); + return; + } + loadFSGrid(); + } + req.send(); +} + +/** Some grid rendering accessor functions ----- */ +function getOrgInfo(rowIndex) { + data = fundingSourceGrid.model.getRow(rowIndex); + if(!data) return; + return fieldmapper.aou.findOrgUnit(data.owner).shortname(); +} + +function getSummaryInfo(rowIndex) { + return new String(fundingSource.summary()[this.field]); +} + +/** builds the credits grid ----- */ +function loadFSGrid() { + if(!fundingSource) return; + var store = new dojo.data.ItemFileReadStore({data:acqfs.toStoreData([fundingSource])}); + var model = new dojox.grid.data.DojoData(null, store, {rowsPerPage: 20, clientSort: true, query:{id:'*'}}); + fundingSourceGrid.setModel(model); + fundingSourceGrid.update(); +} + + +/** builds the credits grid ----- */ +function loadCreditGrid() { + if(fsCreditGrid.isLoaded) return; + var store = new dojo.data.ItemFileReadStore({data:acqfa.toStoreData(fundingSource.credits())}); + var model = new dojox.grid.data.DojoData(null, store, {rowsPerPage: 20, clientSort: true, query:{id:'*'}}); + fsCreditGrid.setModel(model); + fsCreditGrid.update(); + fsCreditGrid.isLoaded = true; +} + +/** builds the allocations grid ----- */ +function loadAllocationGrid() { + if(fsAllocationGrid.isLoaded) return; + var store = new dojo.data.ItemFileReadStore({data:acqfa.toStoreData(fundingSource.allocations())}); + var model = new dojox.grid.data.DojoData(null, store, {rowsPerPage: 20, clientSort: true, query:{id:'*'}}); + fsAllocationGrid.setModel(model); + fsAllocationGrid.update(); + fsAllocationGrid.isLoaded = true; +} + +openils.Util.addOnLoad(loadFS); diff --git a/Open-ILS/web/js/ui/default/acq/financial/view_provider.js b/Open-ILS/web/js/ui/default/acq/financial/view_provider.js new file mode 100644 index 0000000000..181aff8333 --- /dev/null +++ b/Open-ILS/web/js/ui/default/acq/financial/view_provider.js @@ -0,0 +1,99 @@ +dojo.require("dijit.Dialog"); +dojo.require('dijit.layout.TabContainer'); +dojo.require('dijit.layout.ContentPane'); +dojo.require('dijit.form.FilteringSelect'); +dojo.require('dojox.grid.Grid'); +dojo.require("fieldmapper.OrgUtils"); +dojo.require('openils.acq.Provider'); +dojo.require('openils.Event'); +dojo.require('openils.User'); +dojo.require('openils.Util'); + +var provider = null; +var marcRegex = /^\/\/\*\[@tag="(\d+)"]\/\*\[@code="(\w)"]$/; + +function getOrgInfo(rowIndex) { + data = providerGrid.model.getRow(rowIndex); + if(!data) return; + return fieldmapper.aou.findOrgUnit(data.owner).shortname(); +} + +function getTag(rowIdx) { + data = padGrid.model.getRow(rowIdx); + if(!data) return; + return data.xpath.replace(marcRegex, '$1'); +} + +function getSubfield(rowIdx) { + data = padGrid.model.getRow(rowIdx); + if(!data) return; + return data.xpath.replace(marcRegex, '$2'); +} + + +function loadProviderGrid() { + var store = new dojo.data.ItemFileReadStore({data:acqpro.toStoreData([provider])}); + var model = new dojox.grid.data.DojoData( + null, store, {rowsPerPage: 20, clientSort: true, query:{id:'*'}}); + providerGrid.setModel(model); + providerGrid.update(); +} + +function loadPADGrid() { + openils.acq.Provider.retrieveLineitemProviderAttrDefs(providerId, + function(attrs) { + var store = new dojo.data.ItemFileReadStore({data:acqlipad.toStoreData(attrs)}); + var model = new dojox.grid.data.DojoData( + null, store, {rowsPerPage: 20, clientSort: true, query:{id:'*'}}); + padGrid.setModel(model); + padGrid.update(); + } + ); +} + + +function fetchProvider() { + fieldmapper.standardRequest( + ['open-ils.acq', 'open-ils.acq.provider.retrieve'], + { async: true, + params: [ openils.User.authtoken, providerId ], + oncomplete: function(r) { + provider = r.recv().content(); + loadProviderGrid(provider); + } + } + ); +} + +function createOrderRecordField(fields) { + fields.provider = providerId; + if(!fields.xpath) + fields.xpath = '//*[@tag="'+fields.tag+'"]/*[@code="'+fields.subfield+'"]'; + delete fields.tag; + delete fields.subfield; + openils.acq.Provider.createLineitemProviderAttrDef(fields, + function(id) { + loadPADGrid(); + } + ); +} + +function setORDesc() { + var code = dijit.byId('oils-acq-provider-or-code'); + var desc = dijit.byId('oils-acq-provider-or-desc'); + desc.setValue(code.getDisplayedValue()); +} + +function deleteORDataFields() { + var list = [] + var selected = padGrid.selection.getSelected(); + for(var idx = 0; idx < selected.length; idx++) + list.push(padGrid.model.getRow(selected[idx]).id); + openils.acq.Provider.lineitemProviderAttrDefDeleteList( + list, function(){loadPADGrid();}); +} + + +openils.Util.addOnLoad(fetchProvider); + + diff --git a/Open-ILS/web/js/ui/default/acq/picklist/bib_search.js b/Open-ILS/web/js/ui/default/acq/picklist/bib_search.js new file mode 100644 index 0000000000..51e6bcc40e --- /dev/null +++ b/Open-ILS/web/js/ui/default/acq/picklist/bib_search.js @@ -0,0 +1,205 @@ +dojo.require('dojox.form.CheckedMultiSelect'); +dojo.require('fieldmapper.Fieldmapper'); +dojo.require('dijit.ProgressBar'); +dojo.require('dijit.form.Form'); +dojo.require('dijit.form.TextBox'); +dojo.require('dijit.form.NumberSpinner'); +dojo.require('openils.Event'); +dojo.require('openils.acq.Picklist'); +dojo.require('openils.acq.Lineitem'); +dojo.require('openils.User'); +dojo.require('openils.Util'); + +var searchFields = []; +var resultPicklist; +var resultLIs; +var selectedLIs; +var recvCount = 0; +var sourceCount = 0; // how many sources are we searching +var user = new openils.User(); +var searchLimit = 10; + +function drawForm() { + + var sources = fieldmapper.standardRequest( + ['open-ils.search', 'open-ils.search.z3950.retrieve_services'], + [user.authtoken] + ); + + openils.Event.parse_and_raise(sources); + + for(var name in sources) { + source = sources[name]; + bibSourceSelect.addOption(name, name+':'+source.host); + for(var attr in source.attrs) + if(!attr.match(/^#/)) // xml comment nodes + searchFields.push(source.attrs[attr]); + } + + searchFields = searchFields.sort( + function(a,b) { + if(a.label < b.label) + return -1; + if(a.label > b.label) + return 1; + return 0; + } + ); + + var tbody = dojo.byId('oils-acq-search-fields-tbody'); + var tmpl = tbody.removeChild(dojo.byId('oils-acq-search-fields-template')); + + for(var f in searchFields) { + var field = searchFields[f]; + if(dijit.byId('text_input_'+field.name)) continue; + var row = tmpl.cloneNode(true); + tbody.insertBefore(row, dojo.byId('oils-acq-seach-fields-count-row')); + var labelCell = dojo.query('[name=label]', row)[0]; + var inputCell = dojo.query('[name=input]', row)[0]; + labelCell.appendChild(document.createTextNode(field.label)); + input = new dijit.form.TextBox({name:field.name, label:field.label, id:'text_input_'+field.name}); + inputCell.appendChild(input.domNode); + } +} + +function doSearch(values) { + dojo.style('searchProgress', 'visibility', 'visible'); + searchProgress.update({progress: 0}); + + search = { + service : [], + username : [], + password : [], + search : {}, + limit : values.limit, + offset : searchOffset + }; + searchLimit = values.limit; + delete values.limit; + + var selected = bibSourceSelect.getValue(); + for(var i = 0; i < selected.length; i++) { + search.service.push(selected[i]); + search.username.push(''); + search.password.push(''); + sourceCount++; + } + + for(var v in values) { + if(values[v]) { + var input = dijit.byId('text_input_'+v); + search.search[v] = values[v]; + } + } + + fieldmapper.standardRequest( + ['open-ils.acq', 'open-ils.acq.picklist.search.z3950'], + { async: true, + params: [user.authtoken, search], + onresponse: handleResult, + } + ); +} + +function handleResult(r) { + var result = r.recv().content(); + if(openils.Event.parse(result)) { + alert(openils.Event.parse(result)); + dojo.style('searchProgress', 'visibility', 'hidden'); + return; + } + if(result.complete) + return viewResults(result.picklist_id); + searchProgress.update({maximum: result.total, progress: result.progress}); +} + +function viewResults(plId) { + var plist = new openils.acq.Picklist(plId, + function(model) { + resultLIs = plist._items; + dojo.style('oils-acq-pl-search-results', 'visibility', 'visible'); + JUBGrid.populate(plResultGrid, model, plist._items); + }, + {flesh_attrs:1, clear_marc:1, limit: searchLimit} + ); + resultPicklist = plist._plist; +} + +function loadPLSelect() { + var plList = []; + function handleResponse(r) { + plList.push(r.recv().content()); + } + var method = 'open-ils.acq.picklist.user.retrieve'; + fieldmapper.standardRequest( + ['open-ils.acq', method], + { async: true, + params: [openils.User.authtoken], + onresponse: handleResponse, + oncomplete: function() { + plAddExistingSelect.store = + new dojo.data.ItemFileReadStore({data:acqpl.toStoreData(plList)}); + plAddExistingSelect.setValue(); + } + } + ); +} + + +function saveResults(values) { + selectedLIs = resultLIs; + + if(values.which == 'selected') { + selectedLIs = []; + var selected = plResultGrid.selection.getSelected(); + for(var idx = 0; idx < selected.length; idx++) { + var rowIdx = selected[idx]; + var id = plResultGrid.model.getRow(rowIdx).id; + for(var i = 0; i < resultLIs.length; i++) { + var pl = resultLIs[i]; + if(pl.id() == id) { + selectedLIs.push(pl); + } + } + } + } + + if(values.new_name && values.new_name != '') { + // save selected lineitems to a new picklist + if(values.which = 'selected') { + openils.acq.Picklist.create( + {name: values.new_name}, + function(id) { + updateLiList(id, selectedLIs, 0, + function(){location.href = 'view/' + id}); + } + ); + } else { + // save all == change the name of the results picklist + resultPicklist.name(values.new_name); + openils.acq.Picklist.update(resultPicklist, + function(stat) { + location.href = 'view/' + resultPicklist.id(); + } + ); + } + } else if(values.existing_pl) { + // update lineitems to use an existing picklist + updateLiList(values.existing_pl, selectedLIs, 0, + function(){location.href = 'view/' + values.existing_pl}); + } +} + +function updateLiList(pl, list, idx, oncomplete) { + if(idx >= list.length) + return oncomplete(); + var li = selectedLIs[idx]; + li.picklist(pl); + new openils.acq.Lineitem({lineitem:li}).update( + function(r) { + updateLiList(pl, list, ++idx, oncomplete); + } + ); +} + +openils.Util.addOnLoad(drawForm); diff --git a/Open-ILS/web/js/ui/default/acq/picklist/view_list.js b/Open-ILS/web/js/ui/default/acq/picklist/view_list.js new file mode 100644 index 0000000000..ca257410e8 --- /dev/null +++ b/Open-ILS/web/js/ui/default/acq/picklist/view_list.js @@ -0,0 +1,81 @@ +dojo.require('dojox.grid.Grid'); +dojo.require('dijit.Dialog'); +dojo.require('dijit.form.Button'); +dojo.require('dijit.form.TextBox'); +dojo.require('dijit.form.Button'); +dojo.require('openils.acq.Picklist'); +dojo.require('openils.Util'); + +var plList = []; +var listAll = false; + +function makeGridFromList() { + var store = new dojo.data.ItemFileReadStore({data:acqpl.toStoreData(plList)}); + var model = new dojox.grid.data.DojoData(null, store, + {rowsPerPage: 20, clientSort: true, query:{id:'*'}}); + plListGrid.setModel(model); + plListGrid.update(); +} + + +function loadGrid() { + var method = 'open-ils.acq.picklist.user.retrieve.atomic'; + if(listAll) + method = method.replace(/user/, 'user.all'); + + fieldmapper.standardRequest( + ['open-ils.acq', method], + { async: true, + params: [openils.User.authtoken, + {flesh_lineitem_count:1, flesh_username:1}], + oncomplete: function(r) { + var resp = r.recv().content(); + if(e = openils.Event.parse(resp)) + return alert(e); + plList = resp; + makeGridFromList(); + } + } + ); +} + +function createPL(fields) { + if(fields.name == '') return; + openils.acq.Picklist.create(fields, + function(plId) { + fieldmapper.standardRequest( + ['open-ils.acq', 'open-ils.acq.picklist.retrieve'], + { async: true, + params: [openils.User.authtoken, plId, + {flesh_lineitem_count:1, flesh_username:1}], + oncomplete: function(r) { + var pl = r.recv().content(); + plList.push(pl); + makeGridFromList(); + } + } + ); + } + ); +} + +function deleteFromGrid() { + var list = [] + var selected = plListGrid.selection.getSelected(); + for(var idx = 0; idx < selected.length; idx++) { + var rowIdx = selected[idx]; + var id = plListGrid.model.getRow(rowIdx).id; + for(var i = 0; i < plList.length; i++) { + var pl = plList[i]; + if(pl.id() == id && pl.owner() == new openils.User().user.usrname()) { + list.push(id); + plList = (plList.slice(0, i) || []).concat(plList.slice(i+1, plList.length) || []); + } + } + } + openils.acq.Picklist.deleteList(list, function() { makeGridFromList(); }); +} + +openils.Util.addOnLoad(loadGrid); + + diff --git a/Open-ILS/web/js/ui/default/acq/po/li_search.js b/Open-ILS/web/js/ui/default/acq/po/li_search.js new file mode 100644 index 0000000000..d099ed0f2b --- /dev/null +++ b/Open-ILS/web/js/ui/default/acq/po/li_search.js @@ -0,0 +1,189 @@ +dojo.require('fieldmapper.Fieldmapper'); +dojo.require('dijit.ProgressBar'); +dojo.require('dijit.form.Form'); +dojo.require('dijit.form.TextBox'); +dojo.require('dijit.form.CheckBox'); +dojo.require('dijit.form.FilteringSelect'); +dojo.require('dijit.form.Button'); +dojo.require("dijit.Dialog"); +dojo.require('openils.Event'); +dojo.require('openils.Util'); +dojo.require('openils.acq.Lineitem'); +dojo.require('openils.acq.Provider'); +dojo.require('openils.acq.PO'); +dojo.require('openils.widget.OrgUnitFilteringSelect'); + +var recvCount = 0; +var createAssetsSelected = false; +var createDebitsSelected = false; + +var lineitems = []; + +function drawForm() { + buildProviderSelect(providerSelector); +} + +function buildProviderSelect(sel, oncomplete) { + openils.acq.Provider.createStore( + function(store) { + sel.store = new dojo.data.ItemFileReadStore({data:store}); + if(oncomplete) + oncomplete(); + }, + 'MANAGE_PROVIDER' + ); +} + +var liReceived; +function doSearch(values) { + var search = {}; + for(var v in values) { + var val = values[v]; + if(val != null && val != '') + search[v] = val; + } + + if(values.state == 'approved') + dojo.style('oils-acq-li-search-po-create', 'visibility', 'visible'); + else + dojo.style('oils-acq-li-search-po-create', 'visibility', 'hidden'); + + //search = [search, {limit:searchLimit, offset:searchOffset}]; + search = [search, {}]; + options = {clear_marc:1, flesh_attrs:1}; + + liReceived = 0; + lineitems = []; + dojo.style('searchProgress', 'visibility', 'visible'); + fieldmapper.standardRequest( + ['open-ils.acq', 'open-ils.acq.lineitem.search'], + { async: true, + params: [openils.User.authtoken, search, options], + onresponse: handleResult, + oncomplete: viewList + } + ); +} + +function handleResult(r) { + var result = r.recv().content(); + searchProgress.update({maximum: searchLimit, progress: ++liReceived}); + lineitems.push(result); +} + +function viewList() { + dojo.style('searchProgress', 'visibility', 'hidden'); + dojo.style('oils-acq-li-search-result-grid', 'visibility', 'visible'); + var store = new dojo.data.ItemFileWriteStore( + {data:jub.toStoreData(lineitems, null, + {virtualFields:['estimated_price', 'actual_price']})}); + var model = new dojox.grid.data.DojoData( + null, store, {rowsPerPage: 20, clientSort: true, query:{id:'*'}}); + JUBGrid.populate(liGrid, model, lineitems); +} + +function createPOFromLineitems(fields) { + var po = new acqpo(); + po.provider(newPOProviderSelector.getValue()); + createAssetsSelected = fields.create_assets; + createDebitsSelected = fields.create_debits; + + if(fields.which == 'selected') { + // find the selected lineitems + var selected = liGrid.selection.getSelected(); + var selList = []; + for(var idx = 0; idx < selected.length; idx++) { + var rowIdx = selected[idx]; + var id = liGrid.model.getRow(rowIdx).id; + for(var i = 0; i < lineitems.length; i++) { + var li = lineitems[i]; + if(li.id() == id && !li.purchase_order() && li.state() == 'approved') + selList.push(lineitems[i]); + } + } + } else { + selList = lineitems; + } + + if(selList.length == 0) return; + + openils.acq.PO.create(po, + function(poId) { + if(e = openils.Event.parse(poId)) + return alert(e); + updateLiList(poId, selList); + } + ); +} + +function updateLiList(poId, selList) { + _updateLiList(poId, selList, 0); +} + +function checkCreateDebits(poId) { + if(!createDebitsSelected) + return viewPO(poId); + fieldmapper.standardRequest( + ['open-ils.acq', 'open-ils.acq.purchase_order.debits.create'], + { async: true, + params: [openils.User.authtoken, poId, {encumbrance:1}], + oncomplete : function(r) { + var total = r.recv().content(); + if(e = openils.Event.parse(total)) + return alert(e); + viewPO(poId); + } + } + ); +} + +function viewPO(poId) { + location.href = 'view/' + poId; +} + +function _updateLiList(poId, selList, idx) { + if(idx >= selList.length) { + if(createAssetsSelected) + return createAssets(poId); + else + return checkCreateDebits(poId); + } + var li = selList[idx]; + li.purchase_order(poId); + li.state('in-process'); + new openils.acq.Lineitem({lineitem:li}).update( + function(stat) { + _updateLiList(poId, selList, ++idx); + } + ); +} + +function createAssets(poId) { + searchProgress.update({progress: 0}); + dojo.style('searchProgress', 'visibility', 'visible'); + + function onresponse(r) { + var stat = r.recv().content(); + if(e = openils.Event.parse(stat)) + return alert(e); + searchProgress.update({maximum: stat.total, progress: stat.progress}); + } + + function oncomplete(r) { + dojo.style('searchProgress', 'visibility', 'hidden'); + checkCreateDebits(poId); + } + + fieldmapper.standardRequest( + ['open-ils.acq','open-ils.acq.purchase_order.assets.create'], + { async: true, + params: [openils.User.authtoken, poId], + onresponse : onresponse, + oncomplete : oncomplete + } + ); +} + + +openils.Util.addOnLoad(drawForm); + diff --git a/Open-ILS/web/js/ui/default/acq/po/search.js b/Open-ILS/web/js/ui/default/acq/po/search.js new file mode 100644 index 0000000000..acec485177 --- /dev/null +++ b/Open-ILS/web/js/ui/default/acq/po/search.js @@ -0,0 +1,81 @@ +dojo.require('dijit.form.Form'); +dojo.require('dijit.form.Button'); +dojo.require('dijit.form.FilteringSelect'); +dojo.require('dijit.form.NumberTextBox'); +dojo.require('dojox.grid.Grid'); +dojo.require('openils.acq.Provider'); +dojo.require('fieldmapper.OrgUtils'); +dojo.require('dojo.date.locale'); +dojo.require('dojo.date.stamp'); +dojo.require('openils.User'); +dojo.require('openils.Util'); +dojo.require('openils.widget.OrgUnitFilteringSelect'); + +function getOrgInfo(rowIndex) { + data = poGrid.model.getRow(rowIndex); + if(!data) return; + return fieldmapper.aou.findOrgUnit(data.owner).shortname(); +} + +function getProvider(rowIndex) { + data = poGrid.model.getRow(rowIndex); + if(!data) return; + return openils.acq.Provider.retrieve(data.provider).name(); +} + +function getPOOwner(rowIndex) { + data = poGrid.model.getRow(rowIndex); + if(!data) return; + return new openils.User({id:data.owner}).user.usrname(); +} + +function getDateTimeField(rowIndex) { + data = poGrid.model.getRow(rowIndex); + if(!data) return; + var date = dojo.date.stamp.fromISOString(data[this.field]); + return dojo.date.locale.format(date, {formatLength:'medium'}); +} + +function doSearch(fields) { + var itemList = []; + if(!isNaN(fields.id)) + fields = {id:fields.id}; + else + delete fields.id; + + fieldmapper.standardRequest( + ['open-ils.acq', 'open-ils.acq.purchase_order.search'], + { + async:1, + params: [openils.User.authtoken, fields], + onresponse : function(r) { + var msg = r.recv(); + if(msg) itemList.push(msg.content()); + }, + oncomplete : function(r) { + dojo.style('po-grid', 'visibility', 'visible'); + var store = new dojo.data.ItemFileReadStore({data:acqpo.toStoreData(itemList)}); + var model = new dojox.grid.data.DojoData(null, store, + {rowsPerPage: 20, clientSort: true, query:{id:'*'}}); + poGrid.setModel(model); + poGrid.update(); + }, + } + ); +} + +function loadForm() { + + /* load the providers */ + openils.acq.Provider.createStore( + function(store) { + providerSelector.store = + new dojo.data.ItemFileReadStore({data:store}); + }, + 'MANAGE_PROVIDER' + ); + + new openils.User().buildPermOrgSelector('VIEW_PURCHASE_ORDER', poSearchOrderingAgencySelect); +} + +openils.Util.addOnLoad(loadForm); diff --git a/Open-ILS/web/js/ui/default/acq/po/view_po.js b/Open-ILS/web/js/ui/default/acq/po/view_po.js new file mode 100644 index 0000000000..542a13202b --- /dev/null +++ b/Open-ILS/web/js/ui/default/acq/po/view_po.js @@ -0,0 +1,82 @@ +dojo.require("dijit.Dialog"); +dojo.require('dijit.form.FilteringSelect'); +dojo.require('dijit.layout.TabContainer'); +dojo.require('dijit.layout.ContentPane'); +dojo.require('dojox.grid.Grid'); +dojo.require('openils.acq.PO'); +dojo.require('openils.Event'); +dojo.require('openils.User'); +dojo.require('openils.Util'); +dojo.require('fieldmapper.OrgUtils'); +dojo.require('openils.acq.Provider'); +dojo.require('openils.acq.Lineitem'); +dojo.require('dojo.date.locale'); +dojo.require('dojo.date.stamp'); + +var PO = null; +var lineitems = []; + +function getOrgInfo(rowIndex) { + data = poGrid.model.getRow(rowIndex); + if(!data) return; + return fieldmapper.aou.findOrgUnit(data.owner).shortname(); +} + +function getProvider(rowIndex) { + data = poGrid.model.getRow(rowIndex); + if(!data) return; + return openils.acq.Provider.retrieve(data.provider).code(); +} + +function getPOOwner(rowIndex) { + data = poGrid.model.getRow(rowIndex); + if(!data) return; + return new openils.User({id:data.owner}).user.usrname(); +} + +function getDateTimeField(rowIndex) { + data = poGrid.model.getRow(rowIndex); + if(!data) return; + var date = dojo.date.stamp.fromISOString(data[this.field]); + return dojo.date.locale.format(date, {formatLength:'medium'}); +} + +function loadPOGrid() { + if(!PO) return; + var store = new dojo.data.ItemFileReadStore({data:acqpo.toStoreData([PO])}); + var model = new dojox.grid.data.DojoData( + null, store, {rowsPerPage: 20, clientSort: true, query:{id:'*'}}); + poGrid.setModel(model); + poGrid.update(); +} + +function loadLIGrid() { + if(liGrid.isLoaded) return; + + function load(po) { + lineitems = po.lineitems(); + var store = new dojo.data.ItemFileReadStore({data:jub.toStoreData(lineitems)}); + var model = new dojox.grid.data.DojoData( + null, store, {rowsPerPage: 20, clientSort: true, query:{id:'*'}}); + JUBGrid.populate(liGrid, model, lineitems) + } + + openils.acq.PO.retrieve(poId, load, {flesh_lineitems:1, clear_marc:1}); + liGrid.isLoaded = true; +} + +function loadPage() { + fetchPO(); +} + +function fetchPO() { + openils.acq.PO.retrieve(poId, + function(po) { + PO = po; + loadPOGrid(); + }, + {flesh_lineitem_count:1} + ); +} + +openils.Util.addOnLoad(loadPage); diff --git a/Open-ILS/web/js/ui/default/acq/receiving/process.js b/Open-ILS/web/js/ui/default/acq/receiving/process.js new file mode 100644 index 0000000000..8c7a8b8a43 --- /dev/null +++ b/Open-ILS/web/js/ui/default/acq/receiving/process.js @@ -0,0 +1,62 @@ +dojo.require('fieldmapper.Fieldmapper'); +dojo.require('dijit.ProgressBar'); +dojo.require('dijit.form.Form'); +dojo.require('dijit.form.TextBox'); +dojo.require('dijit.form.CheckBox'); +dojo.require('dijit.form.FilteringSelect'); +dojo.require('dijit.form.Button'); +dojo.require("dijit.Dialog"); +dojo.require('openils.Event'); +dojo.require('openils.Util'); +dojo.require('openils.acq.Lineitem'); +dojo.require('openils.widget.OrgUnitFilteringSelect'); + +var lineitems = []; + +function drawForm() { + new openils.User().buildPermOrgSelector('VIEW_PURCHASE_ORDER', orderingAgencySelect); +} + +var liReceived; +function doSearch(values) { + + var search = { + attr_values : [values.identifier], + po_agencies : (values.ordering_agency) ? [values.ordering_agency] : null, + li_states : ['in-process'] + }; + + options = {clear_marc:1, flesh_attrs:1}; + liReceived = 0; + dojo.style('searchProgress', 'visibility', 'visible'); + + fieldmapper.standardRequest( + ['open-ils.acq', 'open-ils.acq.lineitem.search.ident'], + { async: true, + params: [openils.User.authtoken, search, options], + onresponse: handleResult, + oncomplete: viewList + } + ); +} + +var searchLimit = 10; // ? +function handleResult(r) { + var result = r.recv().content(); + searchProgress.update({maximum: searchLimit, progress: ++liReceived}); + lineitems.push(result); +} + +function viewList() { + dojo.style('searchProgress', 'visibility', 'hidden'); + dojo.style('oils-acq-recv-grid', 'visibility', 'visible'); + var store = new dojo.data.ItemFileWriteStore( + {data:jub.toStoreData(lineitems, null, + {virtualFields:['estimated_price', 'actual_price']})}); + var model = new dojox.grid.data.DojoData( + null, store, {rowsPerPage: 20, clientSort: true, query:{id:'*'}}); + JUBGrid.populate(liGrid, model, lineitems); +} + +openils.Util.addOnLoad(drawForm); + diff --git a/Open-ILS/web/js/ui/default/acq/settings/li_attr.js b/Open-ILS/web/js/ui/default/acq/settings/li_attr.js new file mode 100644 index 0000000000..28b8f0bdd1 --- /dev/null +++ b/Open-ILS/web/js/ui/default/acq/settings/li_attr.js @@ -0,0 +1,105 @@ +dojo.require("dijit.Dialog"); +dojo.require('dijit.layout.TabContainer'); +dojo.require('dijit.layout.ContentPane'); +dojo.require('dijit.form.FilteringSelect'); +dojo.require('dijit.form.TextBox'); +dojo.require('dojox.grid.Grid'); +dojo.require("fieldmapper.OrgUtils"); +dojo.require('openils.Event'); +dojo.require('openils.User'); +dojo.require('openils.acq.LineitemAttr'); + +var provider = null; +var marcRegex = /^\/\/\*\[@tag="(\d+)"]\/\*\[@code="(\w)"]$/; +var attrDefStores; + + +function getOrgInfo(rowIndex) { + data = providerGrid.model.getRow(rowIndex); + if(!data) return; + return fieldmapper.aou.findOrgUnit(data.owner).shortname(); +} + +function getTag(rowIdx) { + data = this.grid.model.getRow(rowIdx); + if(!data) return; + return data.xpath.replace(marcRegex, '$1'); +} + +function getSubfield(rowIdx) { + data = this.grid.model.getRow(rowIdx); + if(!data) return; + return data.xpath.replace(marcRegex, '$2'); +} + + +function loadStores(onload) { + if(attrDefStores) + return onload(); + openils.acq.LineitemAttr.createAttrDefStores( + function(stores) { + attrDefStores = stores; + onload(); + } + ) +} + + +function loadMarcAttrGrid() { + loadStores( + function() { + var store = new dojo.data.ItemFileReadStore({data:attrDefStores.marc}); + var model = new dojox.grid.data.DojoData( + null, store, {rowsPerPage: 20, clientSort: true, query:{id:'*'}}); + liMarcAttrGrid.setModel(model); + liMarcAttrGrid.update(); + } + ); +} + +function loadGeneratedAttrGrid() { + loadStores( + function() { + var store = new dojo.data.ItemFileReadStore({data:attrDefStores.generated}); + var model = new dojox.grid.data.DojoData( + null, store, {rowsPerPage: 20, clientSort: true, query:{id:'*'}}); + liGeneratedAttrGrid.setModel(model); + liGeneratedAttrGrid.update(); + } + ); +} + +/* +function createOrderRecordField(fields) { + fields.provider = providerId; + if(!fields.xpath) + fields.xpath = '//*[@tag="'+fields.tag+'"]/*[@code="'+fields.subfield+'"]'; + delete fields.tag; + delete fields.subfield; + openils.acq.Provider.createLineitemProviderAttrDef(fields, + function(id) { + loadPADGrid(); + } + ); +} + +function setORDesc() { + var code = dijit.byId('oils-acq-provider-or-code'); + var desc = dijit.byId('oils-acq-provider-or-desc'); + desc.setValue(code.getDisplayedValue()); +} + +function deleteORDataFields() { + var list = [] + var selected = padGrid.selection.getSelected(); + for(var idx = 0; idx < selected.length; idx++) + list.push(padGrid.model.getRow(selected[idx]).id); + openils.acq.Provider.lineitemProviderAttrDefDeleteList( + list, function(){loadPADGrid();}); +} +*/ + + +//dojo.addOnLoad(loadLIAttrGrid); + + diff --git a/Open-ILS/web/templates/base.tt2 b/Open-ILS/web/templates/base.tt2 new file mode 100644 index 0000000000..84549c1cbe --- /dev/null +++ b/Open-ILS/web/templates/base.tt2 @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + [% content %] + + diff --git a/Open-ILS/web/templates/default/acq/common/jubgrid.tt2 b/Open-ILS/web/templates/default/acq/common/jubgrid.tt2 new file mode 100644 index 0000000000..94bc3748f2 --- /dev/null +++ b/Open-ILS/web/templates/default/acq/common/jubgrid.tt2 @@ -0,0 +1,209 @@ +[%#- +This template creates a split screen Dojo layout. The top frame +of the screen holds a list of of JUBs, or titles. Clicking on a +title in the top frame will load the purchase details for all the +copies on order for that title into the bottom frame. + +To create a display for a set of JUBs, create a Dojo store and +model for the set of JUBs, then place the following lines in your +HTML where you want the display to appear: + + <%namespace file='/oils/default/common/jubgrid.html' name='jubgrid'/> + ${jubgrid.jubgrid('dom_prefix', 'grid_jsid')} + +where 'dom_prefix' is a string that will be used as the prefix +for the DOM notes that are created by this template, and +'grid_jsid' is a valid JavaScript identifier that will name the +DOM node to which the list of JUBs will be attached. For example + + ${jubgrid.jubgrid('oils-acq-picklist', 'pickListGrid', hideDetails)} + +will create a Dojo grid with the DOM id of + + 'oils-acq-picklist-JUB-grid' + +and a jsid of + + pickListGrid + +To fill the grid with data, call the javascript function + + JUBGrid.populate(grid_jsid, model) + +'grid_jsid' is the same javascript id that was used to +instantiate the template, and model is a javascript variable +pointing to the JUB model (and store) that you have created. +-#%] + +[% UNLESS hide_details %] +
+[% END %] + + + + + + + +[% UNLESS hide_details %] + + +
+
+ + +
+
+
+[% ELSE %] +
+[% END %] +
+
+
+[% UNLESS hide_details %] + +
+
+
+ New Copy +
+ + + + + + + + + + + + + +
+ +
+
+ +
+
+
+ + +
+
+ + +
+
+
+
+
+
+
+[% END %] diff --git a/Open-ILS/web/templates/default/acq/financial/list_currency_types.tt2 b/Open-ILS/web/templates/default/acq/financial/list_currency_types.tt2 new file mode 100644 index 0000000000..b98aab38c8 --- /dev/null +++ b/Open-ILS/web/templates/default/acq/financial/list_currency_types.tt2 @@ -0,0 +1,58 @@ +[% WRAPPER 'default/base.tt2' %] +
+
Currency Types
+
+ + + + + + +
+
+ New Currency Type + +
+ + + + + + + + + + + + + + +
+ +
+
+
+ + +
+ + + +
+[% END %] diff --git a/Open-ILS/web/templates/default/acq/financial/list_funding_sources.tt2 b/Open-ILS/web/templates/default/acq/financial/list_funding_sources.tt2 new file mode 100644 index 0000000000..9924621196 --- /dev/null +++ b/Open-ILS/web/templates/default/acq/financial/list_funding_sources.tt2 @@ -0,0 +1,98 @@ +[% WRAPPER 'default/base.tt2' %] +
+
Funding Sources
+
+ + + + + + +
+
+ New Funding Source + +
+ + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+ +
+
+
+ + +
+ + + +
+[% END %] diff --git a/Open-ILS/web/templates/default/acq/financial/list_funds.tt2 b/Open-ILS/web/templates/default/acq/financial/list_funds.tt2 new file mode 100644 index 0000000000..dfaad92ffb --- /dev/null +++ b/Open-ILS/web/templates/default/acq/financial/list_funds.tt2 @@ -0,0 +1,109 @@ +[% WRAPPER 'default/base.tt2' %] + +
+
Funds
+
+ + + + + + +
+
+ New Fund + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+ +
+
+
+ + + + + +
+ + + +
+[% END %] + diff --git a/Open-ILS/web/templates/default/acq/financial/list_providers.tt2 b/Open-ILS/web/templates/default/acq/financial/list_providers.tt2 new file mode 100644 index 0000000000..607dc6971a --- /dev/null +++ b/Open-ILS/web/templates/default/acq/financial/list_providers.tt2 @@ -0,0 +1,75 @@ +[% WRAPPER default/base.tt2 %] + + +
+
Providers
+
+ +
+
+ New Provider + +
+ + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+ +
+
+
+
+ + + + + + +
+[% END %] + diff --git a/Open-ILS/web/templates/default/acq/financial/view_fund.tt2 b/Open-ILS/web/templates/default/acq/financial/view_fund.tt2 new file mode 100644 index 0000000000..5c805df2d0 --- /dev/null +++ b/Open-ILS/web/templates/default/acq/financial/view_fund.tt2 @@ -0,0 +1,145 @@ +[% WRAPPER 'default/base.tt2' %] + + + + + +
+
Fund Details
+
+ +
+ +
+ Create Allocation +
+ + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+ + +
+ +
+ +
+
+
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+
+[% END %] diff --git a/Open-ILS/web/templates/default/acq/financial/view_funding_source.tt2 b/Open-ILS/web/templates/default/acq/financial/view_funding_source.tt2 new file mode 100644 index 0000000000..3d21a63d56 --- /dev/null +++ b/Open-ILS/web/templates/default/acq/financial/view_funding_source.tt2 @@ -0,0 +1,162 @@ +[% WRAPPER 'default/base.tt2' %] + + + + + +
+
Funding Source Details
+
+ +
+ + +
+ Apply Credit +
+ + + + + + + + + + + + +
+ + +
+ + +
+ +
+
+
+
+ Allocate to Fund +
+ + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+ + +
+ +
+ +
+
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+
+
+[% END %] + diff --git a/Open-ILS/web/templates/default/acq/financial/view_provider.tt2 b/Open-ILS/web/templates/default/acq/financial/view_provider.tt2 new file mode 100644 index 0000000000..1b34cf99a4 --- /dev/null +++ b/Open-ILS/web/templates/default/acq/financial/view_provider.tt2 @@ -0,0 +1,117 @@ +[% WRAPPER default/base.tt2 %] + + + +
+
+
+ +
+
+ + +
+ +
+ + +
+ Create Order Record Field +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ +
+
+
+ + + +
+ + + +
+
+
+
+[% END %] + diff --git a/Open-ILS/web/templates/default/acq/picklist/bib_search.tt2 b/Open-ILS/web/templates/default/acq/picklist/bib_search.tt2 new file mode 100644 index 0000000000..71cdf24046 --- /dev/null +++ b/Open-ILS/web/templates/default/acq/picklist/bib_search.tt2 @@ -0,0 +1,99 @@ +[% WRAPPER 'default/base.tt2' %] + + + + + +
+
+ +
+
Search Sources
+ +
+
+
+ +
+
+
Search Fields
+
+
+ + + + + + + + + + + +
Hits Per Source +
+
+
Submit
+
+
+
+ +
+ +
+ Save Results +
+ + + + + + + + + + + + + + + + + +
+ + + + +

+ +
+ +
+
+
+ [% grid_jsid = 'plResultGrid'; domprefix = 'oils-acq-lineitem'; hide_details = 1 %] + [% INCLUDE 'default/acq/common/jubgrid.tt2' %] +
+ +[% END %] + diff --git a/Open-ILS/web/templates/default/acq/picklist/list.tt2 b/Open-ILS/web/templates/default/acq/picklist/list.tt2 new file mode 100644 index 0000000000..c732b991bc --- /dev/null +++ b/Open-ILS/web/templates/default/acq/picklist/list.tt2 @@ -0,0 +1,66 @@ +[% WRAPPER 'default/base.tt2' %] +
+
+
My Picklists
+
+
+
All Picklists
+
+
+ + + + + +
+
+ New Picklist +
+ + + + + + + + +
+ +
+
+
+ +
+ + + +
+[% END %] diff --git a/Open-ILS/web/templates/default/acq/picklist/view.tt2 b/Open-ILS/web/templates/default/acq/picklist/view.tt2 new file mode 100644 index 0000000000..157477dc80 --- /dev/null +++ b/Open-ILS/web/templates/default/acq/picklist/view.tt2 @@ -0,0 +1,41 @@ +[% WRAPPER 'default/base.tt2' %] + + +
+
+
+ Picklist + +
+
Create + date:
+
Last updated:
+
Selector:
+
+
+
+ + [% grid_jsid = 'pickListGrid'; domprefix = 'oils-acq-picklist' %] + [% INCLUDE 'default/acq/common/jubgrid.tt2' %] +
+[% END %] + diff --git a/Open-ILS/web/templates/default/acq/po/li_search.tt2 b/Open-ILS/web/templates/default/acq/po/li_search.tt2 new file mode 100644 index 0000000000..32ad32b642 --- /dev/null +++ b/Open-ILS/web/templates/default/acq/po/li_search.tt2 @@ -0,0 +1,109 @@ +[% WRAPPER default/base.tt2 %] + + + +
+
+ + + + + + + + + + + + + +
+ +
+ +
Search
+
+
+ +
+
+
+ + +
+ reate PO +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ [% grid_jsid = 'liGrid'; domprefix = 'oils-acq-li-search' %] + [% INCLUDE 'default/acq/common/jubgrid.tt2' %] +
+ +[% END %] + + diff --git a/Open-ILS/web/templates/default/acq/po/search.tt2 b/Open-ILS/web/templates/default/acq/po/search.tt2 new file mode 100644 index 0000000000..951795a56a --- /dev/null +++ b/Open-ILS/web/templates/default/acq/po/search.tt2 @@ -0,0 +1,76 @@ +[% WRAPPER default/base.tt2 %] +
+
PO Search
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+ +
+
Search
+
+ + +
+ +[% END %] + diff --git a/Open-ILS/web/templates/default/acq/po/view.tt2 b/Open-ILS/web/templates/default/acq/po/view.tt2 new file mode 100644 index 0000000000..3779485900 --- /dev/null +++ b/Open-ILS/web/templates/default/acq/po/view.tt2 @@ -0,0 +1,40 @@ +[% WRAPPER default/base.tt2 %] + + + + +
+
PO Details
+
+ +
+
+ +
+ + +
+
+
+ + [% grid_jsid = 'liGrid'; domprefix = 'oils-acq-lineitem' %] + [% INCLUDE 'default/acq/common/jubgrid.tt2' %] +
+
+
+[% END %] diff --git a/Open-ILS/web/templates/default/acq/receiving/process.tt2 b/Open-ILS/web/templates/default/acq/receiving/process.tt2 new file mode 100644 index 0000000000..8ca1eae4a9 --- /dev/null +++ b/Open-ILS/web/templates/default/acq/receiving/process.tt2 @@ -0,0 +1,38 @@ +[% WRAPPER default/base.tt2 %] + +

Receiving Processing


+
+
+ + + + + + + + + + + + + +
+ +
+ +
Search
+
+
+
+ +
+ [% grid_jsid = 'liGrid'; domprefix = 'oils-acq-recv' %] + [% INCLUDE 'default/acq/common/jubgrid.tt2' %] +
+ +[% END %] + diff --git a/Open-ILS/web/templates/default/acq/settings/li_attr.tt2 b/Open-ILS/web/templates/default/acq/settings/li_attr.tt2 new file mode 100644 index 0000000000..5a2b16a003 --- /dev/null +++ b/Open-ILS/web/templates/default/acq/settings/li_attr.tt2 @@ -0,0 +1,73 @@ +[% WRAPPER default/base.tt2 %] + + +
+
+ Lineitem Attribute Attribute +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+
+
+ +
+
+ +
+
+
+ +
+
+
+
+[% END %] diff --git a/Open-ILS/web/templates/default/base.tt2 b/Open-ILS/web/templates/default/base.tt2 new file mode 100644 index 0000000000..0a36541665 --- /dev/null +++ b/Open-ILS/web/templates/default/base.tt2 @@ -0,0 +1,28 @@ +[% WRAPPER 'base.tt2' %] + + + +
+
+
+ [% INCLUDE default/menu.tt2 %] +
+
+ [% INCLUDE default/header.tt2 %] +
+
+
+
+
+
+ [% content %] +
+
+ +
+[% END %] diff --git a/Open-ILS/web/templates/default/footer.tt2 b/Open-ILS/web/templates/default/footer.tt2 new file mode 100644 index 0000000000..2b087d2434 --- /dev/null +++ b/Open-ILS/web/templates/default/footer.tt2 @@ -0,0 +1,2 @@ +Powered By + diff --git a/Open-ILS/web/templates/default/header.tt2 b/Open-ILS/web/templates/default/header.tt2 new file mode 100644 index 0000000000..19040564eb --- /dev/null +++ b/Open-ILS/web/templates/default/header.tt2 @@ -0,0 +1,11 @@ +
+ + +
diff --git a/Open-ILS/web/templates/default/menu.tt2 b/Open-ILS/web/templates/default/menu.tt2 new file mode 100644 index 0000000000..b33ff89b51 --- /dev/null +++ b/Open-ILS/web/templates/default/menu.tt2 @@ -0,0 +1,83 @@ +
+ + + +
+ -- 2.43.2