pax_global_header00006660000000000000000000000064141061645030014512gustar00rootroot0000000000000052 comment=33edf374f0db001befc2a0f85a164eb09d95a14f kraft-0.97/000077500000000000000000000000001410616450300125605ustar00rootroot00000000000000kraft-0.97/AUTHORS000066400000000000000000000001651410616450300136320ustar00rootroot00000000000000Klaas Freitag Thomas Richard Heike Freitag kraft-0.97/CMakeLists.txt000066400000000000000000000053131410616450300153220ustar00rootroot00000000000000project(kraft) cmake_minimum_required(VERSION 2.8.12) cmake_policy(SET CMP0063 NEW) find_package(ECM REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules) set(CMAKE_AUTOMOC TRUE) include(KDEInstallDirs) include(KDECMakeSettings) include(ECMInstallIcons) include(KDEFrameworkCompilerSettings) include(KDECMakeSettings) include(FeatureSummary) # Uncomment to enable some tweaks for AppImage build # add_definitions(-DBUILD_APPIMAGE=1) # TODO: Build without deprecated QUrl methods add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0) remove_definitions(-DQT_NO_SIGNALS_SLOTS_KEYWORDS) remove_definitions(-DQT_NO_CAST_FROM_ASCII) find_package(Qt5 CONFIG REQUIRED Core Gui Sql Widgets Xml ) # XmlGui for the ConfigSkeleton find_package(KF5 REQUIRED COMPONENTS I18n Config Contacts ) # Grantlee templating engine find_package(Grantlee5 REQUIRED) set_package_properties(Grantlee5 PROPERTIES DESCRIPTION "Library for templating html and pdf output" URL "https://www.grantlee.org/" PURPOSE "Optionally used for templating" TYPE OPTIONAL ) find_package(KF5Akonadi) set_package_properties(KF5Akonadi PROPERTIES DESCRIPTION "Library for general Access to Akonadi" URL "https://www.kde.org/" PURPOSE "Optionally used for addressbook integration" TYPE OPTIONAL ) find_package(KF5AkonadiContact) set_package_properties(KF5AkonadiContact PROPERTIES DESCRIPTION "Library for Accessing Contacts stored in Akonadi" URL "https://www.kde.org/" PURPOSE "Optionally used for addressbook integration" TYPE OPTIONAL ) if(KF5Akonadi_FOUND AND KF5AkonadiContact_FOUND) add_definitions(-DHAVE_AKONADI) endif() find_package(Ctemplate REQUIRED) find_package(Asciidoctor) # Sets the variable ASCIIDOCTOR_FOUND # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-suggest-override") # disable the warning about null-pointer zero. FIXME. set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-zero-as-null-pointer-constant") #option(QTINDICATE_DISABLE "Disable support for notifications via indicator") # disable the warning about null-pointer zero. FIXME. set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-zero-as-null-pointer-constant") add_definitions(${QT_DEFINITIONS} ) include_directories(${QT_INCLUDES} src) include_directories( /usr/include/KF5/AkonadiCore ) if (IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/po") ki18n_install(po) endif() add_subdirectory(src) add_subdirectory(database) add_subdirectory(reports) add_subdirectory(views) add_subdirectory(importfilter) add_subdirectory(tools) add_subdirectory(styles) add_subdirectory(meta) add_subdirectory(tests) add_subdirectory(manual) feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) kraft-0.97/COPYING000066400000000000000000000432541410616450300136230ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. kraft-0.97/Changes.txt000066400000000000000000000411341410616450300146740ustar00rootroot00000000000000 Changes since version 0.96: ######################################### Add read only mode to kraft. Documents can not be edited, but viewed and printed. Needs to be configured to share database and the PDF store directory. - Fix: Bring back the checkbox for items, this fixes #103 - Fix: Command line option -d works again - Fix: Fix out of index deletes of items, #102 - Fix: Do watermark on PDFs properly - Fix: Fix watermark on all pages functionality - Fix: Align watermark files entry fields properly (community contrib) - Cmake: Build a static lib of all sources to link with kraft and all the tests. => release v. 0.97 (Aug 15, 2021) Changes since version 0.95: ######################################### - Found new MIT licensed icons to avoid uncertainess with CC license. - Allow to use the "add new" button in the doc editor to add new catalog templates. It presets the correct chapter. - Fix: Use the xmlArchivePath correctly (#80) - Fix: Handling of Cancel button in template to doc dialog. - Fix: Convert newlines in the items to
for the weasyprint doc generation. - Fix: In Followup document: If the standard text for pre and post text of the target document type is empty, the one from the source document is copied over instead. (#91) => release v. 0.96 (Feb. 27, 2021) Changes since version 0.90: ######################################### - Add Grantlee as templating engine. - Add Weasyprint as rendering engine for PDFs. - Removed Splash screen completely for simplification. - Switch to python3 with the erml2pdf tool. - Add a date format selector to Kraft settings. Allow to set a four digit year in dates and other formats. - Fix: Show proper amount of items in no-price-display mode. - Fix: Show proper timestamp of last change of catalog items with SQLite database. - Fix: show proper number of items also in Lieferschein. - Fix: Save some more window states (size, position) - Fix: Set the unit of discount items to pauschal. - Fix: Also change lastmodified-Timestamp if only an item was added. - Add the user manual. Open it according to user language. => release v. 0.95 (Aug. 28, 2020) Changes since version 0.82: ######################################### - Reworked follow up and copy document * set the correct header- and footer-texts according to the doc type * Added a checkbox if items should be copied or not - New feature: partial invoices that are substracted in the final invoice - Use an XML based migration system for document types - Added the first unit tests to Kraft - Made the document templates not containing any language specific strings any more. There is only one doc now for all languages, getting translated strings as template variables. - Removed KeepTogether flag for tables to avoid that a long list of items only starts on the next page. Might have impact on some docs. - Fixed formatting of the amount number in the XML output - More Less-KDE: Removed more mandatory dependencies on KDE. - Added 'About Kraft' information to Krafts system view. - Added document type 'Offer without price tags', which does not print price tags on the PDF (issue #58). - Internationalization: Added dutch translation => release v. 0.90 (Dec. 14, 2019) Changes since version 0.81: ######################################### - Fix send document by email - Allow emailing through xdg-email and not only through thunderbird To enable, set mailUA=xdg in the config file in the system section. - Fixed wrong usage of i18n command which caused a lot of error messages on the console. (#37) - Time calculation: Add a time unit to the form, allow to calculate times in hours, minutes and seconds (#43) - Refactored the calculation dialog, fixed using margin. (#42) - Properly ask to waste changes if user hits cancel in Calc dialog. - Fix some glitches in the setup code path, ie. db update. - Relaxed the document layout a bit by choosing a smaller font and adjusting the table column settings a bit. - More minor bug fixes and improvements => release v. 0.82 (Oct 17, 2018) Changes since version 0.80: ######################################### - Fix to build with Qt 5.11 - CMake fixes: Installation directories - Use QProcess instead of system call - Fix appstream XML data - Fix display of individual tax block on documents => release v. 0.81 (June 12, 2018) Changes since version 0.59: ######################################### - Port to Qt5/KDE Frameworks 5 - Reduction of build and runtime dependencies, especially of KDE Frameworks, replacing old KDE classes with their Qt equivalents - Dropped Webkit. Use QTextbrowser instead. Ported html generating code and CSS accordingly - Dependency on Akonadi is now optional, so builds without Akonadi are possible - Address management in Kraft was abstracted to work with or without Akonadi, other address backends could be implemented much easier now - Refresh of the GUI plus new icons, easier. - Refactored time line models completely - Show summaries for month and year items: Amount of doc types and added sums for each document type - GUI: New filter combo to limit to docs of last week or last month - Use templates for system view (contributed by Andy Wuest) - Ship an AppImage for easy testing of Kraft - Updated ReportLab based PDF generation script (erml2pdf) - countless bug fixes and improvements => release v. 0.80 (Apr 1, 2018) Changes since version 0.57: ######################################### - Fix handling of slashes in the doc id template - Fix a bug in calculation of the VAT sum - Removed kraftcat library as it was unused. Easier building now. => release v. 0.58 (Apr, 2014) Changes since version 0.56: ######################################### - No code changes. Just version bump because of a bogus tarball. => release v. 0.57 (Nov 7, 2014) Changes since version 0.55: ######################################### - Fix handling of custom greetings in combobox. - Handle document type changes correctly: Set a new ident number depending on the new document id - Fix behaviour of the greeting combobox: Do not loose custom entries any more - Add receipient email address if document is emailed - Fix document emailing for thunderbird - Fix removing of alternative- and on-demand state of items - Wording fixes - Fix Ok/Cancel for doc editor - Better error messages if template can not be read - A couple of crash fixes and memory management cleanups => release v. 0.56 (oct 30, 2014) Changes since version 0.54: ######################################### - Fix a bug with the PDF generation, regression in 0.54 - Fix compiling with latest version of ctemplate => release v. 0.55 (may 29, 2014) Changes since version 0.53: ######################################### - Use new address fetch job implementation that works independant from Nepomuk- or Baloo indexing of contacts. (KDE >= 4.12) - Support note-of-delivery documents (Lieferscheine) without prices. - Added findcontact utility - Generate a customer sorted document storage structure on disk - Improve stylesheet handling in templates, introduce CSS_IMG_PATH - Use environment variable KRAFT_HOME more consquentely - Fix tax sum calculation - Add customer address UID variable to number circle tags - Some memleak fixes - Add "followup document" to main menu - Fix installation of identity.trml - Allow the tags USERNAME, DATE and TIME in item templates. - Lots of other cleanups and fixes => release v. 0.54 (may 13, 2014) Changes since version 0.50: ######################################### - Fixes drag and drop handling in catalog window. - Fixes with units, do not confuse units any more - Utf8 fixes - other minor fixes - Releases 0.51 and 0.52 screwed. => release v. 0.53 (oct 11, 2013) Changes since version 0.50: ######################################### - Fix drag and drop in catalogues. - Implemented removal of sub chapters. - Add the content of an entire template catalog chapter to the document if the chapter item is selected. - Added a new setting "Own Identity" to allow to pick the own identity from the address books in an installed system. - Make text template loading utf8 save. - Reworked image-in-template example in invoice report template. - Fixed unit handling, no more startsWith coparison of text. => release v. 0.51 (sep 22, 2013) Changes since version 0.45: ######################################### - Handling of individual tax rates for each document item - bugfix: escape texts in reports correctly (reported by Lars Diel). - bugfix: if akonadi address was not found, display proper msg. - Proper error message if a python module is not installed but needed by the pdf generator. - added button to assign an address book entry as document receiver for existing documents (Bug #3477467) - bugfix: Display of number of processed sql commands in setup assistant (Bug #3560611) - started to optimize database save of documents for more performant saves. - bugfix: Fixed import of document items from csv lists. - added page number on default document from page two to end. - bugfix: Fixed mysql database setup. - Rearragned doc type setup dialog to better display path names of template file and watermark file. - bugfix: If a manual entered template has checkbox "store in template catalog, the template is immediately written to and catalog is reloaded. - bugfix: Focus on the last added item on document edit. => release v. 0.50 (dec 17, 2012) Changes since version 0.44: ######################################### - bugfix release: Fixed the display of decimal places in the PDF document which caused broken documents. => release v. 0.45 (nov 08,2011) Changes since version 0.43: ######################################### - Completely reworked the addressbook widget in Kraft, used in new doc assistant and in the "who am i" dialog: Fully Akonadi-based, works with various addressbooks and loads addresses way more fast. - Completely reworked text template widget used in the document editor: Less bugs, more obvious gui, help texts where needed - Fixed bug that crashed Kraft when clicking on empty catalog chapters. - Add ability to add localized report template files, added german localization - Fixed bug that numbers in the doc are not localized correctly - Fixed bug that the doc type is not taken correctly from the new doc wizard - No need any more to checkbox single item templates to move them into the document. Selecting is sufficient. - Added a search line in the "who am i"-dialog => release v. 0.44 (nov 03,2011) Changes since version 0.41: ######################################### - Added search box in digest lists again, for that enhanced the search field class. - cleaned up the digest models - optimized the digest model - changed from treeview to tableview for latest- and all view => release v. 0.43 (may 25, 2011) Changes since version 0.40: ######################################### - More changes to the Akonadi based addressbook integration - switched to a python only version of trml2pdf called erml2pdf, makes porting to other platforms easier plus dropped dependency on java with pdftk - used Qt Model/View for the document lists. Way faster startup. - New document digest view - Catalogs: Implemented nested catalog structure - Catalogs: Moving templates around per d&d - Catalogs: user defined sorting in catalogs - Catalogs: dropping of templates => released v. 0.41 (april 18th, 2011) Changes since version 0.32: ######################################### - Ported to the KDE 4 Platform. In particular, to KDE 4.4 - Use Akonadi based addressbook - Edit dialog for Wages and Units - SQLite-Support - Setup Assistant for SQLite and MySQL Databases => released v. 0.40 Changes since version 0.31: ######################################### - Tax: Added more flexible tax calculation. Kraft now has the tax in a table for easy changing, supports no, reduced and full tax on document level for this release, on item level later. - Numbercycles: All Kraft documents need an unique document number. These are taken from numbercycles now which can be edited and shared between document types. - Watermark: Kraft documents can now be merged with an existing pdf file with the company logo. Configuration through the Kraft settings on a per document type basis - Templates: Kraft now supports different templates for each document type. Templates can be configured through the Kraft settings. - Project-String: There is a project label at the document. It is exported to the document template. - System-Tab: Display of some interesting setup information - Templates for PDF and HTML output are reloaded if modified => released v. 0.32 Changes since version 0.30: ######################################### - added a read only view on documents, customizable with ctemplate as html page - added importing items from text files with configurable import filters, to enable usage of special software which is able to export text files - added document number cycles: The unique id of a document depends on a unique number. Now there can be multiple number cycles which can be used by document types. Multiple doc types can use the same number cycle, that means that for example all types of invoices count the same number cycle while offers are in another. => released v. 0.31 Changes since version 0.25: ######################################### - introduced the extended combo box with explanation line for units. - added document item tagging - added discount item system, based on tagging - tag templates with tag template edit dialog - fixed a bunch of bugs with text templates (header- and footer text) - tag selection in ordinary add-item-to-document dialog => released v. 0.30 Changes since version 0.24: ######################################### - Added a Wizard for document creation - Improved the document overview widget for more intuitive use. - Copying of complete documents added. - Followup documents (eg. Invoice follows Offer) added - Litte marker for new documents added to doc overview list. - Help text added to positions canvas - Client address bits added to the available template variables Changes since version 0.23: ######################################### - internationalisation: Ability to do documents in a different location than the desktop is running under. => released BETA v. 0.24 Changes since version 0.22: ######################################### - fixed: database migration 5: added IF EXISTS - fixed: encoding in the report file - improved: The document digest overview is not longer always redrawed and looses its selected item. => released v. 0.23 Changes since version 0.20: ######################################### - fixed: select from catalogs with doubleclick - fixed: allow prices for positions larger than 10k - fixed: openArchivedDocument printed again instead of opening archived doc from the archive path - fixed: template texts in the catalog view where not shown if they were exactly 60 chars long. - feature: Alternative positions - feature: Demand positions - feature: Switched to google template system, see http://code.google.com/p/google-ctemplate/ - bugfix: Catalog changes show up directly in opened catalogs, also if open as a template catalog in the doc window. => released v. 0.22 Changes since version 0.14: ######################################### - fixed bug with manual price field that needs to be left by TAB to get a change recognised. - increased precision to 2 digits for position amounts - allow euro-sign in texts and in the whiteboard. Due to a lack in the qt3 mysql driver the char needs to be en- and decoded. - reduced the amount of toolbar buttons to only show the important ones. - more beautifull and working navigation block in the document dialog - mailing documents added - Completely changed header- and footer text template system: * now there is more than one text available per text- and doc type * direct adding, editing and removing of texts from the dialog * selecting template texts in the same way as selecting position templates, more intuitive GUI - Addresses also selectable from an address catalog - persist the selection of the greeting and salut text - Document ID now configurable in the settings file, key DocIdent: [document] DocIdent=T-%y%w%i - database upgrade code more robust - now additionally save archived xml documents Bugs fixed: - fixed time lined doc overview, now displaying all documents of all months of a year. - fixed modification indication: Now also adding a position counts as modification - encoding bugs fixed - allow to leave text edit fields using the tab key - deletion of positions: Fixed an iteration over the list issue - smarter template-to-doc-position dialog - others kraft-0.97/INSTALL000066400000000000000000000104151410616450300136120ustar00rootroot00000000000000 ----------------------------------------------------- Packages ----------------------------------------------------- Linux Distributions have package management systems to provide users with all kind of software in a clean and easy way. This should be the prefered way to install Kraft. Check the package pools of your prefered distribution first. If the package is outdated, consider asking your distribution to upgrade! ----------------------------------------------------- Compiling Kraft ----------------------------------------------------- The following section briefly describes how to build Kraft with cmake. ----------------------------------------------------- Precondition ----------------------------------------------------- Check that cmake is installed on your machine and is in your PATH. To do so, just type $ cmake --version on your command line. Version 2.4 is required, the most recent stable version of cmake is preferred. To build Kraft, the following libs and software versions have to be provided: - cmake and the cmake extra modules - Qt libs incl. devel packages version 5.5.0 or later - kcontacts for using the KDE contact classes - A few other KDE classes (kxmlgui, ki18n) - google ctemplate, A simple but powerful template language for C++, packages from the openSUSE Buildservice or from the website https://github.com/OlafvdSpek/ctemplate - optional: akonadi contact for Akonadi based addressbook access - optional: grantlee, an C++ text template framework Required packages for building with openSUSE: - cmake - extra-cmake-modules - gcc-c++ - kcontacts-devel - akonadi-contact-devel - gettext - libctemplate-devel - ki18n-devel - kxmlgui-devel - libQt5Core-devel - libQt5Gui-devel - libQt5Sql-devel - libQt5Widgets-devel ----------------------------------------------------- Build Kraft ----------------------------------------------------- cmake is designed so that the build process can be done in a separate directory. This is highly recommended for users and required for packagers. Go to the top level of the source directory. To build Kraft in the subdirectory ./build/ type $ mkdir build $ cd build $ cmake .. to generate the Makefiles. $ cmake . to change the configuration of the build process. (optional) Check out for errors during the cmake run. Fix them, usually you need more devel packages installed. Ready? Congratulations, your Makefiles were generated! Now you could just type $ make to build the project in the build/ directory. Note that 'make' automatically checks whether any CMakeLists.txt file has changed and reruns cmake if necessary. To start Kraft from the build directory, set the environment variable KRAFT_HOME to the root of the _source_ directory to let Kraft find its resource files: $ KRAFT_HOME=/home/me/kraft build/src/kraft Kraft Installation ================== Type $ make install To change the target root directory to where it is installed, call cmake with the parameter -DCMAKE_INSTALL_PREFIX=/my_dir Kraft Manual ============ Kraft ships a user manual in different languages. To rebuild it, asciidoctor is required. If that dependency is found, cmake detects it and gives a new make target: $ make manual to re-create the docs. Database ======== Kraft either can use a SQLite file based database or a MySQL server based database. The SQLite database is created automatically on the fly on first start. Its use is recommended for all users who want to evaluate Kraft. To run Kraft with MySQL, create or pick a user on the MySQL server with appropiate permissions to write to a specific database and create tables on it. Create an empty database to use with Kraft. Remember both the database name and the credentials. On Krafts first start, enter these data in the setup assistant. Kraft will create the database tables and fill it automatically. Document Generation =================== Kraft generates PDF documents. For that it uses a python tool named erml2pdf. The tool can be found in Kraft's tools directory in this source package. erml2pdf uses a python lib called the ReportLab Toolkit: a mature, Open Source PDF library which can be found at http://www.reportlab.org/rl_toolkit.html Furthermore it uses python-pypdf for pdf processing. Both python modules are not part of Kraft and should be installed separately on the system. kraft-0.97/README.md000066400000000000000000000031631410616450300140420ustar00rootroot00000000000000 # The Kraft Project **Kraft is free software to help to handle documents like quotes and invoices in your small business.** It is a Qt/KF5 based desktop software with a strong focus on ease of use and the just enough feature set for the use case. With Kraft, creating documents will run smooth and free time for more enjoyable things than office work. Check out the website http://volle-kraft-voraus.de for more information. Kraft runs on your Linux desktop. No cloud involved, your data stays with you! With Kraft, writing documents like quotes and invoices is very easy and fast. Repeating tasks are supported, documents can be generated semi automatically, ie. invoices from offers sent out before. For efficient work, Kraft supports catalogs to organize materials and template texts. It focuses on high quality printouts because paper is still the main communication media in the small business world. However, it also sends documents via email. Kraft utilizes a bunch of very useful tools of the free softare world: - KDE addressbook for customer management - MySQL or alternatively SQLite for database support - [WeasyPrint](https://weasyprint.org) or [ReportLab](http://www.reportlab.com/opensource/) for PDF generation. - [Grantlee](https://github.com/steveire/grantlee) or [ctemplate](https://github.com/OlafvdSpek/ctemplate) templating library. ## Interested? [Install Kraft](http://volle-kraft-voraus.de/Main/Download) on your Linux desktop. For questions and comments, please speak up. Check the [web site](http://volle-kraft-voraus.de/Main/Contribution) for contact details. Jun 2005-2021, Klaas Freitag kraft-0.97/Releasechecklist.md000066400000000000000000000011571410616450300163600ustar00rootroot00000000000000## Kraft Release Checklist _Before drafting a release, copy this checklist template into an issue for the specific version._ Release Checklist for Kraft Version XX ### Before the first beta - [ ] Check entries in `src/version.h` - [ ] Check the database scheme version number. - [ ] Update Changes file ### Before the first RC - [ ] Send a tweet/blog about the upcoming release ### For every RC - [ ] Create a proper tag - [ ] Update the packages in a hidden repo on OBS - [ ] x ### Build packages - [ ] in OBS for KDE:Extra - [ ] Submit to Tumbleweed ### AppImage - Update the Kraft package for the AppImage kraft-0.97/Releasenotes.txt000066400000000000000000000057401410616450300157600ustar00rootroot00000000000000 ==================================================================== Please refer to the Kraft Project website http://volle-kraft-voraus.de/Main/Releases ==================================================================== older entries: Das Kraft Release 0.22 verwendet ein neues Text-Template System, das für die benutzerkonfigurierbare PDF-Ausgabe der Dokumente verwendet wird. Es wird das google ctemplate-System verwendet, Details dazu unter http://code.google.com/p/google-ctemplate/ Das neue System ist sehr flexibel und stabil und daher zukunftssicher und löst das selbstgeschriebene alte System ab. Leider hat sich die Syntax geändert, weshalb eigene Templates angepasst werden müssen. Weiterhin enthält Kraft 0.22 zwei neue Arten von Dokument Posten. Es gibt Alternativpositionen, die eine Alternative zu einer vorhergegangenen Position beschreiben. Bedarfspositionen beschreiben Aufwände, die nur bei Bedarf anfallen. Beide Arten addieren ihre Kosten nicht zur Gesamtsumme und sind durch Kursivschrift im Dokument hervorgehoben. Weiterhin wurden viele Fehler behoben und generelle Code Verbesserungen vorgenommen. Das Datenbankschema wurde geändert, das Update geschieht automatisch durch Kraft. ---------------------------------------------------------------------- The Kraft release 0.22 changes the text template system that is used by Kraft to support user configureable pdf output. It was changed from a very basic homegrown system to the google ctemplate system, details under http://code.google.com/p/google-ctemplate/ It is very flexible and mature and thus future proof which was reason enough to switch to it at this early project state. The template format is slightly different to the former format, so editing of own templates is required. Additionally two new kinds of document positions were added: Alternative and demand positions. The alternative positions describe an alternative to another position. The demand position describes a position where it is not yet clear if and how much of it is needed. Both position kinds do not add to overall sum and are marked through italic characters in the printout by default. Furthermore a lot of bugfixes and genral code improvements where committed. The database scheme was extended, update is automatically performed. ==================================================================== This release 0.20 of Kraft brings a big step ahead in the way text templates for the header and footer texts for the different doc types are managed. Kraft now supports to have several templates per type (ie. for the header of invoices) and all of them are offered very handy right to select from a catalog kind in a context aware manner. Kraft takes another important step towards a really well integrated KDE addressbook. All customer addresses are managed in the KDE addressbook but there is a customer catalog now integrated. This release contains a lot of important bug fixes and small additions to functionality, update is really recommended. kraft-0.97/TODO000066400000000000000000000002321410616450300132450ustar00rootroot00000000000000Todo list for Kraft =================== Please use the github issue tracker for enhancements and bug reports: https://github.com/dragotin/kraft/issues kraft-0.97/cmake/000077500000000000000000000000001410616450300136405ustar00rootroot00000000000000kraft-0.97/cmake/modules/000077500000000000000000000000001410616450300153105ustar00rootroot00000000000000kraft-0.97/cmake/modules/COPYING-CMAKE-SCRIPTS000066400000000000000000000024571410616450300203160ustar00rootroot00000000000000Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. kraft-0.97/cmake/modules/FindAsciidoctor.cmake000066400000000000000000000005431410616450300213600ustar00rootroot00000000000000# Find Asciidoctor - an Asciidoc converter to html # # ASCIIDOCTOR_FOUND # ASCIIDOCTOR_EXECUTABLE FIND_PROGRAM(ASCIIDOCTOR_EXECUTABLE asciidoctor) MARK_AS_ADVANCED(ASCIIDOCTOR_EXECUTABLE) IF (NOT ASCIIDOCTOR_EXECUTABLE) SET(ASCIIDOCTOR_FOUND "NO") ELSE (NOT ASCIIDOCTOR_EXECUTABLE) SET(ASCIIDOCTOR_FOUND "YES") ENDIF (NOT ASCIIDOCTOR_EXECUTABLE) kraft-0.97/cmake/modules/FindCtemplate.cmake000066400000000000000000000015671410616450300210420ustar00rootroot00000000000000# - Try to find the ctemplate # Once done this will define # # CTEMPLATE_FOUND - system has ctemplate # CTEMPLATE_INCLUDE_DIR - the ctemplate include directory # CTEMPLATE_LIBRARIES - Link this to use ctemplate # # Copyright (c) 2009, Thomas Richard, # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. if (CTEMPLATE_INCLUDE_DIR AND CTEMPLATE_LIBRARIES) # in cache already SET(CTEMPLATE_FOUND TRUE) else (CTEMPLATE_INCLUDE_DIR AND CTEMPLATE_LIBRARIES) FIND_PATH(CTEMPLATE_INCLUDE_DIR ctemplate/template.h) FIND_LIBRARY(CTEMPLATE_LIBRARIES NAMES ctemplate) include(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(Ctemplate DEFAULT_MSG CTEMPLATE_INCLUDE_DIR CTEMPLATE_LIBRARIES ) endif (CTEMPLATE_INCLUDE_DIR AND CTEMPLATE_LIBRARIES) kraft-0.97/database/000077500000000000000000000000001410616450300143245ustar00rootroot00000000000000kraft-0.97/database/CMakeLists.txt000066400000000000000000000001111410616450300170550ustar00rootroot00000000000000add_subdirectory(mysql) add_subdirectory(sqlite3) add_subdirectory(meta) kraft-0.97/database/README000066400000000000000000000024531410616450300152100ustar00rootroot00000000000000Kraft - solution for open craft =============================== Kraft is a program that supports craftsmen in their daily jobs: making offers, invoices and confirmations. Kraft needs a mysql database running. But setup is easy, please try it. How to setup the database? ========================== First, install the mysql packages of your distribution. Create a database user and select a suitable password. Please replace the word 'user' in the following code examples with the user name you created. The database can be set up using the creation scripts in Krafts database directory. The scripts do create the database completely new, even if they exists. The standard way however to let Kraft create and maintain the database schema for you. All that needs to be done is to - start MySQL on the system - have a user and password combination that has acces to the database - create an empty database with a suitable name like for example "kraft". - enter the credentials and the database name in Kraft's config dialog. Schema creation and maintenance is done by Kraft automatically. To create the database, perform the following steps: The database credentials must be entered into the settings dialog in kraft after the first start. Please restart after you have entered the data. That's it. kraft-0.97/database/errorcodes.txt000066400000000000000000000004461410616450300172400ustar00rootroot00000000000000type problem + native db error 1 mysql not started on the system: 'Can't connect to local MySQL server through socket '/var/lib/mysql/mysql.sock' (111) QMYSQL3: Unable to connect' 1 mysql running, but no db kraft there 'Unknown database 'kraft' QMYSQL3: Unable to connect' kraft-0.97/database/meta/000077500000000000000000000000001410616450300152525ustar00rootroot00000000000000kraft-0.97/database/meta/21_meta.xml000066400000000000000000000115161410616450300172300ustar00rootroot00000000000000 Progress Payment Invoice default en PartialInvoice true Final Invoice Invoice Partial Invoice default en PartialInvoice true Final Invoice Invoice Final Invoice default en SubstractPartialInvoice true Teilrechnung default de PartialInvoice true Schlussrechnung Rechnung Abschlagsrechnung default de PartialInvoice true Schlussrechnung Rechnung Schlussrechnung default de SubstractPartialInvoice true Angebot de Abschlagsrechnung Teilrechnung Schlussrechnung Rechnung Auftragsbestätigung de Abschlagsrechnung Teilrechnung Schlussrechnung Rechnung Lieferschein de docTemplateFile delivery_receipt.trml HidePrices 1 Abschlagsrechnung Teilrechnung Schlussrechnung Rechnung Angebot (keine Preise) de docTemplateFile offer_no_prices.trml HidePrices 1 Delivery Receipt en docTemplateFile delivery_receipt.trml HidePrices 1 Final Invoice Partial Invoice Progress Payment Invoice Invoice Acceptance of Order en Final Invoice Partial Invoice Progress Payment Invoice Invoice Offer en Final Invoice Partial Invoice Progress Payment Invoice Invoice Offer (No Pricetags) en docTemplateFile offer_no_prices.trml HidePrices 1 Offerte nl Rekening Pakbon Pakbon nl docTemplateFile delivery_receipt.trml Rekening Rekening nl kraft-0.97/database/meta/CMakeLists.txt000066400000000000000000000002431410616450300200110ustar00rootroot00000000000000########### install files ############### file(GLOB meta_scripts *_meta.xml) install(FILES ${meta_scripts} DESTINATION ${DATA_INSTALL_DIR}/kraft/meta) kraft-0.97/database/mysql/000077500000000000000000000000001410616450300154715ustar00rootroot00000000000000kraft-0.97/database/mysql/CMakeLists.txt000066400000000000000000000003031410616450300202250ustar00rootroot00000000000000add_subdirectory(migration) ########### install files ############### install(FILES create_schema.sql fill_schema_de.sql fill_schema_en.sql DESTINATION ${DATA_INSTALL_DIR}/kraft/dbinit/mysql) kraft-0.97/database/mysql/create_schema.sql000066400000000000000000000117701410616450300210030ustar00rootroot00000000000000# DROP DATABASE IF EXISTS kraft; # CREATE DATABASE kraft DEFAULT CHARACTER SET "utf8"; # use kraft; CREATE TABLE preisArten ( preisArtID INT NOT NULL, preisArt VARCHAR(64) NOT NULL, PRIMARY KEY( preisArtID ) ); CREATE TABLE wordLists( category VARCHAR(64), word VARCHAR(255), PRIMARY KEY( category, word ) ); CREATE TABLE CatalogSet( catalogSetID INT NOT NULL AUTO_INCREMENT, name VARCHAR(255), description VARCHAR(255), catalogType VARCHAR(64), sortKey INT NOT NULL, PRIMARY KEY(catalogSetID) ); CREATE TABLE CatalogChapters( chapterID INT NOT NULL AUTO_INCREMENT, catalogSetID INT NOT NULL, chapter VARCHAR(255), sortKey INT NOT NULL, PRIMARY KEY(chapterID), INDEX(chapter) ); CREATE TABLE Catalog ( TemplID INT NOT NULL AUTO_INCREMENT, chapterID INT NOT NULL default 1, unitID INT NOT NULL, Floskel TEXT, Gewinn DECIMAL(6,2) default 0, zeitbeitrag TINYINT default 1, enterDatum DATETIME, modifyDatum TIMESTAMP, Preisart INT NOT NULL default 1, EPreis DECIMAL(10,2) default 0, PRIMARY KEY( TemplID ), INDEX ( chapterID ) ); UPDATE Catalog SET modifyDatum=enterDatum; CREATE TABLE CalcTime ( TCalcID INT NOT NULL AUTO_INCREMENT, TemplID INT NOT NULL, name VARCHAR(255), minutes INT default 0, percent INT default 0, stdHourSet INT default 0, allowGlobal INT default 1, modDate TIMESTAMP, PRIMARY KEY( TCalcID), INDEX( TemplID ) ); CREATE TABLE CalcFixed( FCalcID INT NOT NULL AUTO_INCREMENT, TemplID INT NOT NULL, name VARCHAR(255), amount DECIMAL(10,2) default 1.0, price DECIMAL(10,2), percent INT default 0, modDate TIMESTAMP, PRIMARY KEY(FCalcID), INDEX(TemplID) ); CREATE TABLE CalcMaterials( MCalcID INT NOT NULL AUTO_INCREMENT, TemplID INT NOT NULL, name VARCHAR(255), percent INT default 0, modDate TIMESTAMP, PRIMARY KEY(MCalcID), INDEX(TemplID) ); CREATE TABLE CalcMaterialDetails( MCalcDetailID INT NOT NULL AUTO_INCREMENT, CalcID INT NOT NULL, materialID INT NOT NULL, amount DECIMAL(10,2), PRIMARY KEY(MCalcDetailID), INDEX(CalcID) ); CREATE TABLE units( unitID INT NOT NULL, unitShort VARCHAR(255), unitLong VARCHAR(255), unitPluShort VARCHAR(255), unitPluLong VARCHAR(255), PRIMARY KEY(unitID), INDEX(unitShort) ); CREATE TABLE stockMaterial ( matID INT NOT NULL AUTO_INCREMENT, chapterID INT NOT NULL default 1, material mediumtext, unitID INT NOT NULL, perPack DECIMAL(10,2), priceIn DECIMAL(10,2), priceOut DECIMAL(10,2), enterDate DATETIME, modifyDate TIMESTAMP, PRIMARY KEY(matID), INDEX(chapterID) ); CREATE TABLE stdSaetze( stdSaetzeID INT NOT NULL AUTO_INCREMENT, name VARCHAR(255), price DECIMAL(10,2), sortKey int, PRIMARY KEY(stdSaetzeID) ); CREATE TABLE document( docID INT NOT NULL AUTO_INCREMENT, ident VARCHAR(32), docType VARCHAR(255), clientID VARCHAR(32), clientAddress TEXT, salut VARCHAR(255), goodbye VARCHAR(128), lastModified TIMESTAMP, date DATE, pretext TEXT, posttext TEXT, PRIMARY KEY( docID ), INDEX(ident), INDEX(clientID) ); CREATE TABLE docposition( positionID INT NOT NULL AUTO_INCREMENT, docID INT NOT NULL, ordNumber INT NOT NULL, text TEXT, amount DECIMAL(10,2), unit INT, price DECIMAL(10,2), PRIMARY KEY( positionID ), INDEX(docID), UNIQUE( docID, ordNumber) ); CREATE TABLE archdocStates( stateID INT NOT NULL AUTO_INCREMENT, state VARCHAR(32), PRIMARY KEY( stateID ) ); CREATE TABLE archdoc( archDocID INT NOT NULL AUTO_INCREMENT, ident VARCHAR(32), docType VARCHAR(255), docDescription TEXT, clientAddress TEXT, salut VARCHAR(255), goodbye VARCHAR(128), printDate TIMESTAMP, date DATE, pretext TEXT, posttext TEXT, state int, PRIMARY KEY( archDocID ), INDEX(ident) ); CREATE TABLE archdocpos( archPosID INT NOT NULL AUTO_INCREMENT, archDocID INT NOT NULL, ordNumber INT NOT NULL, text TEXT, amount DECIMAL(10,2), unit VARCHAR(64), price DECIMAL(10,2), vat DECIMAL(4,1), PRIMARY KEY( archPosID ), INDEX(archDocID), UNIQUE( archDocID, ordNumber) ); CREATE TABLE kraftsystem( dbschemaversion INT NOT NULL, updateUser VARCHAR(256) ); INSERT INTO kraftsystem ( dbschemaversion ) VALUES ( 1 ); # message Database created. kraft-0.97/database/mysql/fill_schema_de.sql000066400000000000000000000064341410616450300211370ustar00rootroot00000000000000DELETE FROM preisArten; INSERT INTO preisArten VALUES (0, 'offen'); INSERT INTO preisArten VALUES (1, 'selbsterstellt'); INSERT INTO preisArten VALUES (2, 'kalkuliert'); DELETE FROM CatalogSet; INSERT INTO CatalogSet (name, description, catalogType, sortKey) VALUES ( "Standard Mustertexte", "Kalkulierte Musterposten", "TemplCatalog", 1 ); SET @newCat := LAST_INSERT_ID(); DELETE FROM CatalogChapters; INSERT INTO CatalogChapters (chapter, sortKey, catalogSetID) VALUES ('Arbeit', 1, @newCat ); INSERT INTO CatalogChapters (chapter, sortKey, catalogSetID) VALUES ('Maschine', 2, @newCat ); INSERT INTO CatalogChapters (chapter, sortKey, catalogSetID) VALUES ('Materialeinsatz', 3, @newCat ); INSERT INTO CatalogChapters (chapter, sortKey, catalogSetID) VALUES ('Service', 4, @newCat ); INSERT INTO CatalogChapters (chapter, sortKey, catalogSetID) VALUES ('Sonstige', 5, @newCat ); INSERT INTO CatalogChapters (chapter, sortKey, catalogSetID) VALUES ('Transport', 6, @newCat ); UPDATE CatalogChapters SET catalogSetID=@newCat; INSERT INTO CatalogSet( name, description, catalogType, sortKey) VALUES ("Material", "Materialkatalog", "MaterialCatalog", 2 ); SET @newCat := LAST_INSERT_ID(); INSERT INTO CatalogChapters (chapter, sortKey, catalogSetID) VALUES ('Schüttgüter', 3, @newCat); INSERT INTO CatalogChapters (chapter, sortKey, catalogSetID) VALUES ('Naturstein', 2, @newCat); INSERT INTO CatalogChapters (chapter, sortKey, catalogSetID) VALUES ('Beton', 1, @newCat); INSERT INTO CatalogChapters (chapter, sortKey, catalogSetID) VALUES ('Rohre', 4, @newCat); DELETE FROM units; INSERT INTO units VALUES (0, 'm', 'Meter', 'm', 'Meter' ); INSERT INTO units VALUES (1, 'qm', 'Quadratmeter', 'qm', 'Quadratmeter' ); INSERT INTO units VALUES (2, 'cbm', 'Kubikmeter', 'cbm', 'Kubikmeter' ); INSERT INTO units VALUES (3, 'Sck.', 'Sack', 'Sck.', 'Saecke' ); INSERT INTO units VALUES (4, 'l', 'Liter', 'l', 'Liter' ); INSERT INTO units VALUES (5, 'kg', 'Kilogramm', 'kg', 'Kilogramm' ); INSERT INTO units VALUES (6, 'Stck.', 'Stueck', 'Stck.', 'Stueck' ); INSERT INTO units VALUES (7, 't', 'Tonne', 't', 'Tonnen' ); INSERT INTO units VALUES (8, 'pausch.', 'pauschal', 'pausch.', 'pauschal' ); INSERT INTO units VALUES (9, 'Std.', 'Stunde', 'Std.', 'Stunden' ); DELETE FROM stdSaetze; INSERT INTO stdSaetze (name, price, sortKey) VALUES ('Geselle', 34.00, 1 ); INSERT INTO stdSaetze (name, price, sortKey) VALUES ('Meister', 39.00, 2 ); INSERT INTO stdSaetze (name, price, sortKey) VALUES ('Helfer', 30.00, 4 ); INSERT INTO stdSaetze (name, price, sortKey) VALUES ('Auszubildender', 21.00, 3 ); INSERT INTO stdSaetze (name, price, sortKey) VALUES ('Maschinenfuehrer', 33.00, 5 ); DELETE FROM wordLists; INSERT INTO wordLists VALUES ('greeting', 'mit den besten Grüssen,' ); INSERT INTO wordLists VALUES ('greeting', 'liebe Grüsse,' ); INSERT INTO wordLists VALUES ('greeting', 'Hochachtungsvoll,' ); INSERT INTO wordLists VALUES ('greeting', 'mit freundlichem Gruß,' ); INSERT INTO wordLists VALUES ('salut', 'Sehr geehrter Herr %NAME,' ); INSERT INTO wordLists VALUES ('salut', 'Sehr geehrte Frau %NAME,' ); INSERT INTO wordLists VALUES ('salut', 'Sehr geehrte Frau %NAME, sehr geehrter Herr %NAME,' ); INSERT INTO wordLists VALUES ('salut', 'Lieber %GIVEN_NAME,' ); INSERT INTO wordLists VALUES ('salut', 'Liebe %GIVEN_NAME,' ); kraft-0.97/database/mysql/fill_schema_en.sql000066400000000000000000000067301410616450300211500ustar00rootroot00000000000000DELETE FROM preisArten; INSERT INTO preisArten VALUES (0, 'open'); INSERT INTO preisArten VALUES (1, 'manual'); INSERT INTO preisArten VALUES (2, 'calculated'); DELETE FROM CatalogSet; INSERT INTO CatalogSet (name, description, catalogType, sortKey) VALUES ( "Standard Templates", "A set of templates suitable for business", "TemplCatalog", 1 ); SET @newCat := LAST_INSERT_ID(); DELETE FROM CatalogChapters; INSERT INTO CatalogChapters (chapter, sortKey, catalogSetID) VALUES ('Work', 1, @newCat ); INSERT INTO CatalogChapters (chapter, sortKey, catalogSetID) VALUES ('Machine', 2, @newCat ); INSERT INTO CatalogChapters (chapter, sortKey, catalogSetID) VALUES ('Concrete', 3, @newCat ); INSERT INTO CatalogChapters (chapter, sortKey, catalogSetID) VALUES ('Stones', 4, @newCat ); INSERT INTO CatalogChapters (chapter, sortKey, catalogSetID) VALUES ('Misc', 5, @newCat ); INSERT INTO CatalogChapters (chapter, sortKey, catalogSetID) VALUES ('Transportation', 6, @newCat ); INSERT INTO CatalogSet( name, description, catalogType, sortKey) VALUES ("Material", "Material Catalog to Use in Calculations in Templates", "MaterialCatalog", 2 ); SET @newCat := LAST_INSERT_ID(); INSERT INTO CatalogChapters ( chapter, sortKey, catalogSetID ) VALUES ('Sand etc.', 3, @newCat ); INSERT INTO CatalogChapters ( chapter, sortKey, catalogSetID ) VALUES ('Stones', 2, @newCat ); INSERT INTO CatalogChapters ( chapter, sortKey, catalogSetID ) VALUES ('Concreate', 1, @newCat ); INSERT INTO CatalogChapters ( chapter, sortKey, catalogSetID ) VALUES ('Pipes', 4, @newCat ); INSERT INTO CatalogChapters ( chapter, sortKey, catalogSetID ) VALUES ('Wood', 5, @newCat ); INSERT INTO CatalogChapters ( chapter, sortKey, catalogSetID ) VALUES ('Art and Furnitures', 6, @newCat ); DELETE FROM units; INSERT INTO units VALUES (0, 'm', 'Meter', 'm', 'Meter' ); INSERT INTO units VALUES (1, 'sm', 'Squaremeter', 'qm', 'Squaremeter' ); INSERT INTO units VALUES (2, 'cbm', 'Cubikmeter', 'cbm', 'Cubikmeter' ); INSERT INTO units VALUES (3, 'Bag.', 'Bag', 'Bag', 'Bags' ); INSERT INTO units VALUES (4, 'l', 'Liter', 'l', 'Liter' ); INSERT INTO units VALUES (5, 'kg', 'Kilogramm', 'kg', 'Kilogramm' ); INSERT INTO units VALUES (6, 'Pcs.', 'Piece', 'Pcs.', 'Pieces' ); INSERT INTO units VALUES (7, 't', 'Ton', 't', 'Tons' ); INSERT INTO units VALUES (8, 'pausch.', 'pauschal', 'pausch.', 'pauschal' ); INSERT INTO units VALUES (9, 'Hour', 'Hour', 'Hours', 'Hours' ); DELETE FROM stdSaetze; INSERT INTO stdSaetze (name, price, sortKey) VALUES ('Worker', 34.00, 1 ); INSERT INTO stdSaetze (name, price, sortKey) VALUES ('Master', 39.00, 2 ); INSERT INTO stdSaetze (name, price, sortKey) VALUES ('Helper', 30.00, 4 ); INSERT INTO stdSaetze (name, price, sortKey) VALUES ('Trainee', 21.00, 3 ); INSERT INTO stdSaetze (name, price, sortKey) VALUES ('Machine Driver', 33.00, 5 ); DELETE FROM wordLists; INSERT INTO wordLists VALUES ('greeting', 'with kind regards,' ); INSERT INTO wordLists VALUES ('greeting', 'with best regards,' ); INSERT INTO wordLists VALUES ('greeting', 'yours faithfully,' ); INSERT INTO wordLists VALUES ('greeting', 'goodbye and thanks for the fish,' ); INSERT INTO wordLists VALUES ('greeting', 'goodbye' ); INSERT INTO wordLists VALUES ('greeting', 'forever yours,' ); INSERT INTO wordLists VALUES ('salut', 'Dear Mr. %NAME' ); INSERT INTO wordLists VALUES ('salut', 'Dear Mrs. %NAME' ); INSERT INTO wordLists VALUES ('salut', 'Dear Mrs. %NAME, dear Mr. %NAME' ); INSERT INTO wordLists VALUES ('salut', 'Dear %GIVEN_NAME' ); kraft-0.97/database/mysql/migration/000077500000000000000000000000001410616450300174625ustar00rootroot00000000000000kraft-0.97/database/mysql/migration/10_dbmigrate.sql000066400000000000000000000051221410616450300224410ustar00rootroot00000000000000# message allow laternatives and demand positions for offers SELECT @item := docTypeID FROM DocTypes WHERE name="Offer"; INSERT IGNORE INTO attributes (hostObject, hostId, name, value) VALUES ('DocType', @item, 'AllowAlternative', '1'); INSERT IGNORE INTO attributes (hostObject, hostId, name, value) VALUES ('DocType', @item, 'AllowDemand', '1'); SELECT @item := docTypeID FROM DocTypes WHERE name="Angebot"; INSERT IGNORE INTO attributes (hostObject, hostId, name, value) VALUES ('DocType', @item, 'AllowAlternative', '1'); INSERT IGNORE INTO attributes (hostObject, hostId, name, value) VALUES ('DocType', @item, 'AllowDemand', '1'); # message Add a list value identification column to the attribute table ALTER TABLE attributes ADD COLUMN valueIsList tinyint default 0 after value; DROP TABLE IF EXISTS tmp_attrib; CREATE TABLE tmp_attrib ( id INT NOT NULL AUTO_INCREMENT, hostObject VARCHAR(64), hostId INT, name VARCHAR(64), value MEDIUMTEXT, valueIsList TINYINT, PRIMARY KEY(id), UNIQUE INDEX( hostObject, hostId, name ) ); INSERT INTO tmp_attrib (hostObject, hostId, name, value, valueIsList) SELECT hostObject, hostId, name, value, 0 FROM attributes; # message Create an attribute value table CREATE TABLE IF NOT EXISTS attributeValues ( id INT NOT NULL AUTO_INCREMENT, attributeId INT NOT NULL, value VARCHAR(255), PRIMARY KEY( id ), INDEX( attributeId ) ); # message copy the attribute values over to the new attribute value table INSERT INTO attributeValues (attributeId, value) SELECT id, value FROM tmp_attrib WHERE value is not null; # message drop the attrib column ALTER TABLE tmp_attrib DROP COLUMN value; DROP TABLE IF EXISTS attribute_old; RENAME TABLE attributes TO attribute_old, tmp_attrib TO attributes; # message create a table to keep tag templates CREATE TABLE IF NOT EXISTS `tagTemplates` ( `tagTmplID` int(11) NOT NULL auto_increment, `sortkey` int(11) NOT NULL, `name` varchar(255) default NULL, `description` varchar(255) default NULL, `color` char(7) default NULL, PRIMARY KEY (`tagTmplID`), KEY `sortkey` (`sortkey`) ); INSERT IGNORE INTO tagTemplates (sortkey, name, description, color) VALUES (3, 'Discount', 'Marks items to give discount on', '#ff1c1c' ); INSERT IGNORE INTO tagTemplates (sortkey, name, description, color) VALUES (1, 'Material', 'Marks material', '#4e4e4e' ); INSERT IGNORE INTO tagTemplates (sortkey, name, description, color) VALUES (2, 'Work', 'Marks working hour items', '#ffbb39' ); INSERT IGNORE INTO tagTemplates (sortkey, name, description, color) VALUES (4, 'Plants', 'Marks plant items', '#26b913' ); kraft-0.97/database/mysql/migration/11_dbmigrate.sql000066400000000000000000000005461410616450300224470ustar00rootroot00000000000000# message Adding relation table information to attribute table ALTER TABLE attributes ADD COLUMN relationTable varchar(64) default NULL AFTER valueIsList; ALTER TABLE attributes ADD COLUMN relationIDColumn varchar(64) default NULL AFTER relationTable; ALTER TABLE attributes ADD COLUMN relationStringColumn varchar(64) default NULL AFTER relationIDColumn; kraft-0.97/database/mysql/migration/12_dbmigrate.sql000066400000000000000000000006641410616450300224510ustar00rootroot00000000000000CREATE TABLE numberCycles ( id INT NOT NULL AUTO_INCREMENT, name VARCHAR(64) NOT NULL, lastIdentNumber INT NOT NULL DEFAULT 0, identTemplate VARCHAR(64) NOT NULL DEFAULT "%i-%yyyy", PRIMARY KEY( id ), UNIQUE(name) ); SELECT @id := IF( ISNULL(MAX( docID)), 1, MAX(docID) ) FROM document; INSERT INTO numberCycles (name, lastIdentNumber) VALUES ("default", @id); kraft-0.97/database/mysql/migration/13_dbmigrate.sql000066400000000000000000000001501410616450300224400ustar00rootroot00000000000000# message Adding a taxType column ALTER TABLE docposition ADD COLUMN taxType int default 3 AFTER price; kraft-0.97/database/mysql/migration/14_dbmigrate.sql000066400000000000000000000005671410616450300224550ustar00rootroot00000000000000# message Add tax table CREATE TABLE taxes ( id INT NOT NULL AUTO_INCREMENT, fullTax DECIMAL(5,1), reducedTax DECIMAL(5,1), startDate DATE, PRIMARY KEY( id ) ); INSERT INTO taxes ( fullTax, reducedTax, startDate ) VALUES (16.0, 7.0, '1998-04-01' ); INSERT INTO taxes ( fullTax, reducedTax, startDate ) VALUES (19.0, 7.0, '2007-01-01' ); kraft-0.97/database/mysql/migration/15_dbmigrate.sql000066400000000000000000000006201410616450300224440ustar00rootroot00000000000000ALTER TABLE document ADD COLUMN projectLabel VARCHAR(255) AFTER language; ALTER TABLE archdoc ADD COLUMN projectLabel VARCHAR(255) AFTER language; ALTER TABLE archdoc ADD COLUMN tax DECIMAL(5,1) AFTER projectLabel; ALTER TABLE archdoc ADD COLUMN reducedTax DECIMAL(5,1) AFTER tax; ALTER TABLE archdocpos DROP COLUMN vat; ALTER TABLE archdocpos ADD COLUMN taxType INT DEFAULT 0 AFTER overallPrice; kraft-0.97/database/mysql/migration/16_dbmigrate.sql000066400000000000000000000013351410616450300224510ustar00rootroot00000000000000ALTER TABLE CalcMaterials RENAME TO CalcMaterialsOld; CREATE TABLE CalcMaterials ( MCalcID INT NOT NULL AUTO_INCREMENT, TemplID INT NOT NULL, materialID INT NOT NULL, percent INT DEFAULT 0, amount DECIMAL(10,2), modDate TIMESTAMP, PRIMARY KEY(MCalcID) ); INSERT INTO CalcMaterials (TemplID, materialID, amount, percent, modDate) SELECT CalcMaterialsOld.TemplID, CalcMaterialDetails.materialID, CalcMaterialDetails.amount, CalcMaterialsOld.percent, CalcMaterialsOld.modDate FROM CalcMaterialDetails INNER JOIN CalcMaterialsOld ON CalcMaterialDetails.CalcID=CalcMaterialsOld.MCalcID; DROP TABLE IF EXISTS CalcMaterialsOld; DROP TABLE IF EXISTS CalcMaterialDetails; DROP TABLE IF EXISTS attribute_old;kraft-0.97/database/mysql/migration/17_dbmigrate.sql000066400000000000000000000002021410616450300224420ustar00rootroot00000000000000ALTER TABLE CatalogChapters ADD COLUMN parentChapter int(11) default 0; ALTER TABLE CatalogChapters ADD COLUMN description text; kraft-0.97/database/mysql/migration/18_dbmigrate.sql000066400000000000000000000003341410616450300224510ustar00rootroot00000000000000# message Adding sort column and usage statistics ALTER TABLE Catalog ADD COLUMN sortKey INT(11) default 0; ALTER TABLE Catalog ADD COLUMN lastUsed DATETIME; ALTER TABLE Catalog ADD COLUMN useCounter INT(11) default 0; kraft-0.97/database/mysql/migration/19_dbmigrate.sql000066400000000000000000000025301410616450300224520ustar00rootroot00000000000000# message Create new document type delivery note INSERT INTO DocTypes (name) VALUES ('Lieferschein'); SET @lsId := LAST_INSERT_ID(); INSERT INTO attributes (hostObject, hostId, name, valueIsList) VALUES ('DocType', @lsId, 'HidePrices', 1); INSERT INTO DocTypes (name) VALUES ('Delivery Receipt'); SET @nodId := LAST_INSERT_ID(); INSERT INTO attributes (hostObject, hostId, name, valueIsList) VALUES ('DocType', @nodId, 'HidePrices', 1); # message Add more document relation settings # Lieferschein follows Angebot SELECT @item := docTypeID FROM DocTypes WHERE name="Angebot"; # mayfail INSERT INTO DocTypeRelations VALUES( @item, @lsId, 10 ); # Rechnung follows Lieferschein SELECT @follower := docTypeID FROM DocTypes WHERE name="Rechnung"; # mayfail INSERT INTO DocTypeRelations VALUES( @lsId, @follower, 11 ); # Delivery Receipt follows Offer SELECT @item := docTypeID FROM DocTypes WHERE name="Offer"; # mayfail INSERT INTO DocTypeRelations VALUES( @item, @nodId, 12 ); # Invoice follows Delivery Receipt SELECT @follower := docTypeID FROM DocTypes WHERE name="Invoice"; # mayfail INSERT INTO DocTypeRelations VALUES( @nodId, @follower, 13 ); # enhance the clientID col in document because the ids can be larger. ALTER TABLE document CHANGE COLUMN clientID clientID VARCHAR(255); ALTER TABLE archdoc CHANGE COLUMN clientUid clientUid VARCHAR(255); # Done. kraft-0.97/database/mysql/migration/20_dbmigrate.sql000066400000000000000000000004001410616450300224340ustar00rootroot00000000000000ALTER TABLE CalcTime ADD COLUMN timeUnit INT default 0; -- Add a unit id ALTER TABLE DocCalcTime ADD COLUMN timeUnit INT default 0; -- Add a unit id UPDATE CalcTime set timeUnit=0; -- Update existing CalcTime entries. UPDATE DocCalcTime set timeUnit=0; kraft-0.97/database/mysql/migration/21_dbmigrate.sql000066400000000000000000000003431410616450300224430ustar00rootroot00000000000000ALTER TABLE document ADD COLUMN predecessor VARCHAR(32) AFTER projectLabel; ALTER TABLE archdoc ADD COLUMN predecessor VARCHAR(32) AFTER projectLabel; UPDATE document SET predecessor = 0; UPDATE archdoc SET predecessor = 0; kraft-0.97/database/mysql/migration/22_dbmigrate.sql000066400000000000000000000002551410616450300224460ustar00rootroot00000000000000 INSERT INTO taxes (fullTax, reducedTax, startDate) VALUES (16.0, 5.0, '2020-07-01'); INSERT INTO taxes (fullTax, reducedTax, startDate) VALUES (19.0, 7.0, '2021-01-01'); kraft-0.97/database/mysql/migration/2_dbmigrate.sql000066400000000000000000000021551410616450300223650ustar00rootroot00000000000000# message: Creating document position calulation tables ; CREATE TABLE DocCalcTime ( TCalcID INT NOT NULL AUTO_INCREMENT, TemplID INT NOT NULL, name VARCHAR(255), minutes INT default 0, percent INT default 0, stdHourSet INT default 0, allowGlobal INT default 1, modDate TIMESTAMP, PRIMARY KEY( TCalcID), INDEX(TemplID) ); CREATE TABLE DocCalcFixed( FCalcID INT NOT NULL AUTO_INCREMENT, TemplID INT NOT NULL, name VARCHAR(255), amount DECIMAL(10,2) default 1.0, price DECIMAL(10,2), percent INT default 0, modDate TIMESTAMP, PRIMARY KEY(FCalcID), INDEX(TemplID) ); CREATE TABLE DocCalcMaterials( MCalcID INT NOT NULL AUTO_INCREMENT, TemplID INT NOT NULL, name VARCHAR(255), percent INT default 0, modDate TIMESTAMP, PRIMARY KEY(MCalcID), INDEX(TemplID) ); CREATE TABLE DocCalcMaterialDetails( MCalcDetailID INT NOT NULL AUTO_INCREMENT, CalcID INT NOT NULL, materialID INT NOT NULL, amount DECIMAL(10,2), PRIMARY KEY(MCalcDetailID), INDEX(CalcID) ); kraft-0.97/database/mysql/migration/3_dbmigrate.sql000066400000000000000000000003421410616450300223620ustar00rootroot00000000000000CREATE TABLE plantPrices ( matchCode VARCHAR(255), price DECIMAL(8,2), lastUpdate TIMESTAMP, PRIMARY KEY( matchCode ) ); ALTER TABLE document ADD COLUMN docDescription TEXT AFTER docType; kraft-0.97/database/mysql/migration/4_dbmigrate.sql000066400000000000000000000077461410616450300224020ustar00rootroot00000000000000CREATE TABLE DocTexts ( docTextID INT NOT NULL AUTO_INCREMENT, name VARCHAR(64), description TEXT, text TEXT, docType VARCHAR( 64 ), textType VARCHAR( 64 ), modDate TIMESTAMP, PRIMARY KEY( docTextID ), INDEX( docType, textType ) ); INSERT INTO DocTexts ( name, text, docType, textType ) VALUES ( 'Standard', 'Please edit me - Bitte passe mich an!', 'Offer', 'Header Text' ); INSERT INTO DocTexts ( name, text, docType, textType ) VALUES ( 'Standard', 'Please edit me - Bitte passe mich an!', 'Offer', 'Footer Text' ); INSERT INTO DocTexts ( name, text, docType, textType ) VALUES ( 'Standard', 'Please edit me - Bitte passe mich an!', 'Invoice', 'Header Text' ); INSERT INTO DocTexts ( name, text, docType, textType ) VALUES ( 'Standard', 'Please edit me - Bitte passe mich an!', 'Invoice', 'Footer Text' ); INSERT INTO DocTexts ( name, text, docType, textType ) VALUES ( 'Standard', 'Please edit me - Bitte passe mich an!', 'Acceptance of Order', 'Header Text' ); INSERT INTO DocTexts ( name, text, docType, textType ) VALUES ( 'Standard', 'Please edit me - Bitte passe mich an!', 'Acceptance of Order', 'Footer Text' ); INSERT INTO DocTexts ( name, text, docType, textType ) VALUES ( 'Standard', 'Please edit me - Bitte passe mich an!', 'Angebot', 'Kopf Text' ); INSERT INTO DocTexts ( name, text, docType, textType ) VALUES ( 'Standard', 'Please edit me - Bitte passe mich an!', 'Angebot', 'Fuß Text' ); INSERT INTO DocTexts ( name, text, docType, textType ) VALUES ( 'Standard', 'Please edit me - Bitte passe mich an!', 'Rechnung', 'Kopf Text' ); INSERT INTO DocTexts ( name, text, docType, textType ) VALUES ( 'Standard', 'Please edit me - Bitte passe mich an!', 'Rechnung', 'Fuß Text' ); INSERT INTO DocTexts ( name, text, docType, textType ) VALUES ( 'Standard', 'Please edit me - Bitte passe mich an!', 'Auftragsbestätigung', 'Kopf Text' ); INSERT INTO DocTexts ( name, text, docType, textType ) VALUES ( 'Standard', 'Please edit me - Bitte passe mich an!', 'Auftragsbestätigung', 'Fuß Text' ); INSERT INTO DocTexts ( name, text, docType, textType ) SELECT 'Standard', word, 'Offer', 'Footer Text' FROM wordLists WHERE category='docFooter_Offer'; INSERT INTO DocTexts ( name, text, docType, textType ) SELECT 'Standard', word, 'Offer', 'Header Text' FROM wordLists WHERE category='docHeader_Offer'; INSERT INTO DocTexts ( name, text, docType, textType ) SELECT 'Standard', word, 'Invoice', 'Footer Text' FROM wordLists WHERE category='docFooter_Invoice'; INSERT INTO DocTexts ( name, text, docType, textType ) SELECT 'Standard', word, 'Invoice', 'Header Text' FROM wordLists WHERE category='docHeader_Invoice'; INSERT INTO DocTexts ( name, text, docType, textType ) SELECT 'Standard', word, 'Acceptance of Order', 'Footer Text' FROM wordLists WHERE category='docFooter_Acceptance of Order'; INSERT INTO DocTexts ( name, text, docType, textType ) SELECT 'Standard', word, 'Acceptance of Order', 'Header Text' FROM wordLists WHERE category='docHeader_Acceptance of Order'; INSERT INTO DocTexts ( name, text, docType, textType ) SELECT 'Standard', word, 'Angebot', 'Fuß Text' FROM wordLists WHERE category='docFooter_Angebot'; INSERT INTO DocTexts ( name, text, docType, textType ) SELECT 'Standard', word, 'Angebot', 'Kopf Text' FROM wordLists WHERE category='docHeader_Angebot'; INSERT INTO DocTexts ( name, text, docType, textType ) SELECT 'Standard', word, 'Rechnung', 'Fuß Text' FROM wordLists WHERE category='docFooter_Rechnung'; INSERT INTO DocTexts ( name, text, docType, textType ) SELECT 'Standard', word, 'Rechnung', 'Kopf Text' FROM wordLists WHERE category='docHeader_Rechnung'; INSERT INTO DocTexts ( name, text, docType, textType ) SELECT 'Standard', word, 'Auftragsbestätigung', 'Fuß Text' FROM wordLists WHERE category='docFooter_Auftragsbestätigung'; INSERT INTO DocTexts ( name, text, docType, textType ) SELECT 'Standard', word, 'Auftragsbestätigung', 'Kopf Text' FROM wordLists WHERE category='docHeader_Auftragsbestätigung'; kraft-0.97/database/mysql/migration/5_dbmigrate.sql000066400000000000000000000043051410616450300223670ustar00rootroot00000000000000# message Creating attributes table... CREATE TABLE attributes ( hostObject VARCHAR(64), hostId INT NOT NULL, name VARCHAR(64), value MEDIUMTEXT, PRIMARY KEY( hostObject, hostId, name ) ); # message Creating attributes for archived documents CREATE TABLE archPosAttribs ( archPosAttribId INT NOT NULL AUTO_INCREMENT, archDocID INT NOT NULL, name VARCHAR(64), value VARCHAR(64), PRIMARY KEY( archPosAttribId ) ); # message Adding position type and overall price ot archdocpositions ALTER TABLE archdocpos ADD COLUMN kind VARCHAR(64) AFTER ordNumber; ALTER TABLE archdocpos ADD COLUMN overallPrice DECIMAL(10,2) AFTER price; # message Changing old kinds to Normal UPDATE archdocpos SET kind = "Normal"; # message Calculating archive position price UPDATE archdocpos SET overallPrice = ROUND( price * amount, 2); # message Creating Document Type table CREATE TABLE DocTypes ( docTypeID INT NOT NULL AUTO_INCREMENT, name VARCHAR(255), PRIMARY KEY( docTypeID ) ); # message Filling doc type attributes INSERT INTO DocTypes (name) VALUES ( 'Offer' ); SET @dtId := LAST_INSERT_ID(); INSERT INTO attributes VALUES ('DocType', @dtId, 'AllowDemand', 'true'); INSERT INTO attributes VALUES ('DocType', @dtId, 'AllowAlternative', 'true'); INSERT INTO DocTypes (name) VALUES ( 'Acceptance of Order' ); SET @dtId := LAST_INSERT_ID(); INSERT INTO attributes VALUES ('DocType', @dtId, 'AllowDemand', 'true'); INSERT INTO attributes VALUES ('DocType', @dtId, 'AllowAlternative', 'true'); INSERT INTO DocTypes (name) VALUES ( 'Invoice' ); # message Filling doc type attributes INSERT INTO DocTypes (name) VALUES ( 'Angebot' ); SET @dtId := LAST_INSERT_ID(); INSERT INTO attributes VALUES ('DocType', @dtId, 'AllowDemand', 'true'); INSERT INTO attributes VALUES ('DocType', @dtId, 'AllowAlternative', 'true'); INSERT INTO DocTypes (name) VALUES ( 'Auftragsbestätigung' ); SET @dtId := LAST_INSERT_ID(); INSERT INTO attributes VALUES ('DocType', @dtId, 'AllowDemand', 'true'); INSERT INTO attributes VALUES ('DocType', @dtId, 'AllowAlternative', 'true'); INSERT INTO DocTypes (name) VALUES ( 'Rechnung' ); # message Drop an unused table archdocStates DROP TABLE IF EXISTS archdocStates; kraft-0.97/database/mysql/migration/6_dbmigrate.sql000066400000000000000000000004511410616450300223660ustar00rootroot00000000000000# message Localisation information on document level ALTER TABLE document ADD country VARCHAR(32) AFTER posttext; ALTER TABLE document ADD language VARCHAR(32) AFTER country; ALTER TABLE archdoc ADD country VARCHAR(32) AFTER posttext; ALTER TABLE archdoc ADD language VARCHAR(32) AFTER country; kraft-0.97/database/mysql/migration/7_dbmigrate.sql000066400000000000000000000001051410616450300223630ustar00rootroot00000000000000 ALTER TABLE archdoc ADD clientUid VARCHAR(32) AFTER clientAddress; kraft-0.97/database/mysql/migration/8_dbmigrate.sql000066400000000000000000000027711410616450300223770ustar00rootroot00000000000000 CREATE TABLE DocTypeRelations ( typeId INT NOT NULL, followerId INT NOT NULL, sequence INT NOT NULL, PRIMARY KEY( typeId, followerId ) ); # Acceptance of Order follows Offer SELECT @item := docTypeID FROM DocTypes WHERE name="Offer"; SELECT @follower := docTypeID FROM DocTypes WHERE name="Acceptance of Order"; INSERT INTO DocTypeRelations VALUES( @item, @follower, 1 ); # Invoice follorws Offer SELECT @item := docTypeID FROM DocTypes WHERE name="Offer"; SELECT @follower := docTypeID FROM DocTypes WHERE name="Invoice"; INSERT INTO DocTypeRelations VALUES( @item, @follower, 2 ); # Invoice follows Acceptance of Order SELECT @item := docTypeID FROM DocTypes WHERE name="Acceptance of Order"; SELECT @follower := docTypeID FROM DocTypes WHERE name="Invoice"; INSERT INTO DocTypeRelations VALUES( @item, @follower, 3 ); # Acceptance of Order follows Offer SELECT @item := docTypeID FROM DocTypes WHERE name="Angebot"; SELECT @follower := docTypeID FROM DocTypes WHERE name="Auftragsbestätigung"; INSERT INTO DocTypeRelations VALUES( @item, @follower, 4 ); # Invoice follorws Offer SELECT @item := docTypeID FROM DocTypes WHERE name="Angebot"; SELECT @follower := docTypeID FROM DocTypes WHERE name="Rechnung"; INSERT INTO DocTypeRelations VALUES( @item, @follower, 5 ); # Invoice follows Acceptance of Order SELECT @item := docTypeID FROM DocTypes WHERE name like "Auftragsbest%"; SELECT @follower := docTypeID FROM DocTypes WHERE name="Rechnung"; INSERT INTO DocTypeRelations VALUES( @item, @follower, 6 ); kraft-0.97/database/mysql/migration/9_dbmigrate.sql000066400000000000000000000007531410616450300223760ustar00rootroot00000000000000# message add a document type id to text table alter table DocTexts add column docTypeId int after docType; # message populate the doc type id column in docTexts update DocTexts set docTypeId=( SELECT docTypeID FROM DocTypes WHERE name=docType ); # message create a type column for the docposition alter table docposition add column postype VARCHAR(64) AFTER text; # message create type column for the archdocpos table alter table archdocpos add column postype VARCHAR(64) AFTER kind; kraft-0.97/database/mysql/migration/CMakeLists.txt000066400000000000000000000002711410616450300222220ustar00rootroot00000000000000########### install files ############### file(GLOB mig_scripts *_dbmigrate.sql) install(FILES README ${mig_scripts} DESTINATION ${DATA_INSTALL_DIR}/kraft/dbmigrate/mysql) kraft-0.97/database/mysql/migration/README000066400000000000000000000014721410616450300203460ustar00rootroot00000000000000Kraft Database Migration ======================== The Kraft database schema might change over the time. To achieve that smoothly for the users, here is a database migration system. Every version of Kraft has a hardcoded version of the required database schema version. In the Kraft database there is a system table that carries the version of the current running database schema. In case the current database version is lower than the required, Kraft looks in this migration directory if there is a script starting with the number "current version +1". If found, Kraft executes the sql commands contained in the file. After all are finished, the version in the database system table is updated. Note that the migration file my contain lines like # message: bla bla Kraft shows the message lines in the status line. kraft-0.97/database/sqlite3/000077500000000000000000000000001410616450300157105ustar00rootroot00000000000000kraft-0.97/database/sqlite3/CMakeLists.txt000066400000000000000000000003051410616450300204460ustar00rootroot00000000000000add_subdirectory(migration) ########### install files ############### install(FILES create_schema.sql fill_schema_de.sql fill_schema_en.sql DESTINATION ${DATA_INSTALL_DIR}/kraft/dbinit/sqlite3) kraft-0.97/database/sqlite3/create_schema.sql000066400000000000000000000160211410616450300212140ustar00rootroot00000000000000 CREATE TABLE preisArten ( preisArtID INTEGER PRIMARY KEY ASC autoincrement, preisArt VARCHAR(64) NOT NULL ); CREATE TABLE wordLists( category VARCHAR(64), word VARCHAR(255), PRIMARY KEY( category, word ) ); CREATE TABLE CatalogSet( catalogSetID INTEGER PRIMARY KEY ASC autoincrement, name VARCHAR(255), description VARCHAR(255), catalogType VARCHAR(64), sortKey INT NOT NULL ); CREATE TABLE CatalogChapters( chapterID INTEGER PRIMARY KEY ASC autoincrement, catalogSetID INT NOT NULL, chapter VARCHAR(255), sortKey INT NOT NULL ); CREATE INDEX chapterIndx ON CatalogChapters( chapter ); CREATE TABLE Catalog ( TemplID INTEGER PRIMARY KEY ASC autoincrement, chapterID INT NOT NULL default 1, unitID INT NOT NULL, Floskel TEXT, Gewinn DECIMAL(6,2) default 0, zeitbeitrag TINYINT default 1, enterDatum DATETIME, modifyDatum TIMESTAMP(14), Preisart INT NOT NULL default 1, EPreis DECIMAL(10,2) default 0 ); CREATE INDEX chapterIdIndx ON Catalog( chapterID ); CREATE TRIGGER insert_catalog_timeEnter AFTER INSERT ON Catalog BEGIN UPDATE Catalog SET enterDatum = DATETIME('NOW') WHERE TemplID = new.TemplID; END; CREATE TRIGGER update_catalog_timeEnter AFTER UPDATE ON Catalog BEGIN UPDATE Catalog SET modifyDatum = DATETIME('NOW') WHERE TemplID = new.TemplID; END; UPDATE Catalog SET modifyDatum=enterDatum; CREATE TABLE CalcTime ( TCalcID INTEGER PRIMARY KEY ASC autoincrement, TemplID INT NOT NULL, name VARCHAR(255), minutes INT default 0, percent INT default 0, stdHourSet INT default 0, allowGlobal INT default 1, modDate TIMESTAMP(14) ); CREATE INDEX calcTimeIndx ON CalcTime( TemplID ); CREATE TRIGGER update_calcTime_modifyDate AFTER UPDATE ON CalcTime BEGIN UPDATE CalcTime SET modDate = DATETIME('NOW') WHERE TCalcID = new.TCalcID; END; CREATE TABLE CalcFixed( FCalcID INTEGER PRIMARY KEY ASC autoincrement, TemplID INT NOT NULL, name VARCHAR(255), amount DECIMAL(10,2) default 1.0, price DECIMAL(10,2), percent INT default 0, modDate TIMESTAMP(14) ); CREATE INDEX calcFixedIndx ON CalcFixed( TemplID ); CREATE TRIGGER update_calcFixed_modifyDate AFTER UPDATE ON CalcFixed BEGIN UPDATE CalcFixed SET modDate = DATETIME('NOW') WHERE FCalcID = new.FCalcID; END; CREATE TABLE CalcMaterials( MCalcID INTEGER PRIMARY KEY ASC autoincrement, TemplID INT NOT NULL, name VARCHAR(255), percent INT default 0, modDate TIMESTAMP(14) ); CREATE INDEX calcMatIndx ON CalcMaterials( TemplID ); CREATE TRIGGER update_calcMaterials_modifyDate AFTER UPDATE ON CalcMaterials BEGIN UPDATE CalcMaterials SET modDate = DATETIME('NOW') WHERE MCalcID = new.MCalcID; END; CREATE TABLE CalcMaterialDetails( MCalcDetailID INTEGER PRIMARY KEY ASC autoincrement, CalcID INT NOT NULL, materialID INT NOT NULL, amount DECIMAL(10,2) ); CREATE INDEX calcIdIndx ON CalcMaterialDetails( CalcID ); CREATE TABLE units( unitID INTEGER PRIMARY KEY ASC autoincrement, unitShort VARCHAR(255), unitLong VARCHAR(255), unitPluShort VARCHAR(255), unitPluLong VARCHAR(255) ); CREATE INDEX unitShortIndx ON units( unitShort ); CREATE TABLE stockMaterial ( matID INTEGER PRIMARY KEY ASC autoincrement, chapterID INT NOT NULL default 1, material mediumtext, unitID INT NOT NULL, perPack DECIMAL(10,2), priceIn DECIMAL(10,2), priceOut DECIMAL(10,2), enterDate DATETIME, modifyDate TIMESTAMP(14) ); CREATE INDEX matChapterIndx ON stockMaterial( chapterID ); CREATE TRIGGER insert_material_enterDate AFTER INSERT ON stockMaterial BEGIN UPDATE stockMaterial SET enterDate = DATETIME('NOW') WHERE matID = new.matID; END; CREATE TRIGGER update_material_modifyDate AFTER UPDATE ON stockMaterial BEGIN UPDATE stockMaterial SET modifyDate = DATETIME('NOW') WHERE matID = new.matID; END; CREATE TABLE stdSaetze( stdSaetzeID INTEGER PRIMARY KEY ASC autoincrement, name VARCHAR(255), price DECIMAL(10,2), sortKey int ); CREATE TABLE document( docID INTEGER PRIMARY KEY ASC autoincrement, ident VARCHAR(32), docType VARCHAR(255), docDescription TEXT, clientID VARCHAR(32), clientAddress TEXT, salut VARCHAR(255), goodbye VARCHAR(128), lastModified TIMESTAMP, date DATE, pretext TEXT, posttext TEXT, country VARCHAR(32), language VARCHAR(32), projectLabel VARCHAR(255) ); CREATE INDEX identIndx ON document( ident ); CREATE INDEX clientIndx ON document( clientID ); CREATE TRIGGER update_document AFTER UPDATE ON document BEGIN UPDATE document SET lastModified = DATETIME('NOW') WHERE docID = new.docID; END; CREATE TABLE docposition( positionID INTEGER PRIMARY KEY ASC autoincrement, docID INT NOT NULL, ordNumber INT NOT NULL, text TEXT, postype VARCHAR(64), amount DECIMAL(10,2), unit INT, price DECIMAL(10,2), taxType INT default 3 ); CREATE INDEX docIdIndx ON docposition( docID ); CREATE UNIQUE INDEX ordIndx ON docposition( docID, ordNumber ); CREATE TABLE archdocStates( stateID INTEGER PRIMARY KEY ASC autoincrement, state VARCHAR(32) ); CREATE TABLE archdoc( archDocID INTEGER PRIMARY KEY ASC autoincrement, ident VARCHAR(32), docType VARCHAR(255), docDescription TEXT, clientAddress TEXT, clientUid VARCHAR(32), salut VARCHAR(255), goodbye VARCHAR(128), printDate TIMESTAMP, date DATE, pretext TEXT, posttext TEXT, country VARCHAR(32), language VARCHAR(32), projectLabel VARCHAR(255), tax DECIMAL(5,1), reducedTax DECIMAL(5,1), state int ); CREATE INDEX archIdentIndx ON archdoc( ident ); CREATE TRIGGER update_archdoc AFTER UPDATE ON archdoc BEGIN UPDATE archDoc SET printDate = DATETIME('NOW') WHERE archDocID = new.archDocID; END; CREATE TABLE archdocpos( archPosID INTEGER PRIMARY KEY ASC autoincrement, archDocID INT NOT NULL, ordNumber INT NOT NULL, kind VARCHAR(64), postype VARCHAR(64), text TEXT, amount DECIMAL(10,2), unit VARCHAR(64), price DECIMAL(10,2), overallPrice DECIMAL(10,2), taxType INT default 0 ); CREATE INDEX archDocIdIndx ON archdocpos( archDocID ); CREATE UNIQUE INDEX archOrdIndx ON archdocpos( archDocID, ordNumber ); CREATE TABLE kraftsystem( dbschemaversion INT NOT NULL, updateUser VARCHAR(256) ); INSERT INTO kraftsystem ( dbschemaversion ) VALUES ( 1 ); kraft-0.97/database/sqlite3/fill_schema_de.sql000066400000000000000000000106531410616450300213540ustar00rootroot00000000000000DELETE FROM preisArten; INSERT INTO preisArten (preisArt) VALUES ('offen'); INSERT INTO preisArten (preisArt) VALUES ('selbsterstellt'); INSERT INTO preisArten (preisArt) VALUES ('kalkuliert'); DELETE FROM CatalogSet; INSERT INTO CatalogSet (name, description, catalogType, sortKey) VALUES ( "Standard Mustertexte", "Kalkulierte Musterposten", "TemplCatalog", 1 ); DELETE FROM CatalogChapters; INSERT INTO CatalogChapters (chapter, sortKey, catalogSetID) VALUES ('Arbeit', 1, (SELECT catalogSetID FROM CatalogSet WHERE name="Standard Mustertexte")); INSERT INTO CatalogChapters (chapter, sortKey, catalogSetID) VALUES ('Maschine', 2, (SELECT catalogSetID FROM CatalogSet WHERE name="Standard Mustertexte") ); INSERT INTO CatalogChapters (chapter, sortKey, catalogSetID) VALUES ('Materialeinsatz', 3, (SELECT catalogSetID FROM CatalogSet WHERE name="Standard Mustertexte") ); INSERT INTO CatalogChapters (chapter, sortKey, catalogSetID) VALUES ('Service', 4, (SELECT catalogSetID FROM CatalogSet WHERE name="Standard Mustertexte") ); INSERT INTO CatalogChapters (chapter, sortKey, catalogSetID) VALUES ('Sonstige', 5, (SELECT catalogSetID FROM CatalogSet WHERE name="Standard Mustertexte") ); INSERT INTO CatalogChapters (chapter, sortKey, catalogSetID) VALUES ('Transport', 6, (SELECT catalogSetID FROM CatalogSet WHERE name="Standard Mustertexte") ); INSERT INTO CatalogSet( name, description, catalogType, sortKey) VALUES ("Material", "Materialkatalog", "MaterialCatalog", 2 ); INSERT INTO CatalogChapters (chapter, sortKey, catalogSetID) VALUES ('Schüttgüter', 3, (SELECT catalogSetID FROM CatalogSet WHERE name="Material")); INSERT INTO CatalogChapters (chapter, sortKey, catalogSetID) VALUES ('Naturstein', 2, (SELECT catalogSetID FROM CatalogSet WHERE name="Material")); INSERT INTO CatalogChapters (chapter, sortKey, catalogSetID) VALUES ('Beton', 1, (SELECT catalogSetID FROM CatalogSet WHERE name="Material")); INSERT INTO CatalogChapters (chapter, sortKey, catalogSetID) VALUES ('Rohre', 4, (SELECT catalogSetID FROM CatalogSet WHERE name="Material")); DELETE FROM units; INSERT INTO units (unitShort, unitLong, unitPluShort, unitPluLong) VALUES ('m', 'Meter', 'm', 'Meter' ); INSERT INTO units (unitShort, unitLong, unitPluShort, unitPluLong) VALUES ('qm', 'Quadratmeter', 'qm', 'Quadratmeter' ); INSERT INTO units (unitShort, unitLong, unitPluShort, unitPluLong) VALUES ('cbm', 'Kubikmeter', 'cbm', 'Kubikmeter' ); INSERT INTO units (unitShort, unitLong, unitPluShort, unitPluLong) VALUES ('Sck.', 'Sack', 'Sck.', 'Säcke' ); INSERT INTO units (unitShort, unitLong, unitPluShort, unitPluLong) VALUES ( 'l', 'Liter', 'l', 'Liter' ); INSERT INTO units (unitShort, unitLong, unitPluShort, unitPluLong) VALUES ('kg', 'Kilogramm', 'kg', 'Kilogramm' ); INSERT INTO units (unitShort, unitLong, unitPluShort, unitPluLong) VALUES ('Stck.', 'Stueck', 'Stck.', 'Stück' ); INSERT INTO units (unitShort, unitLong, unitPluShort, unitPluLong) VALUES ('t', 'Tonne', 't', 'Tonnen' ); INSERT INTO units (unitShort, unitLong, unitPluShort, unitPluLong) VALUES ('pausch.', 'pauschal', 'pausch.', 'pauschal' ); INSERT INTO units (unitShort, unitLong, unitPluShort, unitPluLong) VALUES ('Std.', 'Stunde', 'Std.', 'Stunden' ); DELETE FROM stdSaetze; INSERT INTO stdSaetze (name, price, sortKey) VALUES ('Geselle', 34.00, 1 ); INSERT INTO stdSaetze (name, price, sortKey) VALUES ('Meister', 39.00, 2 ); INSERT INTO stdSaetze (name, price, sortKey) VALUES ('Helfer', 30.00, 4 ); INSERT INTO stdSaetze (name, price, sortKey) VALUES ('Auszubildender', 21.00, 3 ); INSERT INTO stdSaetze (name, price, sortKey) VALUES ('Maschinenführer', 33.00, 5 ); DELETE FROM wordLists; INSERT INTO wordLists VALUES ('greeting', 'mit den besten Grüssen,' ); INSERT INTO wordLists VALUES ('greeting', 'liebe Grüsse,' ); INSERT INTO wordLists VALUES ('greeting', 'Hochachtungsvoll,' ); INSERT INTO wordLists VALUES ('greeting', 'mit freundlichem Gruß,' ); INSERT INTO wordLists VALUES ('salut', 'Sehr geehrter Herr %NAME,' ); INSERT INTO wordLists VALUES ('salut', 'Sehr geehrte Frau %NAME,' ); INSERT INTO wordLists VALUES ('salut', 'Sehr geehrte Frau %NAME, sehr geehrter Herr %NAME,' ); INSERT INTO wordLists VALUES ('salut', 'Lieber %GIVEN_NAME,' ); INSERT INTO wordLists VALUES ('salut', 'Liebe %GIVEN_NAME,' ); kraft-0.97/database/sqlite3/fill_schema_en.sql000066400000000000000000000102371410616450300213640ustar00rootroot00000000000000DELETE FROM preisArten; INSERT INTO preisArten VALUES (0, 'open'); INSERT INTO preisArten VALUES (1, 'manual'); INSERT INTO preisArten VALUES (2, 'calculated'); DELETE FROM CatalogSet; INSERT INTO CatalogSet (name, description, catalogType, sortKey) VALUES ( "Standard Templates", "A set of templates suitable for business", "TemplCatalog", 1 ); -- (SELECT catalogSetID FROM CatalogSet WHERE name="Standard Templates") DELETE FROM CatalogChapters; INSERT INTO CatalogChapters (chapter, sortKey, catalogSetID) VALUES ('Work', 1, (SELECT catalogSetID FROM CatalogSet WHERE name="Standard Templates")); INSERT INTO CatalogChapters (chapter, sortKey, catalogSetID) VALUES ('Machine', 2, (SELECT catalogSetID FROM CatalogSet WHERE name="Standard Templates")); INSERT INTO CatalogChapters (chapter, sortKey, catalogSetID) VALUES ('Material', 3, (SELECT catalogSetID FROM CatalogSet WHERE name="Standard Templates")); INSERT INTO CatalogChapters (chapter, sortKey, catalogSetID) VALUES ('Service', 4, (SELECT catalogSetID FROM CatalogSet WHERE name="Standard Templates")); INSERT INTO CatalogChapters (chapter, sortKey, catalogSetID) VALUES ('Transportation', 5, (SELECT catalogSetID FROM CatalogSet WHERE name="Standard Templates")); INSERT INTO CatalogChapters (chapter, sortKey, catalogSetID) VALUES ('Misc', 6, (SELECT catalogSetID FROM CatalogSet WHERE name="Standard Templates")); INSERT INTO CatalogSet( name, description, catalogType, sortKey) VALUES ("Material", "Material Catalog to Use in Calculations in Templates", "MaterialCatalog", 2 ); -- (SELECT catalogSetID FROM CatalogSet WHERE name="Material") INSERT INTO CatalogChapters ( chapter, sortKey, catalogSetID ) VALUES ('Bulk Solids', 3, (SELECT catalogSetID FROM CatalogSet WHERE name="Material") ); INSERT INTO CatalogChapters ( chapter, sortKey, catalogSetID ) VALUES ('Stones', 2, (SELECT catalogSetID FROM CatalogSet WHERE name="Material")); INSERT INTO CatalogChapters ( chapter, sortKey, catalogSetID ) VALUES ('Concrete', 1, (SELECT catalogSetID FROM CatalogSet WHERE name="Material")); INSERT INTO CatalogChapters ( chapter, sortKey, catalogSetID ) VALUES ('Pipes', 4, (SELECT catalogSetID FROM CatalogSet WHERE name="Material")); INSERT INTO CatalogChapters ( chapter, sortKey, catalogSetID ) VALUES ('Wood', 5, (SELECT catalogSetID FROM CatalogSet WHERE name="Material")); INSERT INTO CatalogChapters ( chapter, sortKey, catalogSetID ) VALUES ('Art and Furnitures', 6, (SELECT catalogSetID FROM CatalogSet WHERE name="Material")); DELETE FROM units; INSERT INTO units VALUES (0, 'm', 'Meter', 'm', 'Meter' ); INSERT INTO units VALUES (1, 'sm', 'Squaremeter', 'qm', 'Squaremeter' ); INSERT INTO units VALUES (2, 'cbm', 'Cubikmeter', 'cbm', 'Cubikmeter' ); INSERT INTO units VALUES (3, 'Bag.', 'Bag', 'Bag', 'Bags' ); INSERT INTO units VALUES (4, 'l', 'Liter', 'l', 'Liter' ); INSERT INTO units VALUES (5, 'kg', 'Kilogramm', 'kg', 'Kilogramm' ); INSERT INTO units VALUES (6, 'Pcs.', 'Piece', 'Pcs.', 'Pieces' ); INSERT INTO units VALUES (7, 't', 'Ton', 't', 'Tons' ); INSERT INTO units VALUES (8, 'pausch.', 'pauschal', 'pausch.', 'pauschal' ); INSERT INTO units VALUES (9, 'Hour', 'Hour', 'Hours', 'Hours' ); DELETE FROM stdSaetze; INSERT INTO stdSaetze (name, price, sortKey) VALUES ('Worker', 34.00, 1 ); INSERT INTO stdSaetze (name, price, sortKey) VALUES ('Master', 39.00, 2 ); INSERT INTO stdSaetze (name, price, sortKey) VALUES ('Assistant', 30.00, 4 ); INSERT INTO stdSaetze (name, price, sortKey) VALUES ('Trainee', 21.00, 3 ); INSERT INTO stdSaetze (name, price, sortKey) VALUES ('Machine Operator', 33.00, 5 ); DELETE FROM wordLists; INSERT INTO wordLists VALUES ('greeting', 'with kind regards,' ); INSERT INTO wordLists VALUES ('greeting', 'with best regards,' ); INSERT INTO wordLists VALUES ('greeting', 'yours faithfully,' ); INSERT INTO wordLists VALUES ('greeting', 'goodbye and thanks for the fish,' ); INSERT INTO wordLists VALUES ('greeting', 'goodbye' ); INSERT INTO wordLists VALUES ('salut', 'Dear Mr. %NAME' ); INSERT INTO wordLists VALUES ('salut', 'Dear Mrs. %NAME' ); INSERT INTO wordLists VALUES ('salut', 'Dear Mrs. %NAME, dear Mr. %NAME' ); INSERT INTO wordLists VALUES ('salut', 'Dear %GIVEN_NAME' ); kraft-0.97/database/sqlite3/migration/000077500000000000000000000000001410616450300177015ustar00rootroot00000000000000kraft-0.97/database/sqlite3/migration/10_dbmigrate.sql000066400000000000000000000050431410616450300226620ustar00rootroot00000000000000-- 5_dbmigrate.sql -- message Add a list value identification column to the attribute table --ALTER TABLE attributes ADD COLUMN valueIsList tinyint default 0; -- AFTER value; DROP TABLE IF EXISTS tmp_attrib; CREATE TABLE tmp_attrib ( id INTEGER PRIMARY KEY ASC autoincrement, hostObject VARCHAR(64), hostId INT, name VARCHAR(64), value MEDIUMTEXT, valueIsList TINYINT ); -- CREATE UNIQUE INDEX tmpIndx_10 ON tmp_attrib( hostObject, hostId, name ); INSERT INTO tmp_attrib (hostObject, hostId, name, value, valueIsList) SELECT hostObject, hostId, name, value, 0 FROM attributes; -- message Create an attribute value table CREATE TABLE IF NOT EXISTS attributeValues ( id INTEGER PRIMARY KEY ASC autoincrement, attributeId INT NOT NULL, value VARCHAR(255) ); CREATE INDEX attribValueIndx_10 ON attributeValues( attributeId ); -- message copy the attribute values over to the new attribute value table INSERT INTO attributeValues (attributeId, value) SELECT id, value FROM tmp_attrib WHERE value is not null; -- message drop the attrib column -- ALTER TABLE tmp_attrib DROP COLUMN value; -- DROP TABLE attributes; ALTER TABLE attributes RENAME TO attributes_unused; CREATE TABLE attributes ( id INTEGER PRIMARY KEY ASC autoincrement, hostObject VARCHAR(64), hostId INT, name VARCHAR(64), valueIsList TINYINT, relationTable varchar(64) default NULL, relationIDColumn varchar(64) default NULL, relationStringColumn varchar(64) default NULL ); CREATE UNIQUE INDEX attribIndx_10 ON attributes( hostObject, hostId, name ); INSERT INTO attributes (hostObject, hostId, name, valueIsList) SELECT hostObject, hostId, name, valueIsList FROM tmp_attrib; -- message create a table to keep tag templates CREATE TABLE IF NOT EXISTS tagTemplates ( tagTmplID INTEGER PRIMARY KEY ASC autoincrement, sortkey int NOT NULL, name varchar(255) default NULL, description varchar(255) default NULL, color char(7) default NULL ); INSERT INTO tagTemplates (sortkey, name, description, color) VALUES (3, 'Discount', 'Marks items to give discount on', '#ff1c1c' ); INSERT INTO tagTemplates (sortkey, name, description, color) VALUES (1, 'Material', 'Marks material', '#4e4e4e' ); INSERT INTO tagTemplates (sortkey, name, description, color) VALUES (2, 'Work', 'Marks working hour items', '#ffbb39' ); INSERT INTO tagTemplates (sortkey, name, description, color) VALUES (4, 'Plants', 'Marks plant items', '#26b913' ); DROP TABLE IF EXISTS tmp_attrib; DROP TABLE IF EXISTS attributes_unused; kraft-0.97/database/sqlite3/migration/11_dbmigrate.sql000066400000000000000000000006671410616450300226720ustar00rootroot00000000000000-- Columns already added in 5_dbmigrate.sql *sqlite workaround* -- message Adding relation table information to attribute table --ALTER TABLE attributes ADD COLUMN relationTable varchar(64) default NULL -- AFTER valueIsList; --ALTER TABLE attributes ADD COLUMN relationIDColumn varchar(64) default NULL; -- AFTER relationTable; --ALTER TABLE attributes ADD COLUMN relationStringColumn varchar(64) default NULL; -- AFTER relationIDColumn;kraft-0.97/database/sqlite3/migration/12_dbmigrate.sql000066400000000000000000000007011410616450300226600ustar00rootroot00000000000000CREATE TABLE numberCycles ( id INTEGER PRIMARY KEY ASC autoincrement, name VARCHAR(64) NOT NULL, lastIdentNumber INT NOT NULL, identTemplate VARCHAR(64) NOT NULL ); CREATE UNIQUE INDEX numCycleIdx_12 ON numberCycles( name ); INSERT INTO numberCycles (name, lastIdentNumber, identTemplate) VALUES ("default", (SELECT ifnull( 1+MAX(docID), 1 ) FROM document), '%i-%yyyy' ); kraft-0.97/database/sqlite3/migration/13_dbmigrate.sql000066400000000000000000000002571410616450300226670ustar00rootroot00000000000000--Column already added in create_schema.sql *Sqlite workaround* -- message Adding a taxType column --ALTER TABLE docposition ADD COLUMN taxType int default 3; -- AFTER price; kraft-0.97/database/sqlite3/migration/14_dbmigrate.sql000066400000000000000000000005531410616450300226670ustar00rootroot00000000000000-- message Add tax table CREATE TABLE taxes ( id INTEGER PRIMARY KEY ASC autoincrement, fullTax DECIMAL(5,1), reducedTax DECIMAL(5,1), startDate DATE ); INSERT INTO taxes ( fullTax, reducedTax, startDate ) VALUES (16.0, 7.0, '1998-04-01' ); INSERT INTO taxes ( fullTax, reducedTax, startDate ) VALUES (19.0, 7.0, '2007-01-01' ); kraft-0.97/database/sqlite3/migration/15_dbmigrate.sql000066400000000000000000000007451410616450300226730ustar00rootroot00000000000000-- message Add project label and tax to archive --ALTER TABLE document ADD COLUMN projectLabel VARCHAR(255); -- AFTER language; --ALTER TABLE archdoc ADD COLUMN projectLabel VARCHAR(255); -- AFTER language; --ALTER TABLE archdoc ADD COLUMN tax DECIMAL(5,1); -- AFTER projectLabel; --ALTER TABLE archdoc ADD COLUMN reducedTax DECIMAL(5,1); -- AFTER tax; -- ALTER TABLE archdocpos DROP COLUMN vat; --ALTER TABLE archdocpos ADD COLUMN taxType INT DEFAULT 0; -- AFTER overallPrice; kraft-0.97/database/sqlite3/migration/16_dbmigrate.sql000066400000000000000000000012561410616450300226720ustar00rootroot00000000000000ALTER TABLE CalcMaterials RENAME TO CalcMaterialsOld; CREATE TABLE CalcMaterials ( MCalcID INTEGER PRIMARY KEY ASC autoincrement, TemplID INT NOT NULL, materialID INT NOT NULL, percent INT DEFAULT 0, amount DECIMAL(10,2), modDate TIMESTAMP(14) ); INSERT INTO CalcMaterials (TemplID, materialID, amount, percent, modDate) SELECT CalcMaterialsOld.TemplID, CalcMaterialDetails.materialID, CalcMaterialDetails.amount, CalcMaterialsOld.percent, CalcMaterialsOld.modDate FROM CalcMaterialDetails INNER JOIN CalcMaterialsOld ON CalcMaterialDetails.CalcID=CalcMaterialsOld.MCalcID; DROP TABLE IF EXISTS CalcMaterialsOld; DROP TABLE IF EXISTS CalcMaterialDetails;kraft-0.97/database/sqlite3/migration/17_dbmigrate.sql000066400000000000000000000002011410616450300226600ustar00rootroot00000000000000ALTER TABLE CatalogChapters ADD COLUMN parentChapter int(11) default 0; ALTER TABLE CatalogChapters ADD COLUMN description text; kraft-0.97/database/sqlite3/migration/18_dbmigrate.sql000066400000000000000000000002431410616450300226670ustar00rootroot00000000000000 ALTER TABLE Catalog ADD COLUMN sortKey INT default 0; ALTER TABLE Catalog ADD COLUMN lastUsed DATETIME; ALTER TABLE Catalog ADD COLUMN useCounter INT default 0; kraft-0.97/database/sqlite3/migration/19_dbmigrate.sql000066400000000000000000000024661410616450300227010ustar00rootroot00000000000000 # message Create new document type delivery note INSERT INTO DocTypes (name) VALUES ('Lieferschein'); INSERT INTO attributes (hostObject, hostId, name, valueIsList) VALUES ('DocType', (SELECT docTypeID FROM DocTypes WHERE name='Lieferschein'), 'HidePrices', 'true'); INSERT INTO DocTypes (name) VALUES ('Delivery Receipt'); INSERT INTO attributes (hostObject, hostId, name, valueIsList) VALUES ('DocType', (SELECT docTypeID FROM DocTypes WHERE name='Delivery Receipt'), 'HidePrices', 'true'); # Delivery Receipt follows Offer # mayfail # message Add more document relation settings INSERT INTO DocTypeRelations VALUES( (SELECT docTypeID FROM DocTypes WHERE name="Offer"), (SELECT docTypeID FROM DocTypes WHERE name="Delivery Receipt"), 12 ); # mayfail INSERT INTO DocTypeRelations VALUES( (SELECT docTypeID FROM DocTypes WHERE name="Delivery Receipt"), (SELECT docTypeID FROM DocTypes WHERE name="Invoice"), 13 ); # Lieferschein follows Angebot # mayfail INSERT INTO DocTypeRelations VALUES( (SELECT docTypeID FROM DocTypes WHERE name="Angebot"), (SELECT docTypeID FROM DocTypes WHERE name="Lieferschein"), 10 ); # Rechnung follows Lieferschein # mayfail INSERT INTO DocTypeRelations VALUES( (SELECT docTypeID FROM DocTypes WHERE name="Lieferschein"), (SELECT docTypeID FROM DocTypes WHERE name="Rechnung"), 11 ); # Done. kraft-0.97/database/sqlite3/migration/20_dbmigrate.sql000066400000000000000000000004021410616450300226550ustar00rootroot00000000000000 ALTER TABLE CalcTime ADD COLUMN timeUnit INT default 0; -- Add a unit id ALTER TABLE DocCalcTime ADD COLUMN timeUnit INT default 0; -- Add a unit id UPDATE CalcTime set timeUnit=0; -- Update existing CalcTime entries. UPDATE DocCalcTime set timeUnit=0; kraft-0.97/database/sqlite3/migration/21_dbmigrate.sql000066400000000000000000000002471410616450300226650ustar00rootroot00000000000000--- Add a column predecessor to the document table ALTER TABLE document ADD COLUMN predecessor VARCHAR(32); ALTER TABLE archdoc ADD COLUMN predecessor VARCHAR(32); kraft-0.97/database/sqlite3/migration/22_dbmigrate.sql000066400000000000000000000002551410616450300226650ustar00rootroot00000000000000 INSERT INTO taxes (fullTax, reducedTax, startDate) VALUES (16.0, 5.0, '2020-07-01'); INSERT INTO taxes (fullTax, reducedTax, startDate) VALUES (19.0, 7.0, '2021-01-01'); kraft-0.97/database/sqlite3/migration/2_dbmigrate.sql000066400000000000000000000034041410616450300226020ustar00rootroot00000000000000-- message: Creating document position calulation tables ; CREATE TABLE DocCalcTime ( TCalcID INTEGER PRIMARY KEY ASC autoincrement, TemplID INT NOT NULL, name VARCHAR(255), minutes INT default 0, percent INT default 0, stdHourSet INT default 0, allowGlobal INT default 1, modDate TIMESTAMP(14) ); CREATE INDEX calcTimeTemplIndx_2 ON DocCalcTime( TemplID ); CREATE TRIGGER update_docCalcTime_modDate AFTER UPDATE ON DocCalcTime BEGIN UPDATE DocCalcTime SET modDate = DATETIME('NOW') WHERE TCalcID = new.TCalcID; END; CREATE TABLE DocCalcFixed( FCalcID INTEGER PRIMARY KEY ASC autoincrement, TemplID INT NOT NULL, name VARCHAR(255), amount DECIMAL(10,2) default 1.0, price DECIMAL(10,2), percent INT default 0, modDate TIMESTAMP(14) ); CREATE INDEX CalcFixedTemplIndx_2 ON DocCalcFixed( TemplID ); CREATE TRIGGER update_docCalcFixed_modDate AFTER UPDATE ON DocCalcFixed BEGIN UPDATE DocCalcFixed SET modDate = DATETIME('NOW') WHERE FCalcID = new.FCalcID; END; CREATE TABLE DocCalcMaterials( MCalcID INTEGER PRIMARY KEY ASC autoincrement, TemplID INT NOT NULL, name VARCHAR(255), percent INT default 0, modDate TIMESTAMP(14) ); CREATE INDEX CalcMaterialTemplIndx_2 ON DocCalcMaterials( TemplID ); CREATE TRIGGER update_docCalcMaterials_modDate AFTER UPDATE ON DocCalcMaterials BEGIN UPDATE DocCalcMaterials SET modDate = DATETIME('NOW') WHERE MCalcID = new.MCalcID; END; CREATE TABLE DocCalcMaterialDetails( MCalcDetailID INTEGER PRIMARY KEY ASC autoincrement, CalcID INT NOT NULL, materialID INT NOT NULL, amount DECIMAL(10,2) ); CREATE INDEX CalcMaterialDetailsCalcIDIndx_2 ON DocCalcMaterialDetails( CalcID ); kraft-0.97/database/sqlite3/migration/3_dbmigrate.sql000066400000000000000000000007541410616450300226100ustar00rootroot00000000000000-- message Add plant Prices table CREATE TABLE plantPrices ( matchCode VARCHAR(255), price DECIMAL(8,2), lastUpdate TIMESTAMP, PRIMARY KEY( matchCode ) ); CREATE TRIGGER update_plantPrices AFTER UPDATE ON plantPrices BEGIN UPDATE plantPrices SET lastUpdate = DATETIME('NOW') WHERE matchCode = new.matchCode; END; -- Columns already added in create_schema.sql *sqlite workaround* --ALTER TABLE document ADD COLUMN docDescription TEXT AFTER docType; kraft-0.97/database/sqlite3/migration/4_dbmigrate.sql000066400000000000000000000103421410616450300226030ustar00rootroot00000000000000-- message Create DocTexts table CREATE TABLE DocTexts ( docTextID INTEGER PRIMARY KEY ASC autoincrement, name VARCHAR(64), description TEXT, text TEXT, docType VARCHAR( 64 ), docTypeId int, textType VARCHAR( 64 ), modDate TIMESTAMP(14) -- INDEX( docType, textType ) ); CREATE INDEX DocTextsIndx_4 ON DocTexts (docType, textType); CREATE TRIGGER update_docTexts AFTER UPDATE ON DocTexts BEGIN UPDATE DocTexts SET modDate = DATETIME('NOW') WHERE docTextID = new.docTextID; END; INSERT INTO DocTexts ( name, text, docType, textType ) VALUES ( 'Standard', 'Please edit me - Bitte passe mich an!', 'Offer', 'Header Text' ); INSERT INTO DocTexts ( name, text, docType, textType ) VALUES ( 'Standard', 'Please edit me - Bitte passe mich an!', 'Offer', 'Footer Text' ); INSERT INTO DocTexts ( name, text, docType, textType ) VALUES ( 'Standard', 'Please edit me - Bitte passe mich an!', 'Invoice', 'Header Text' ); INSERT INTO DocTexts ( name, text, docType, textType ) VALUES ( 'Standard', 'Please edit me - Bitte passe mich an!', 'Invoice', 'Footer Text' ); INSERT INTO DocTexts ( name, text, docType, textType ) VALUES ( 'Standard', 'Please edit me - Bitte passe mich an!', 'Acceptance of Order', 'Header Text' ); INSERT INTO DocTexts ( name, text, docType, textType ) VALUES ( 'Standard', 'Please edit me - Bitte passe mich an!', 'Acceptance of Order', 'Footer Text' ); INSERT INTO DocTexts ( name, text, docType, textType ) VALUES ( 'Standard', 'Please edit me - Bitte passe mich an!', 'Angebot', 'Kopf Text' ); INSERT INTO DocTexts ( name, text, docType, textType ) VALUES ( 'Standard', 'Please edit me - Bitte passe mich an!', 'Angebot', 'Fuß Text' ); INSERT INTO DocTexts ( name, text, docType, textType ) VALUES ( 'Standard', 'Please edit me - Bitte passe mich an!', 'Rechnung', 'Kopf Text' ); INSERT INTO DocTexts ( name, text, docType, textType ) VALUES ( 'Standard', 'Please edit me - Bitte passe mich an!', 'Rechnung', 'Fuß Text' ); INSERT INTO DocTexts ( name, text, docType, textType ) VALUES ( 'Standard', 'Please edit me - Bitte passe mich an!', 'Auftragsbestätigung', 'Kopf Text' ); INSERT INTO DocTexts ( name, text, docType, textType ) VALUES ( 'Standard', 'Please edit me - Bitte passe mich an!', 'Auftragsbestätigung', 'Fuß Text' ); INSERT INTO DocTexts ( name, text, docType, textType ) SELECT 'Standard', word, 'Offer', 'Footer Text' FROM wordLists WHERE category='docFooter_Offer'; INSERT INTO DocTexts ( name, text, docType, textType ) SELECT 'Standard', word, 'Offer', 'Header Text' FROM wordLists WHERE category='docHeader_Offer'; INSERT INTO DocTexts ( name, text, docType, textType ) SELECT 'Standard', word, 'Invoice', 'Footer Text' FROM wordLists WHERE category='docFooter_Invoice'; INSERT INTO DocTexts ( name, text, docType, textType ) SELECT 'Standard', word, 'Invoice', 'Header Text' FROM wordLists WHERE category='docHeader_Invoice'; INSERT INTO DocTexts ( name, text, docType, textType ) SELECT 'Standard', word, 'Acceptance of Order', 'Footer Text' FROM wordLists WHERE category='docFooter_Acceptance of Order'; INSERT INTO DocTexts ( name, text, docType, textType ) SELECT 'Standard', word, 'Acceptance of Order', 'Header Text' FROM wordLists WHERE category='docHeader_Acceptance of Order'; INSERT INTO DocTexts ( name, text, docType, textType ) SELECT 'Standard', word, 'Angebot', 'Fuß Text' FROM wordLists WHERE category='docFooter_Angebot'; INSERT INTO DocTexts ( name, text, docType, textType ) SELECT 'Standard', word, 'Angebot', 'Kopf Text' FROM wordLists WHERE category='docHeader_Angebot'; INSERT INTO DocTexts ( name, text, docType, textType ) SELECT 'Standard', word, 'Rechnung', 'Fuß Text' FROM wordLists WHERE category='docFooter_Rechnung'; INSERT INTO DocTexts ( name, text, docType, textType ) SELECT 'Standard', word, 'Rechnung', 'Kopf Text' FROM wordLists WHERE category='docHeader_Rechnung'; INSERT INTO DocTexts ( name, text, docType, textType ) SELECT 'Standard', word, 'Auftragsbestätigung', 'Fuß Text' FROM wordLists WHERE category='docFooter_Auftragsbestätigung'; INSERT INTO DocTexts ( name, text, docType, textType ) SELECT 'Standard', word, 'Auftragsbestätigung', 'Kopf Text' FROM wordLists WHERE category='docHeader_Auftragsbestätigung'; kraft-0.97/database/sqlite3/migration/5_dbmigrate.sql000066400000000000000000000060371410616450300226120ustar00rootroot00000000000000-- message Creating attributes table... CREATE TABLE attributes ( hostObject VARCHAR(64), hostId INT NOT NULL, name VARCHAR(64), value MEDIUMTEXT, valueIsList tinyint default 0, relationTable VARCHAR(64) default NULL, relationIDColumn VARCHAR(64) default NULL, relationStringColumn VARCHAR(64) default NULL, PRIMARY KEY( hostObject, hostId, name ) ); -- message Creating attributes for archived documents CREATE TABLE archPosAttribs ( archPosAttribId INTEGER PRIMARY KEY ASC autoincrement, archDocID INT NOT NULL, name VARCHAR(64), value VARCHAR(64) ); -- Columns already added in create_schema.sql *sqlite workaround* -- message Adding position type and overall price ot archdocpositions --ALTER TABLE archdocpos ADD COLUMN kind VARCHAR(64); -- AFTER ordNumber; --ALTER TABLE archdocpos ADD COLUMN overallPrice DECIMAL(10,2); -- AFTER price; -- message Changing old kinds to Normal UPDATE archdocpos SET kind = "Normal"; -- message Calculating archive position price UPDATE archdocpos SET overallPrice = ROUND( price * amount, 2); -- message Creating Document Type table CREATE TABLE DocTypes ( docTypeID INTEGER PRIMARY KEY ASC autoincrement, name VARCHAR(255) ); -- message Filling doc type attributes INSERT INTO DocTypes (name) VALUES ( 'Offer' ); INSERT INTO attributes (hostObject, hostId, name, value) VALUES ('DocType', (SELECT docTypeID FROM DocTypes WHERE name="Offer"), 'AllowDemand', 'true'); INSERT INTO attributes (hostObject, hostId, name, value) VALUES ('DocType', (SELECT docTypeID FROM DocTypes WHERE name="Offer"), 'AllowAlternative', 'true'); INSERT INTO DocTypes (name) VALUES ( 'Acceptance of Order' ); INSERT INTO attributes (hostObject, hostId, name, value) VALUES ('DocType', (SELECT docTypeID FROM DocTypes WHERE name="Acceptance of Order"), 'AllowDemand', 'true'); INSERT INTO attributes (hostObject, hostId, name, value) VALUES ('DocType', (SELECT docTypeID FROM DocTypes WHERE name="Acceptance of Order"), 'AllowAlternative', 'true'); INSERT INTO DocTypes (name) VALUES ( 'Invoice' ); -- message Filling doc type attributes INSERT INTO DocTypes (name) VALUES ( 'Angebot' ); INSERT INTO attributes (hostObject, hostId, name, value) VALUES ('DocType', (SELECT docTypeID FROM DocTypes WHERE name="Angebot"), 'AllowDemand', 'true'); INSERT INTO attributes (hostObject, hostId, name, value) VALUES ('DocType', (SELECT docTypeID FROM DocTypes WHERE name="Angebot"), 'AllowAlternative', 'true'); INSERT INTO DocTypes (name) VALUES ( 'Auftragsbestätigung' ); INSERT INTO attributes (hostObject, hostId, name, value) VALUES ('DocType', (SELECT docTypeID FROM DocTypes WHERE name="Auftragsbestätigung"), 'AllowDemand', 'true'); INSERT INTO attributes (hostObject, hostId, name, value) VALUES ('DocType', (SELECT docTypeID FROM DocTypes WHERE name="Auftragsbestätigung"), 'AllowAlternative', 'true'); INSERT INTO DocTypes (name) VALUES ( 'Rechnung' ); -- message Drop an unused table archdocStates DROP TABLE IF EXISTS archdocStates; kraft-0.97/database/sqlite3/migration/6_dbmigrate.sql000066400000000000000000000004771410616450300226150ustar00rootroot00000000000000-- message Localisation information on document level --ALTER TABLE document ADD country VARCHAR(32); -- AFTER posttext; --ALTER TABLE document ADD language VARCHAR(32);-- AFTER country; --ALTER TABLE archdoc ADD country VARCHAR(32);-- AFTER posttext; --ALTER TABLE archdoc ADD language VARCHAR(32);-- AFTER country; kraft-0.97/database/sqlite3/migration/7_dbmigrate.sql000066400000000000000000000002141410616450300226030ustar00rootroot00000000000000-- Columns already added in create_schema.sql *sqlite workaround* -- ALTER TABLE archdoc ADD clientUid VARCHAR(32);-- AFTER clientAddress;kraft-0.97/database/sqlite3/migration/8_dbmigrate.sql000066400000000000000000000053051410616450300226120ustar00rootroot00000000000000-- message Create Document Relations Table CREATE TABLE DocTypeRelations ( typeId INT NOT NULL, followerId INT NOT NULL, sequence INT NOT NULL, PRIMARY KEY( typeId, followerId ) ); -- Acceptance of Order follows Offer -- SELECT @item := docTypeID FROM DocTypes WHERE name="Offer"; -- SELECT @follower := docTypeID FROM DocTypes WHERE name="Acceptance of Order"; INSERT INTO DocTypeRelations VALUES( (SELECT docTypeID FROM DocTypes WHERE name="Offer"), (SELECT docTypeID FROM DocTypes WHERE name="Acceptance of Order"), 1 ); -- Invoice follorws Offer -- SELECT @item := docTypeID FROM DocTypes WHERE name="Offer"; -- SELECT @follower := docTypeID FROM DocTypes WHERE name="Invoice"; -- INSERT INTO DocTypeRelations VALUES( @item, @follower, 2 ); INSERT INTO DocTypeRelations VALUES( (SELECT docTypeID FROM DocTypes WHERE name="Offer"), (SELECT docTypeID FROM DocTypes WHERE name="Invoice"), 2 ); -- Invoice follows Acceptance of Order -- SELECT @item := docTypeID FROM DocTypes WHERE name="Acceptance of Order"; -- SELECT @follower := docTypeID FROM DocTypes WHERE name="Invoice"; -- INSERT INTO DocTypeRelations VALUES( @item, @follower, 3 ); INSERT INTO DocTypeRelations VALUES( (SELECT docTypeID FROM DocTypes WHERE name="Acceptance of Order"), (SELECT docTypeID FROM DocTypes WHERE name="Invoice"), 3 ); -- Acceptance of Order follows Offer -- SELECT @item := docTypeID FROM DocTypes WHERE name="Angebot"; -- SELECT @follower := docTypeID FROM DocTypes WHERE name="Auftragsbestätigung"; -- INSERT INTO DocTypeRelations VALUES( @item, @follower, 4 ); INSERT INTO DocTypeRelations VALUES( (SELECT docTypeID FROM DocTypes WHERE name="Angebot"), (SELECT docTypeID FROM DocTypes WHERE name="Auftragsbestätigung"), 4 ); -- Invoice follorws Offer -- SELECT @item := docTypeID FROM DocTypes WHERE name="Angebot"; -- SELECT @follower := docTypeID FROM DocTypes WHERE name="Rechnung"; -- INSERT INTO DocTypeRelations VALUES( @item, @follower, 5 ); INSERT INTO DocTypeRelations VALUES( (SELECT docTypeID FROM DocTypes WHERE name="Angebot"), (SELECT docTypeID FROM DocTypes WHERE name="Rechnung"), 5 ); -- Invoice follows Acceptance of Order -- SELECT @item := docTypeID FROM DocTypes WHERE name like "Auftragsbest%"; -- SELECT @follower := docTypeID FROM DocTypes WHERE name="Rechnung"; -- INSERT INTO DocTypeRelations VALUES( @item, @follower, 6 ); INSERT INTO DocTypeRelations VALUES( (SELECT docTypeID FROM DocTypes WHERE name="Auftragsbestätigung"), (SELECT docTypeID FROM DocTypes WHERE name="Rechnung"), 6 ); kraft-0.97/database/sqlite3/migration/9_dbmigrate.sql000066400000000000000000000012071410616450300226100ustar00rootroot00000000000000-- Columns already added in 4_dbmigrate.sql *sqlite workaround* -- message add a document type id to text table --alter table DocTexts add column docTypeId int; -- AFTER docType; -- message populate the doc type id column in docTexts update DocTexts set docTypeId=(SELECT docTypeID FROM DocTypes WHERE name=docType); -- Columns already added in create_schema.sql *sqlite workaround* -- message create a type column for the docposition --alter table docposition add column postype VARCHAR(64); -- AFTER text; -- message create type column for the archdocpos table --alter table archdocpos add column postype VARCHAR(64); -- AFTER kind; kraft-0.97/database/sqlite3/migration/CMakeLists.txt000066400000000000000000000002531410616450300224410ustar00rootroot00000000000000########### install files ############### file(GLOB mig_scripts *_dbmigrate.sql) install(FILES ${mig_scripts} DESTINATION ${DATA_INSTALL_DIR}/kraft/dbmigrate/sqlite3) kraft-0.97/database/sqlite3/migration/README000066400000000000000000000014731410616450300205660ustar00rootroot00000000000000Kraft Database Migration ======================== The Kraft database schema might change over the time. To achieve that smoothly for the users, here is a database migration system. Every version of Kraft has a hardcoded version of the required database schema version. In the Kraft database there is a system table that carries the version of the current running database schema. In case the current database version is lower than the required, Kraft looks in this migration directory if there is a script starting with the number "current version +1". If found, Kraft executes the sql commands contained in the file. After all are finished, the version in the database system table is updated. Note that the migration file my contain lines like -- message: bla bla Kraft shows the message lines in the status line. kraft-0.97/importfilter/000077500000000000000000000000001410616450300153005ustar00rootroot00000000000000kraft-0.97/importfilter/CMakeLists.txt000066400000000000000000000002321410616450300200350ustar00rootroot00000000000000########### install files ############### install(FILES woerlein_txt.ftr standard_txt.ftr DESTINATION ${DATA_INSTALL_DIR}/kraft/importfilter/positions/) kraft-0.97/importfilter/standard_txt.ftr000066400000000000000000000003321410616450300205120ustar00rootroot00000000000000AMOUNT: COL(1) UNIT: COL(2) TEXT: COL(3) UNIT_PRICE: COL(4) NAME: Standard Import DESCRIPTION: Simple Standard Importfilter for four columns:
Amount, Unit, Text, Single Price ENCODING: windows-1250 SEPARATOR: ; kraft-0.97/importfilter/woerlein_txt.ftr000066400000000000000000000005401410616450300205370ustar00rootroot00000000000000AMOUNT: COL(1) TEXT: COL(2) - COL(3)
COL(4), COL(5) cm
Rabatt: COL(7)% # TEXT: "%s - %s\n%s, %s\n%s", COL(2), COL(3), COL(4), COL(5), COL(7) UNIT: Stck. UNIT_PRICE: COL(6) NAME: Wörlein Pflanzenkatalog Export DESCRIPTION: Importfilter for CSV exported Wörlein Pflanzenkatalog documents ENCODING: windows-1250 SEPARATOR: ; TAGS: plants kraft-0.97/manual/000077500000000000000000000000001410616450300140355ustar00rootroot00000000000000kraft-0.97/manual/CMakeLists.txt000066400000000000000000000020421410616450300165730ustar00rootroot00000000000000 FIND_PACKAGE(Asciidoctor) SET (install_files kraft-en.html kraft-de.html) if (ASCIIDOCTOR_FOUND) SET( manual_src ${PROJECT_SOURCE_DIR}/manual/kraft.adoc ) SET( out_file kraft-en.html ) ADD_CUSTOM_COMMAND( OUTPUT "${out_file}" COMMAND "${PROJECT_SOURCE_DIR}/manual/makeman.sh" ARGS "${PROJECT_SOURCE_DIR}/manual" DEPENDS ${manual_src} COMMENT "Make the manual" ) message( "Asciidoctor found") ADD_CUSTOM_TARGET(manual ALL echo DEPENDS "${out_file}" ) INSTALL(FILES ${CMAKE_BINARY_DIR}/manual/kraft-en.html ${CMAKE_BINARY_DIR}/manual/kraft-de.html DESTINATION ${DATA_INSTALL_DIR}/kraft/manual) else(ASCIIDOCTOR_FOUND) # Maybe there are preinstalled html files in the tarball message("Asciidoctor not found, installing pre built manual.") install(FILES "kraft-de.html" "kraft-en.html" DESTINATION ${DATA_INSTALL_DIR}/kraft/manual) endif(ASCIIDOCTOR_FOUND) INSTALL(FILES kraftmanual.css DESTINATION ${DATA_INSTALL_DIR}/kraft/manual) INSTALL(DIRECTORY images DESTINATION ${DATA_INSTALL_DIR}/kraft/manual) kraft-0.97/manual/Readme.md000066400000000000000000000047631410616450300155660ustar00rootroot00000000000000## How to Contribute There are two ways to contribute to the user manual of Kraft. ### Work on the English Manual Just improve the English Manual that is stored in the file `kraft.adoc` in this directory. With that you can concentrate on writing and do not deal with scripting etc. Content rules! Please send your changes either as pull request, or via email to the project maintainers. They will care for the rest. Every change that is done to the master document in English language will be translated to all the other internationalized versions of the doc. ### Translate The translations of Kraft will be maintained on Transifex, just like the normal software strings. Check there for translation tasks. ## Internationalization of the Manual This is based on the great [doc here](https://github.com/KiCad/kicad-doc/blob/master/doc_alternatives/README.adoc). The process of internationalization of the Kraft Manual is done in different steps. It relies on the the same gettext process that is also used for the normal software strings. To extract the strings from the manual, the great utility po4a is used. ### Step 1: String Template Extraction This is extracting strings ready for the gettext system. This needs only to be done once. Later, the pot file is only going to be updated. ``` po4a-gettextize -f asciidoc -M utf-8 -m kraft.adoc -p po/kraft.pot ``` The update works with the command msgmerge: ``` msgmerge -vU kraft-de.po kraft.pot ``` ### Step 2: Translation Copy the template into the nationalized version: `cp po/kraft.pot po/kraft-de.po` and use the gettext editor you like or upload to Transifex. Keep in mind that snapshots images should be nationalized. I suggest to create a internationalized image dirs such as: ``` images images-de images-es ``` This way untranslated images fallback to English images. po4a correctly translate image reference to enable the fallback. ### Step 3: Produce Internationalized Master Documents ``` po4a-translate -f asciidoc -M utf-8 -m kraft.adoc -p po/kraft-de.po -k 0 -l kraft-de.adoc ``` ### Step 4: Produce all Kind of Internationalized Output Formats ``` asciidoc -a lang=de kraft-de.adoc #convert into html a2x -a lang=de -f pdf kraft-de.adoc #convert into pdf a2x -a lang=de -f epub kraft-de.adoc #convert into epub ``` ### Step 5: Update Translations With the following command the .po file will be updated automatically. ``` po4a-updatepo -M utf8 -f asciidoc -m kraft.adoc -p po/kraft-de.po ``` ### Step 6: Loop repeat from step 2 kraft-0.97/manual/images/000077500000000000000000000000001410616450300153025ustar00rootroot00000000000000kraft-0.97/manual/images/create_new_doc.png000066400000000000000000001055661410616450300207660ustar00rootroot00000000000000PNG  IHDR|s+1CI pHYs+ IDATx^wXn "JSPn.{^޻b6@P@@zF'BqNfggf3aa6KU`LAA_C$!H%BQKQaIkE|^AA~xFAA~:IJ8&AAߍF32R|Vd)˧6ZYIq   d,e&@Q!R a\$A`q&=RS)”D,"L0+PTf |nAApFO$BNk+v(-;QAAJ.! UcÁ"")(41vŅB  ?t)Pa-2Ea8䄒$jjF?EA,/Kbr^p$Fv8QD{EUA?59}eٕxB> h:cr2T(kWBJ>~ Kxp:dQlrzI拜2~yq2 E.#o;.?' `lSA~d)/+&Ԟ;q~}HD~ßa;Hol73NtS5?I3oPXiYѯ ۰/<}p{gKցno]ޕqŸ?W`!Isꕤ䍛6D"`˗.155LHj8Xa&E3X*jq-,>")IV?7DKKsmN:sv--M|ȟSi?j*??i3~UQ][[c4w;K걭kj$gJ(B" ^*|yACv勺ϲu܇7#z\E[4.`,VvBHXB~#.~DRRR+իV]u99q# fZR H *~ &PTyYiyYrK]T^#UTjHMD,OxnS|=v4;A/购oTLD +xؙiJGtkD/IyybK&t<2L҃o=FH,H~}إ5xhQV ~X&zԎԽ0]T(~ÓGRzG#-4ieyN>̱[3vٲY$(vypAu3OFIB陇kiNzx2\Hz-P}Vh{ }PqȌ1.VawmK;l_+,Q&H '\m/|%i=hpWs-(=鳁 дlހ ř:C1˥n=`ӢI^tk q𮭴JS^sC> Эk^K3X /e\9u|%7۸;`JfN1~sr='u}vS,٦=i7X*NvZܷعvFFW\5BD"BI;牲J5 Ees J %F\XOhga.I0 !k#[ ]vRtIn9^{2P%ܘǟMaz 6}v&ܾ;YT &,~wӬgd!zB q66Lr캅C6>ոZyEo4kGՀekz.L{͙i kYtSxks;YB Ҷ%-*ҵ0W{P{S?uͻBUq䯍Ӷ)U?^Zg6= .%neYޥ vÿ76nߒݑ~O.Z;X7>)RQiCXv,Y~+OIy,8ϻS1_F.{6=[7/2ا/j_1ʹ?ҪeUŬOmVw|tcXiXB?\^"]fm^^|Fhബ&)/W0dN8!D2eCs$R1Ȼyw!#pU /N>M)n]9wTyƓ1:̋HI)DFeiZee@wZ)E$2FC~-%*Hs4j/??#"*M~ެK˄Hol;D}miFfvnSmhܲYbd, Sw0A'(tjXvqy0Yg'{7>@gwtv2{}v)!)fWPa3(4)<&_Y`R1*:Z @wC9RtuUj5/4;A%mL#$HʧgbX"w5$JZ+`e0eN8eZD89:%|"QPbPsn ;OhpTgN8+ R9Ы'I:O<QԱ]|̥ I(UBLގm4It$jReBQyeHp hp>8E(bJ[(u(!VcHf2ڷt\XyTw\I6&rU#p5u5(* q/:&>rQ-5je *oH,/.$ a" ʟȩv]zduC?4 Ѻǣj<5 \fc UMfP <v*CC̬,000Xz՚5B|iTUPkQ20kk`;+e;wc*SsdA8?6;ܡK;ɵov(>#{a;c-c ܠ-eU,,(Ov:Ô&,K^*(btyd|j_$oeS cپ$lOVyvg[ ??U%,H83Gƭ8QXqӌ~&-9Q@Z(" H$I 0 F l;;*VhҊ+Ulӈgr@s3oa ϔ 3+)իzðkW.˧/l\ZbyE]fի VX6BӨ))Fð_`QpH@(!! ,C(Dë}cݜP`q[ҭdaPv 0is[UEVHlԩy+.(FV;PSUP5lլJ*)JMSqpn(!d曗 Up%m4qܧ]?d ?{)NLVi**,V32}Ut8[ȇT7/M}tРӏh"5-muY.5-jLyK3p!qz-m_߽{OQ=KBdU]?DGNR_ݞ+ʺ$~n:‰ϷN\0mĶ~EeOC>Cq਋8=No{ߛҊK<MAkڹ[gc'7/4ښTIڛG_|LUwhŔYvACjW&q؅w !a{[FAW.sb:4 7Y%:*V'#Œ/\; jl_$8`Rϝt'J quC{WۢvA|rtvT@3n}!*VA %IFOi8R# yq>X:^FrL4dq05{.2ܬ)R pޛ.,w/ŊiUգ4=΍ⲋpv33{ ]w\BOyIozKi->-pȏ":EG~Mo~j1Yn?~%_,m:wٵv=zA?PgL\ /xucGդVX7߹w\3بX"A-r^ @$ ^č`I sB|*:\жeU73K"ݎ4Ӓ x~ΆiߖX߽xt\iS(Q|U=Ig\aM%X"JsFGRf$,ỤV%}2~HϸIS*e[ yv6e'EGEŇf^m={!OlrKC> @SYȭA@4T(05:hV1s卯%CЃo<:vIpCdTaƗ LYE*GEE15\yO=@tc!Z,,۔GE J(0ycU ,suiJƌA?o pDVt4Ҳg֊9o~rԷuwUx]fyFtoC+4펞^F w!]kL?j][c>I-{vGѽ/zȳlJS266kF I!ne[܂iͯ%qW^܄ku4.0( ^"A73o`ftȯY0@w]`2ꆽ!۹{K3?}ETT2ޡq%@ױu0~x _𻂮Dʰ0W^><Ű <rsD޼ͭMo1i&]FZk94c3S,L˒+W)LӜc75uwr{8an/$ZV*:n#=+jx[IO (L˼ccO%I~R͕ ^^"sdHʯݒ!<9me'&+ٿ;ʹ3M壒qسln˷hfX4/1/;x׷z n_ wIWcO}彸fvϺvAŁ%Nwuz+Nڶ"ފ*0}G'1`FmWT9%s3s psNRe!iP@kEZiӜհO>>>ރ>QdaZj%2#S>IyNclSط"H$ Xۚ, */+ a$`JW9yӀG( #Ќ1wE S˫Jv]u,,tq@ \cͱM#l@&\߻dTse K#ȼǫVtP7H<-9_'#"sHz{+h̪ y qCF ) gX׬qV,Jt[F rrI,ry PTڶ5 (i\DTq7F*`T7EOi$`ZUR\J` åp#C44JOȤTG`Ui8`L gXE$ TAVN @IU-K(U WFm@T͖nӰǬ7]1γFfZ4yM`Id@WD}ԣʓb\*ׯ9;v7=s>\2c@!w,;A`܉g@\ 9gA4nM׳ǣK? IRZ'g' FPXI؛we cZ)weM@FV')%(xӉ.Sd WU0:&? ðp(R@3tz75X֪!6}viC!yO]ޭ Avz[)SuREo϶Z۽E 8+S ҄~L*ި ^JT m^Ѝ쒏gWc>zgѶ^U0F;j}g39$`Vv U4J\"NxhLͰE#4٥+qnw o8q9 Fxk萿cLֶMm<,Kyh`):⌷]M'5tS)jM/Oz(0fӝ%qih30?\;=Tϵ'԰q53Vŋ2"(eZ>oS_% gOp0R!DYq1\>8X]8( FasWԂfn;mE;/7݇7o[S4QʍwrjuJ*ݾ6KFxzA"BUwCWգb.{yUu h)C'N} mGj#s]T6kڌ\qU+LVV4(AWb.\6ZK'y$03xÀw&Y9Ngl62O4- O\q+,ՙ6Z62\r ,?c[A@7I]ۼy>:8@k_ukԭ, ۊu0JZYܵ0헺h#)MX~ɽsBo/$aʖ#9eACioވ6Tm=mZN g(qu3q٘V68Ќ *h`pԶװ9;.\;<^10b5_[3a+0Uͻ YtԆU(Ywz]Z)iL-hx!Fͤ3Y6$ݗk8kc Ǚ,-l"LKL].~n~=L8,&ٺv^cWu~kfw?wI l]S[1 &he;}NZ,ժMKF<\#f5Vg1h /[}lـ9}[)qcǙW~ A?3i_= z. AE s S/k˷5 ۮ=.fȿ*LN[% HzA?xA[G+ussPSIډ$`*v:Ad(" )VRH[Sf\  ȿok2r%$CEy^C s2l;b 1("f33'#A  HAAAA  DPEA&. 4tA  M]Ai"(" HAAAA  DPEA&. 4tA  M]Ai"Mtůy 9Kȧw#\*_o_/YTAn~M}RK̸.^72XŁQ]c׭ *ٰSi!-}{zOڸVvr'_ DޛFvqv=h<||:w"Cr8HiG )u=.P`i(lÿZ?ʟ4c1H&mwjp83*ߤCnظ>}^B$?8r=v#Wzc~_wX~Acɀ ?Rǯ5QSwsDvfe{d2T)3LO 70w1UehjT}wjsJC,jZcS}MzCCًkҜ,UY<]q8݈2\ͤy,){_f=\wgYh@DpeXԾaXԫ?攱|qJKY3..7b7pμ~;,׶hx[VcV ^t׼RREk'ؔn_i K ŝ&vu9 ~W :CkH'W|ʻ"cש]uMurp TX couW!g;yn o1 7^%[j1Jvyb!Msڒg[}oQsn>{n)kQ~iLR}_k0W[չ;&ͰB\~آ%#:bŝM-f.8k (TؓS`Hf=;H`,O)j;D[a([խ2\|]c7iUC*mwE]aڕ>w'[J15Lz0ۅo7[{Ua.>{}7_0͜ѢZ8vn,PWm=Qlh$;KS<35{7ջDžZ# qݯ´u6jya Դpۃ)0.uV|XRHX]5z|)I{?pmk}Ҿ{DCCW?}:{3By[ E{κ+'>Z~x{@'ϥ#|!?xy},:/>]a=l2_6Cۍ޶&Di;ٵiHg@g5\?YnS}x_Ύwv645&p̵w_̯(0?x\ ?x[C mͭ>I yqf9vu#K$r7veqxCs ]on7J:_({`1 2.r0F xQlG"A5=LYQa{^ g:\<-\|yb~'I< +d^[oL;g?s̷;Y!uE?ЮSodoNuz0Z oH|>د]{5OEy';of,WO{£ {' yϷ+ʌo{̼-qޙR_hi7By̠m~6~Յn?vH H=?ûGyB7h]K[l—Fhsy"~vɉ]KxqF{=&G ʍ X0XO(w5iﳸ\ncm|r%k}y;|.y?D kt\wݭuK3qDL<+rޅ%p]fHw| ?xm/煏BQY־;?EavnV<Baq}v HT.6 k[&^e _x{t۲GºBFWF^Bytt2"/ 4(Ӊ\?51Ρ˒\C?qB7G&;>ξ˕O,צCߵ3gw(Azƻ21F_/S:Oq1bјlz,Y LugCZ.xy|?:(zh[ T-t$SX YzoZNg7&L~-8(9zѠ]ѩ$;[@4#pLe߾^~%hY80tvo]G ݦ60D;IKXK>S_woKHOޞfvļ wbƁix?i4:iQQ;ph{aO?01 Ô:2mze4trh).kpU Bs6E_K[{M5Q ˦fo>?㗇(I9٥Tf$|8  1ͼX* uunJ$?O>4(9dۇ,(UROXkj/$|!h40(qeuuʿƮ Ӏ$IY7 !%$˫ԵԿQ$n4zu:tAq4RLIm1QQK[PT@uOY곳|\@p /LǕ55ZUc(_3K0+%\{Gw,đwK|(II1n6Uk@zL`8 ت.8FQD6` y| uQJѬO1(_zw@R[ð*=1٤˂\ЌvVJu[۝Ʃhs&hjU&c:"1QedwTGd>/926x`E<mS:T_{ٕv:6kkC持9EUbX՝|?H>V Mt*HO1p-]-I,X S:80)U+8E_=%LCKpq:$I;կJtus9ソA/Mr 77xW:)PgZ LK_Պr(oZ'/UlsǼ+bW}̀rTKf^WBd^nh8ijjj'Y1o& DdhڽW__h#{fVe=mzѮ0˲h+h2WB 1l*DevݻkeGL93fLlնի1twߗ1*4rotcQs E"uL^  ,e0* nN k}4\Nm*&΍oUEV2gi-RQrniP02j+Kv[=#(_@ (p8L\]K`U[n7j7ծ|RM'w~z'βʵC eiN%Xl浝5Z_{}݂E{\P*Wj7fVohj*GE#Tia5Ԅ9yҊt \2Mv\1X&SU TB5S=2:weL}-,%y"ڴ5cvXPf) @d~͕]ĵtN|qR! @q2K(Z= /.( 9KZ5S\#ReBaE3E Ygfv|0pc7_K˓Uĭlo'M2R>GdJ@^H>׏Cىa\10=&ֲ \'| 5Bv|B!U١7Bt=ZU-3޸IH!;}Vn4;O84 _&q_Ƭ!UZZ@B~WQ^ϱE_ްj̞(o uө߹;/xյkwt=Cjg0(vu>:}+)?MwwF/ˍ)uQt \[6M\dIf1W.Gvo+ԨF~wWTI⭋/(o['fQ 9~)̳ިa\k+Y̋QD04ZiƒJ$@ۭJvn] N\- ,Iʐ}U+hDBĨ\հ*! \ۭoě{d;:?g-cѮ[ڋ7) ".h!z;o/g/|@8pS+.(43P)?UONCDOPH!;gA9wDoWǮ^_iپȅьm\kjn.ʮZm0B.z30n6f c=9X}?Ux3&x aq^h?f܌WFqf|X5#:Towhkߙ!;ka Xѧt._-~sG>5p3EY,Q'X>vX.#(h[imO.89Xl;1mN:WYHVBaUt:gq:M:0 ]>j􄕏zuR׍5np N5;cʲ߽.jV2n2aiǙ1{H>e0`*wM}̯-1l`QnQT'Li)AFrqq=~XvIKSꬋ`{GTmgNՠ0~3(zb@77w1 }Xc5{݊i->J.}Vנ*K~qg-gƊ,٣- LЪv$)өV3X]fm`Vof_SmQ1`Z߶CԦ.y?}.uz z?G;2!߭^M5(Jx>6}OLT#,&B;9jmRQ-.Zp{}]8Ϟ}E/ 9b7Ķku{ę(tZ DD=dH3q9m|҈ZZ;ŦeYA߇[2f{hpcV   tA  M]Ai"(" HAAAA  DPEA&. 4tA  M]Ai"(F/OB&5es|: }=蒢 IDAT;;uswwsJ4||:OnRO{uqx!YZ$ ^5tJCz ?NBJ>ȄÝ#7  A*oҌiw_}m:>?ueƿ~!URv5z ?Ny R)0tA5M[woHg(98U$<틮&D>JYgcaY7>}&Y8»ǀ TiԾ!>1kϾSpj{>OX$N,vwKQ_^p+暑]]<,$bOs"V>aNv#M~ U{k$?n=G,8*[d{d‡ʠF|9UneLr˽Ec% Lps8s߳ZKgg 9`–{ TQ-Sytus_#O^w7#Ĥ&)Vr.n^VFYRA|/ BߥZmȩ$`tfIBJ3e+>λ܁Dҙk^wpf497/79:zׂvqS> ݾ6Ukn;ubڕ=}fʲ=/tVÚ"c6w)j&?]ĴvxOX~?C{' yEK>9٣f+n9:i@]1OӻiPij׭ _ͣ%2=&PEo?2Ĭ٫.<>eg1'ovqyO{+,9a|ae m\0#k\ch[vǎ oC=Ŷȼf+#Mj5SdhJ,z߆ ?w^1A!Cma Cz1ڼ[d;k3S2_ͽ ~Ŵio#g{cZ[!6t8:dg H2i@бt`w^\jĂZ,p7qӯA,~;K1kiW,Z)@װ5ډ0oMg?t@)𙸻oVA [G3[W(1) U1K|,[IiG<#:b˾}m_J>R8i\V Xi&vc2=Uh,"KIf H~Lc)!O@|}P#On3?{WkV'TV, !_H^9q SQcWMI@azJRNv)ZF͕`ͤ%cne6VǗjY} {JQh O qQd,']^`hb|ny\JDYUt oa gb͓ɢLۓztNeM".uSPs`lڍ\nNjk&NCN&FV𫚧#c(@$^s"ۈ^~S|lGeX"RS0زm˫v@ FI&OyeD^Py\#OqOUiWQVG.1F鯟GHy{l-#>mЦ}~UfU>}OgقΞmGy;[b?'ۿOa|[nH, 7ƪnn_< Cv۳ЧP4vnB~xuwCܾTU}oϡPZ!t+>j)0 &]5A B@MjP. t &x"Ui#>zZP0zjM!tKЇ+/gnnZPիUŪu`xIKKCBQYXXH$ -uxPni 7xK! /L:H\oj҆2'xz@|Zqݼ<<+>^8G ϶Á*y=]>6"h˭%iՄOVS i6phuW}oP>^ٵv[gڦUuۧc9"?\o-z6vDOaHܵvySX(ȽC)>bww;ݻ{Z_/W7?\ I;y{tlHzsQ#˚gt?ۻI9oX@[(2jw$]N_Y;vv@d?:6xa`Vk&?dy饙wpF-ƝHV] IѣoӻV\O2O^;&-V]fԽ7gWZF6~wC u7o"xpœ},[:xx\@ޯ>k%zo!ö:g)<Lu2`;_BDÇ [lYhhhХ˖ 6L DC}YjT(Lj= [wkŜ>~+NMu0R[eXTZFHQc"ǎ>E SݩETG _l^^v4r+'"6ܻ1֮Gh%k{ĠG2"Sq3e4jfkIu>-Be7uòzu:~qVlg˧$2Fy_3y ޜ ӭiZw/('""AqjDZ*/nݺGʝ=jTݺuN P2骐&$C+ufM-LeOxEB\a՜|;c "a8̃eYyba["""^!ԒDꚘhge+`9EGtl{Ҕ;CjsdGD~jhe>NLIMxiOr-P |Pl D7u%OLH5y'>{D7Ay{i֫erĘ/'G~666F/]l("ZlQ666K[+4J>.$x\m Abd 1BS]50KSO,<C%%d]%%"") t޽iD  EhhDUOstxtX"Fkjii[^ïj1#X ˬp]ʕr(΅[zCkk[:80(w6ߖMQg͚MDӦMk>.M1Y 2ՀѨ]ĥ­UmҬ* ٺN̘o8ְ\YwD|cAOâ_5un՞A/S8".#&YD!]LFlh{.[ζȽuÜqpӣ{FˈKyɵȶuԣ:>]כ-\ZP]JXVJVia\iY3Fnbν3M-sZcDĚ{w=ιʈiƻrLa٘C.km22d֤Jg7ֿmȡ뤵ZmoɇdȬJ}7~>Օ|-ٯm u;H1:el:[\3bcC7@r~}\*Nczou],n_'xBBBTKҥKeʔQi"=\*)blܮO9=Od/RÇo8m[Ŕ3D P$xBWX>kOQ-%B-!P`A7s?tK! /L6[oP.`xDQQF|wҦvZO>}<\`bUU+M͂B100Svjjj@45btv3n)$q(p# t &]5A B@MF.I$tuujT8%o [ڼxB "#ž?imZP<8/mRҐdiiZ Pl]5A B@M3w{UK3.tݻ?iI'#w@!ܻw%O4A|ODʠmРIΟ. B i6++TK܉,ArojoߴW!6}]e(yt:\Z_6D*dh Z )3ۯkƍm{q?'R3gHG{;Ǝݧ}wCZ׮U˦W9V_Z1cM}/ ~'SK۶q[gI?\/6iвaiQ l԰}.=(BIZdet$=kO ]""#\vڹOncp}ߘaZrm犓rb"oUkW_t^7DꎌYBiۙ{/\~|K'nzYj40C"PaT>Qn)qOo:͛GۄΛ=\AEc\vEtD{A׭T+YX~m.eݩ82/ͣUK_ewJ2/ձ{C]~dUu#G|rؖSv?k=x8p-\=;ooӻV\O2O^{(ϽAOt kMylʖ)V~q'gz9ǣ9""G2.;x; է==0MOui"M3#V=v*rחm!hv;c&ڊc=>urߌL#ƠrF ҕZ|44/>#ήuӦf)NesCc'q8Rט/ҥ3jc$8I>Է{jV:r rbOӠqo_ӵj| רxCA(Jn eR3g( VlɯsL?:u6e;KmafZsCPk!F ;T1Nu&Q'\<~Ͳ{y!#0nN-ENaνkkP!}DюBDIm;smfef 8#ס8٣{]} #5j2pgট͛97ocCy#vUA8t1uF*ODv"bH`j߯[>Nl|ҍʌB!.]!f2"%$i[ħK2tאiK$PbDFFZ\B\Yqi"!.ٰjNyrCc#a8+X9'..6Nu.""RA-IODI !G^߽yx@'Y8*Rbb?^\ 'e4V"{u7j +.v EjĽ#&w=▦yW`onX"b"̧zn%""^.ؤMd]e MMII;K( M+>.$x\m Abd 1BS]50KSO7 KD ;u Gw'uD <^O}?sʳWmlf.=IDyi[^ïj{ye0jM<ʪ %+׸[Wu?4;Ĭcljj2VG6u3(R".16^N$ ".!:VaP0oquV_klB_PZŨ$T[LDWoT1bGXpRʒ/c8,"k٦L^еBy㈈ɦ:ŏؿ .=}ޭ9dQǧ$*K ;I[&ڤYUu8q1/&pa]4m IDATZ/>Hǂ{wqXSM_Y2#2bŸEdĆ̻g"j6 xKZfB%|]"ұՒ=wEIXc W8pՙXliR ĚPbN 2H̺5ts#v䜉mGo8iXAb~h{u?/WmQ wdǂΞjZޣ٘C.km22d֤J/o]ӝoQb^u]|I9C@ 懏'- ӪU+"]@#.,,jhh B8d߼JKG! B"Pȥ‡{tmfׅ$BmFѫ=(VnPYLF?py8N&eddH霌oeS^$ Bu^}1qvT+B} !8$., P#bH$T[WNCy.tP:]*0 gZZZEB@MjPOJvK7[trȟXk+TKM>R?G.~^eonӯȈcvQe>?0ן$摍O>2Qߝ1 ]]ZϽ.|MNKU'4ӻۺ:P_^Y>ֿ!Y)YWSk{VVcdg K]s߅Qɰ<,x'ƻw]\́NvGdÛ񶠣ׯ#}5Ox}AqIkl6Nv|+{IS8܄:~>)>*S 5U+k"]Fúz@(<[ (V6yGOvY ɽ[,4)Wj\j2D\̹ל>V~so淵;}d[Y[u/JspjW)k`ߚaڻ0ձ?vhԾE1;JO>2iܶ{7whlxH_oO}>-mwmٹ8""٧]3V}"OfT^^ndҳÒvYWwjެGq'+Oiιeq ڼS'D$ :p mhĥ]҈O >ks;{;.=T}(T׊OcBw/Gg#W_H_;[S;G~ _}=VPƣ]~iޱEi_|6"N.W]=b,iԸmamj±xK͛Nx~>\9zv㱔;uvttkު_缋 :{yo ֓S)(i^ܺ)7.?ֻ Wx">@]UѶ }|<ܽt;aAsމ>5oeD\M{xyzì){eo]}=:Me#y2bI૬_8͉#zvsos zvx8{Ͻ&ol߭[ g({!4wKum M] k{ǚ \V2[ nc7N][ڽogS=zzzz,կU<קߦ" '۽X8zDCa~p_k]'*3巏>'iZ?C9zک-?,_}OeC+; B6$m]?늦ʮ@^rjmE#8"Ƭ^sk$F%ZՌ!ƚNp$GD$`7#84婽6|9`b嫋ֆQƝ#g?i<ȥ/v}h+Z-;FTi>l୊mY HhQw̦g^></1yS7Opѫ栠=/8 .[ԧs'5}y픔3?_vK!{VE}8xM+Wo}wc2n_ϝ%j/wXV5y|A:QK۹t~zZOw j W^+:e{:<}9cwcMiY-Tܞ.DDOoy~e_?~ѳv 7q+DĘTcAuAjS׼krj#lugO]~.=[k[rI>s|w{8f#gm7zϱLwbgOXss+ⵌAT1PuJ۾Kz5wlvI[;VkȩV\TVlտ]4-]r%7j9vVcIôA. U6h~ɸv4XzP,+׵] Wk%}:n!Y֫+r\X ]:79w6;HX֩1͚XG*s1rݳF>,y^6b4~(\k%L:wRü*h1>fNH剈]űv޶&aܼ,""#yh`P_ H }H;.'>qQ, bM6o͒Y3kYL3Kͫwhb;UˉկTYyS5[__Z17j4m1];7qµdwl:7)Hh۱{}CN巿=LۙiqjrHܩ cɧ!2ձ+V>|Rz"u'VGoUoڈ ;Ѻϓ1:= Hr)~m+-zT=}7ϺpG/ tXHաΗ |,t//dȸiOӁ!z}k-1vG/bDO82ȩ]佨 eRK=*Xc3Mg4,t'"RdfͲ[J8mBVx>.9:F˿ .SPVyRAD\ltly\R($ryZT\߽.(⤜n[ GӑSaÿ7ߥ1&G\"kdfru+r&-'"VHyư,ˊ o~+ yR$U3NuL8.U)p׿~g ;,,?^ٺv癧rV%qBfh:޶v]?agE m8|]>9Z|zAQBu6H|;A-}J,8:U|75+Y]c"[q񙷗voxY&.'"b ~c691 ̲50=KIp杧C3Y!verɹ$yc801,\OK(oU%buMLf,lc8iց>WH$_Od*NyY#6 &mE#=;/Wꛚ ?F<"#L|XzZczecٰFzn-}CEySmIo,]XYy k`PyIr,߽VVG6u :""y4}nahY ZzV#ߖB+iaч?8̲Gcj׼|rw"2iN#"-5a^*kZjcTx4 \{r~Y [1WihW;4e rƳrM̽c: M uO=t2'O9wfKO4cb9}#565H3b>MMt);[Lݰuֿ׏5<0Ykb?#ħ&~J5oа̫3ެRr:0FwPncЙgVuT՗^d^ۻn76nj­Ƨ2Ljؔ1>8mV*XC1[~lþTq7o`ںuD=S0^Ngƿx*)&2FIUHP۷ڍ +CޥsDEI^6Ak6߈䉗' };`b+b/E>uݝ[Cħ'ej1/yq/m QA\JO*Z533.͐٠;k"j(䮠ws &]5A B@MjP. t &]5)c 㸨hLZQJ,/V\. KKYUrBZGH[TzZ#_](hjE)0X,V#z|X|9ss ՊR****Exjm|axF&iii?ɗP.W{KIMEB^Pkx`ԯW6;y"BDhhPSY[C6Oǯmǯ(\8پۑrQy'ytTwN3WnI瞋KkVӱfy)gΔfr|vMǞmh'.)p :\t1-fLX5v""ooēIi2>#w`IvyV}xģHf9_&ORS2[wcsW L?rs/RF5NVۜuqM,%e{ቚe6iR#'M*25aWg̡aڋQR"ņ0eܴ~ 3ѧ.szwU+qK庾)D̛ ;y.%""N;JdDskzi*W ʌ|pS/z-oXH]}=JWTozdeeq/2&Fr<+`9 qIV&Ok%J>^&PyzIeq'RsքE_)1&e:2#?\ҺV1"r#yݴзTaKǗ_H3D* UlX[՝glgnr?CY!mPx >:C'wdZ7 ZzBəU.'LĹK?w:TSN1Wد/N#ih{wN}iɢ) ܛ~1YH']fVll(z܃\?˗F%TDX-K:֯df/g$DD t8x.#::QAWRS{H:D>I_3s}jSO{Ʀ<1zi$h[u USVز 5yCHXtjffZ‡^')kV58K Ь\wdeƾ|i[I&~6뚭AO "b ΐ'ݽ𞈈hMԵ2`ȼR7b44"SK q:OēЬMf_JIzu,s#0YރǯčpWߢ/OD$;kxA^ ʖjJ)[+&횱U{]Bu<|?o~'f>0n& SC|oVɈ-nIffeh"6rt'gZ-xn+*]`Tk:/~Ŭ$ت*=<+]1Vup\Ux⥉ji#*Sjѕ+W\7W--_PJ|[#$$ITt钵usq?bw~<_k>Aɡα-;)"tR۫S6CR Ew [X)&#tv) OR ٮB) ?!]+[](VffkikVR陙BHeaaZQJEEEbl"(**ih[#_](3SظTՊRJK%jj査ի,#K7==qŊ jڵj=ypՊRJ_,jUPk E0LOD),{_z Aڵ@@"hJ7FnhO B@MjP. t &,)T[8T[dL*Dz5M$db$)Ilr|L$M Ijr|R9ayeQrT=H~`IENDB`kraft-0.97/manual/images/numbercycles.png000066400000000000000000000317031410616450300205070ustar00rootroot00000000000000PNG  IHDR"qK pHYs+ IDATx^uX[ ""`ctzыM("`w.,s?v%dy]<>̜3{QXT  ;ð   N_,+55N30IHރ$kژ()*L0rs}ЫG)+q0H//;?aaaaaai1+6t@% ˢxg㘡kά=i(LcN) g[uǦ҂4Xo͘O$~cjKP㰄 yֳݎO}bLJ\cu*Åa.4>@bwz#ǖ85e;f:꾯]71sQ>G#b6}.TpsKgPԝ[6 }qFn*Kڳ`ї{ař.,CW/*2PTl"-T}ѡ,޺JAe^v{v 7v)6.lOD"*1Nߌ:N[;vN CC/,$\[I3sCQ+*1S"- Ͻ 7čؘ^;O>@M7t)vYQևdSM[k;;ڙk;GȍKRn`^0GdX':͟Ɗh$)gCzm( GGͰP%@DhޯB)FJDDh(4 B *F=i&"@CVs0%!Vp3F 1Y?:a)I=ev9G^mi%ATD{I ahU{MrU5O-K~,2b"J O)vxB4bRuReEl%}%nYB <Ʒ;8ŨJ^y׃*eɓTA~ȽRIɢViA}hT- r$A$!-#]AI(6v~N~GUUI0XM6!?k ߦL6Q A҄ĥeD8i$ =\AWC%%&f=C;! /u#$'_h deiPI1y@ @*.,A3uZz[e&;dbWYr( eg+)Iɒy9y} Q/NUB4h&ӯ[Jԝ̄fE g|k\Ea*HwQT\jɭA"@2Ə-dKiD-QhOETUIzvCx 6F\D?ɈJ̬s9ؤJ*u= B|yٵ_&.Q*zwooƋ`4ىOikI^tm (znjRDhtd}Tk1-4RAw2(ٵ deej&lY J.y; gn}@P}2K"m|2iP]9cöV[ ymцnuPPSso;׎ݗEDlZ)[[̄u&Bb*:s߁RzՎfPCFoI(X.ەkJaUㅁ5:[ϟpr79eyYM+O6-vaEJ|{w{6oR#^^fm5 9guҬ {-u^ke%žFD/q۳p+!*[mog؟ *I3'(]wz7;zVFl~xjn;[n_e!"";INIINI3=K3ne<[d t\wۈ2NM1+@e O{ #^??lӯjUI1ff6]yϝN?9k% k]yzik>sFo1ݴ{1!aI՞=%0F!wٯ{,/@TS+&; 70r_[~Ly=љU~9țrEqx0>oba1#KH3*~~nȑ=e#w)Mȋ*?~ζ֖n>!fzW,ZXDL"7-88Q.ܽh79gEb):.`JyK7Je^v{v 7v)6.lODrVCe}"1h!;Gd~._:͘λثd XՎJOi׻~E(Vz]8D.+dEuGȝeXp=Z}y}VL!e-p֛VzwŅSR~.}*Z%<,|Ij r>BTG=祇.GxE3u\ V/]zz~kf  LRRR̟?xX>.NcuaC4 U e\]{K@JVME6LI,܌QBsΩGzvUsbQqa Rm ,vp;{v.+$$E^x*âi6SܺJTQ=SQՒ͋:ω Yz+Ѐ&;}*~_yAj8Q$oXyaDԇ\נRݤA_~>8A^\u) G$I2eH)IHˉrˋ*t*-,،ʇ;F 2`H LHIBZ3͊Od|zeEYޗׯs]U[4RȪK")`f|^PGk.*d fۻW'6gQ@}r^)TJPy3^k3Rm̘ [h=u=e5M $>,,aSܩ3fUk-2h߲jv+YZ1˷wWi@*Z9*zb?۽fV It]g' HmGʋ;M[l,bkح8Rf=o;׎ݗEDlZ)U)%)Er2W)^B&_ur_q}!Bz~wWRwv\8PѳF<i1y]grB2 D((u3Q>D!Bjk®g,V5hwoA(,*CgJ bܥMڗFFFS1{no5e$L ?p⿒1IErdN8[!a?$fB矋;~?_UpõYaFc&!!;5sr$%+ᘵoz|301kuu{WVV!A3~Ǭ}#II_0101010101010101010k[QE/C^S.R2p͓k%Das<}bH|d؃OU.kwy $mU%i˰>>?qX{'0c K[]g[s6k$C5t_qzJ\c%w"@WIUݧv|!Ttw-I;Gڻ]]hffi=(,;#A)vd}0mǃ:a?MnR}U1dj1@[F6]N&5`T-aQ0grqh-A fvWAieZiH]b\0e9#?;XɤI}mC |Ol_'yK^%)rҽ~T$A*mM19wdd3s \;_Y/aدa             1c7?;3(/N$tsYkpM]m1iY^ҋ/4[B2L?@/nDJmggc Y>n$[*0T:7qVwǯ 㹴~>cFHu36-׺oj7)M;(%AWY݉ɏRS񫿵YzY1zzRC~9t% vqoy5'+K>j:P28(nl7ܯT̊ #MLL}mp'g~jenbh;q[3UTtp PkcV*6i54 ŠC& ഖY/#&% 1mWWlRz} UFEѕo4X2=Nq؈7}w$ hwG,c!#/Uφn4@*@SPPPv^oP 4շbcCW4~Rd&4^TAn>TNV.!+W''&fJ 8 gwdD 3N?D2 I)DwgÍ?[-?l+w %|h4P -F O/$@œ{;0L ;ۻ72eޭ!c; ijgCһ26}xr@k}L^z_UǜRGNS@jY:<<~W@>w쌛g2*b7\q**,UTȸHM.j~1>U< >Z(JҨ.Z-ơ#*ES,ZIe}P" 5ռ8jϹ}-U9=E?b(g%ː+;¿:q'^ξcwUm6 qH _yTl[SSy%p*F~3vf$k#c˂7ו> ,h>54[6Yagjhl/Pp׸[ZztڴA'7[DŽ SVF4'id_[sec::NV }{?qz=N7y]hBN Y8믫S<6[T j7V>yL93W9MsEآ7_j^H.]jBոuКᓖuV%O. vXM 31|kr-:Txk,n^]y҂wi}v4OGp!˲`0ξNE< >r_g[Kl<-ZZ ڏ k*Fs6yu\>>AD{ZՂG%TnȒ n9c= RIgOh4bد61101010101010101010kG1)*R7Z;~%>c3} e/Wv32; w._H}oݷ3wwtbt7sc#3穛B>p:BŏzX36244TuZg-(%QJIX` N \.cwh$wmKs/:G)~hOv'9k|%^vpVREjWlK*O{Ib\ЭGV~ZMW29{"w̥vvՄ/+y\mGX=RuhMb}3tܘy:ΒahAu2 -7fdO1֔$ aAVC}΂''kʆc=GzS|Fv/_7cY=•&vo|?ri}HyMC'nݎg]+OV.@e\\ڼ1qgh?vJv?K`3n?f頲?/ݿRS3749mm}sϾu[hAhEXw;q9l{BfؼiK:IxԃӃH}m! $)dsMtX3*/):N-=jVK6/~{kPÃ#H7yd=_4q  !1/4bb:O՗-M L+=-o9V;]8DeoÎ{NFV6eV.QQa ZR~Cŏ-XduJ  j)v%QV.@I**X"Q)&!V}9WeYtB|yjp"fbNd~ȣA%p=_':zmy;[EL$dQ~^j$P% *?)`T-^eɇAe/YN tXF*kkI|`C*IgK=\S4S|/@QolljUu7& `Kk֔"uK5ۭb]&6T6LJ+$VbG*{ccXAo8rZN3LwSVt:=[7Tܤ.ciDiE\4LN,Ta>6c=pb?Yf6;6K`8kFSnZ3SZJLyk.R%0rھO>Vl?I"&weM3KI~k׸PY K/s XtNWrw{˝ڀ՘aH+qܓ;/\aaaaaaaߛqLgt: UGNN$p~'OivPVVᝁ1>~޵ @Uxߐ.2/>!Mk j#I*H9t۴Q1Piυ,vQbv{:xl-.I3:<;Ѷ .G9ݘήbcn͋kݬ-ͭ/;*{zwȑN6^㎊ Tރc~^6.ޫϽ,EPaɵgWyY/,^⾫iĄ%U3!x# {~{riD!VVI&Ɩܯ?.+#5l,w[+{c2O>hcmi'a'I8f *mur舳~{RQ3~BO\va"X,E:yQyK/ݗ"N ]8$L @7|vnA1Kib3zIO'}g0+3޼d d#x8 hZ&fOUPUQZz^lgZzʛ"bROҊ2iȰU촰yX_gv݉htڬMh~NF74Y`yK}ݲgN$x=Y!Bs|eN7D.Zo])RS;5{ !`Ye9Y ciWYO!^ԉ9/r*ĻW!amCs\Wj}}ui53V>uԮYAJ$Zڽ D[{œYt4`iݼ6.}`JJgڃ3;$e`Á&ʦS" #]N2(;SXTL%b6~$J_˳)E6L.dDٟU 8ѷxbQ駦8FA+{no5=N7ðERja[3j4ưw-W/Bu[18>'"|x/шacvƏV,hl~&X6pڑ֝,3 8h$$ijp7m_vN8тc͛>I mmQQQ?c/B8 DEEeeeygG$ea ~acacacacacacacacaW3 Iܼ:0 k\I:]@ߗ[ڳ:aѭIa10L@       `#2QIENDB`kraft-0.97/manual/kraft-de.html000066400000000000000000002275541410616450300164370ustar00rootroot00000000000000 Das Kraft Handbuch

Einführung

Kraft ist ein Qt- und KDE-Programm zum Organisieren von Bürodokumenten wie Angeboten und Rechnungen in kleinen Unternehmen. Es erleichtert die Erstellung der Dokument und vereinfacht wiederkehrende Aufgaben.

Mit Kraft wird kein Textbearbeitungsprogramm mehr benötigt. Rechnungs- und Angebotsdokumente werden mit ein paar Klicks erstellt und automatisch abgelegt. Kraft generiert hochqualitative PDF Dokumente für Druck, EMail und Archivierung.

Funktionen
  • Kundenverwaltung, durch die Verwendung von KAdressbuch tief in die KDE Infrastruktur integriert.

  • Unterstütztes Erstellen von Angeboten, Rechnungen und ähnlichen Dokumenten.

  • Vorlagen für Dokument-Kopf- und Fusstexte sowie Dokumentposten.

  • Kalkulation von einzelnen Posten.

  • Materialverwaltung.

  • Konfigurierbare Dokument-Erstellung im PDF Format zum Druck und Versenden per Email.

Kraft arbeitet mit den Adressdaten aus dem KDE Adressbuch, das ein Modul von KDE PIM, einer Informations-Management-Anwendung ist. Alle Adressen werden in der Anwendung KAdressBook gesammelt.

Der Quelltext zu Kraft ist open source and wird unter der Lizenz GNU General Public License veröffentlicht.

Note
Kraft wird durch eine Gemeinschaft von Benutzern, Entwicklern und anderen vorangetrieben.
Dieses Benutzerhandbuch benötigt Ihre Hilfe zur Verbesserung.

Lernen Sie mehr auf how to contribute!

Erster Start und Grundkonfiguration

Während des initialen Setup wird der Datenbank-Typ und die Adresse der Firma abgefragt.

Die Adresse der eigenen Firma, die automatisch auf dem Ausgabedokument erscheint, kann auf zwei Arten eingegeben werden: In der Prozedur nach dem ersten Start im entsprechenden Tab, oder später über den Einstellungsdialog. Die Adresse kann entweder aus dem Adressbuch gewählt werden oder per Hand eingegeben werden. Dieser Schritt ist für die fehlerfreie Erstellung der Dokumente nötig.

Nach dem initialen Setup kann Kraft über das Menü Preferences[Settings] konfiguriert werden. Das erlaubt, Kraft für die eigenen Bedürfnisse anzupassen.

Im Einstellungsfenster befinden sich die Tabs:

*Dokument Voreinstellungen
*Steuern
*Dokumenttypen
*Stundensätze
*Einheiten
*die eigene Identität

Jeder der Tabs erlaub es, sinnvolle Werte für die spezielle Anforderung einzugeben.

Dokumenttypen

Beim ersten Start finden sich folgende Dokumenttypen:

  • Auftragsbestätigung

  • Lieferschein

  • Rechnung

  • Angebot

Übersetze diese Typen in die eigene Sprache. Du kannst ebenso neue hinzufügen und Einträge entfernen, die Du nicht benutzen wirst.

Nummernkreise

Numbercycles Nummernkreise werden zur Definition der Dokumentnummern benötigt, die auf jedem Dokument gedruckt wird. Die Dokumentnummer ist eine wichtige und eindeutige Kennzahl des Dokuments und muss Regularien gehorchen.

Verschiedene Dokumenttypen können die selben Nummernkreise verwenden um Ids aus ihnen zu generieren. Nummernkreise werden durch ihren Namen identifiziert. Benutzer können neue Nummernkreise anlegen und sie durch Klicken auf Nummernkreise bearbeiten…​ bearbeiten

Kraft unterstützt Zähler, die automatisch für jedes neue Dokument eines bestimmten Dokumenttypes erhöht werden. Zusätzlich zu dem Zähler können weitere Informationen hinzugefügt werden um eine nützliche Dokumentnummer zu erzeugen, so wie konstanten Text oder Teile des Datums.

See the following table for available variables which can be used:

%y or %yyyy

the year of the document date.

%yy

the year of the document (two digits).

%w

the week number of the document date.

%ww

the week number of the document date with leading zero.

%d

the day number of the document date.

%dd

the day number of the document date with leading zero.

%m or %M

the month number of the document date.

%MM

the month number with leading zero.

%c

the customer id from kaddressbook

%i

the unique counter (mandatory)

%type

the localised doc type (offer, invoice etc.)

%uid

the contact id of the client.

Steuern

In vielen Ländern gibt es zwei Arten von Mehrwertsteuer für veräusserte Produkte.

A high level and a low level.

Fill here the appropriate amounts in for the high level and the low level. If the tax-level is changing, then you add here the start date with the new tax-levels.

Wages

A list of wage costs is maintained in Kraft. The items are used in templates and during calculation.

All data can be edited, customized and new items can be added in the Kraft Configuration Dialog reachable through the Settings menu.

Remember that these units are later used in the documents, it is therefor important that you translate them to your own language and to fill in the correct prices.

Units of measurement

A list of units of measurement is maintained in Kraft. In Kraft Configuration Dialog reachable through the Settings menu can you edit and customize items already in the list, and also can you add new items to the list.

Remember that these units are later used in the documents, it is therefor important that you translate them to your own language.

Own identity

Check here if the information that you have given during the initial setup is correct for the use in the documents.

Warning
If you made the choice to use the information from KaddressBook then is the information from a later manual entry ignored.

After we have made some corrections to the configuration, we go back to the main window.Here we see three tabs:

  • Documents

  • Timeline

  • Catalogs

We go first to catalogs.

Catalog

In the tab catalog are two different catalogs:

Material

A catalog of material that are sold, with their purchase prices, the profit and the sell-price.

and Standard Templates

A catalog of standard recipes of work like planting trees.

Both catalogs can have chapters and sub-chapters for to organize your templates. First we are going to fill in the

Material-catalog

A catalog of material that are sold, with their purchase prices, the profit and the sell-price. First we are going to add new chapters and subchapters.

New chapters

Select with the mouse the column-name material, select now in the context-menu [Add a sub chapter]

and add an extra chapter like Trees

New sub chapters

We are going to ad sub chapters in the map Trees. Select with the mouse the name of the chapter where you like to add a subchapter, select now in the context-menu [Add a sub chapter] and ad an extra subchapters like Loaf trees and needle trees. After adding the extra chapters and subchapters for dividing the material, we are going to add the material themself.

New template

Select with the mouse the name of the sub-chapter or chapter where you like to add a material. Select the sub map Loaf trees and select now in the context-menu

Add the extra materials coconut tree, apple tree and pine-apple tree.

Fill in the price that we have paid.

Fill in the profit that we want to have on the material

And fill in how much is in a packet.

Now we are going to:

Standard Templates

This is a catalog of standard recipes of work like:

  • planting trees

  • cutting grass

  • transport costs

  • planting grass

  • sowing grass-seed

We add here the standard work of planting a tree.

Select with the mouse the name of the chapter [Work] where you like to add the new template,

select now the context-menu [New template]

and the extra templates Plant tree and cut grass.

After we made the new template, a window opens with 4 tabs:

  • Template

  • Time calculation

  • Fix costs

  • Material

First we go to the tab:

Template

We give here the name of the new standard template like Plant tree

Warning
be careful, this name is later used in the invoice

we select that this is per piece and that the margin is 8% and that the full VAT is applicable.

Time calculation

We fill here in a number of work with the time:

Table 1. Spent time

Dig hole

32 min.

worker

Place tree

12 min.

worker

Fill hole

17 min.

worker

give water

5 min.

worker

The cost for worker which we have earlier filled in is now used.

Note
in the invoice we see later only Plant tree, we will not see the parts dig hole,place tree,fill hole,give water

Now we go to the tab

Fixed costs

and fill in:

Table 2. Fixed item

Transportcost

35 euro

1 pcs.

After this we go to the tab:

Material

Here we fill in:

Table 3. Used materials

1

support pole

3,5 euro

We go now back to the first tab template

On the first tab [template], we can now see the overall cost per one unit

Click on [OK] for saving the result or on [cancel] for discarding the result.

We make a second template cut grass

we fill in cut grass, as unit we choose sm (square meter), on the second tab we fill in that we need 3 min per square meter.

Click on [OK] for saving the result or on [Cancel] for discarding the result.

We are now ready for the first invoice.

Creating Documents

The first Invoice

Open the tab documents

Click on create document

The window document [creation wizard opens].

Select in document type invoice.

Fill in on the whiteboard content a short text about what the invoice is, like: cut grass and planted tree for mister Jonson

Click on next

Select on the new window the name and address from the client.

(if the name and address is not there, click then on new contact or on edit contact if you want to edit the contact)

Click on OK.

Now opens the window document [items].

this window has 2 tabs and the 3 buttons on the top:

  • Add item…​,

  • Add discount item,

  • Show templates.

In the left tab you can see all the items that we want to place on the invoice, on the right tab we see the text from the header, the total price and the footer.

If you click on the text of the header or the footer on the right side then the window changes in such a way that you can edit the header or the footer.

Adapt the header and the footer to your situation, on the footer you can place a text: We make your garden-dream come to reality..

Click on the button Show templates.

The right tab changes and show now the earlier made templates, we select in the group Work, the subgroup Plant tree and click then on the button with the to the left pointing arrow on the bottom side.

A new window [Create Item from Template] opens.

Because we have planted 2 trees, we go to the field [insert] and change this to 2 pcs.

Click on OK for saving the result or on cancel for discarding the result.

The window close and we go back to the main window.

We click again on Show templates and select this time cut grass, we click again on the button with the arrow, in the opened window we select that the grass-field was 24 square meter.

Click on OK for saving the result or on Cancel for discarding the result.

We add now manually an item by clicking on the button Add item… and the window [create new item] opens.

Because we have delivered a special tree, we fill here in the name of the special tree liguster, at the field insert we fill in the number of the special trees that we have delivered and the price of them.

Warning
Remind that in the catalog we can add a profit on the price of the material, in the invoice and in the offer we can not add a profit on the price of the material.

We have now an invoice with 3 items.

Click on OK for saving the invoice or on Cancel for discarding the invoice.

We click on OK and save the result.

Your first invoice is now ready for sending.

In the window documents we see our first invoice, notice that this document has a document number which we can see on the left side.

On top of the window with all the invoices we see the button [Print Document], on which we click.

From the invoice will now a PDF be made which we can print on paper or send by email to the client.

After this we are going to create a offer for some work in a garden.

Creating an Offer

The client has asked to plant a tree, we will offer 3 different trees which we can plant.

Beside this, we have seen that there is a lifeless three, which we will offer to remove as extra work. Numbercycles

For the total price we do not want to show the price of the removal of the lifeless tree and we want for the total price only to show the price of one tree and not three.

Open again the tab documents.

Click on create document

The window Document Creation Wizard opens.

select in document type > Offer.

Fill in on the whiteboard content a short text about what the offer is, like: plant one tree and removal of lifeless tree

Click on next

Select on the new window the name and address from the client.

(if the name and address is not there, click then on new contact or on edit contact if you want to edit the contact)

Click on OK.

Now the window [edit document] opens.

This window has 2 tabs and the 3 buttons on the top:

  • Add item…​,

  • Add discount item,

  • Show templates.

Click on the button Show templates.

The right tab changes and show now the earlier made templates, we select in the group Work, the subgroup Plant tree and click then on the button with the to the left pointing arrow on the bottom side.

A new window [Create Item from Template] opens.

Because we want to plant 1 tree, we go to the field [insert] and keep this on 1 pcs.

Click on OK for saving the result or on Cancel for discarding the result.

The window close and we go back to the main window.

We click on the button Show templates and this time we select in catalog Material

The material-catalog opens, and we can select in the chapter trees the subchapter loaf trees in which we select the apple tree which we made earlier.

Click on we OK for saving the result or on cancel for discarding the result.

The window close and we go back to the main window.

We add now manually an item by clicking on the button Add item….

the window [create new item] opens.

We want that the client can make a choice from an apple, a pear tree and the liguster.

Therefor we are going to add also a pear tree manually.

We click on the button Add item… and the window [create new item] opens.

We fill here in the name of the tree Pear tree, at the field insert we fill in the number of the special trees that we have delivered and the price of them.

We want add this to the material catalog for future use, therefor we select also [select this item as template for future documents] and we select in trees.

Click on OK for saving the result or on Cancel for discarding the result.

We does this again but then for the liguster.

We have now 3 items with trees in the offer.

As last item we add an item with remove tree with 0,5 hour for 32 euro.

On the left side of an item we can see 2 buttons:

a button with a flag and a button with what looks like a page.

We select the upper button with the page after which opens a context-menu with the items:

[Item kind]->[Normal]
[Item kind]>[Alternative]
[Item kind]>[On demand]
[Tax]
[Move up]
[Move down]
[Lock item]
[Unlock item]
[Delete item]

We choose here [Item kind] and change for pear tree from [normal] to [alternative].

We do this also for [liguster] and for [remove tree] we change this from [normal] to [on demand].

Click on OK for saving the result or on Cancel for discarding the result.

We want to see the result and therefor we click on the button [show document].

We see now that the prize of the pear tree, the liguster and the removal of the tree is not used for the total prize. When we are happy with the result, we can click on the button close after which we click on the button Print Document for making a PDF what we can print out or send to the client.

After your first invoice is now your first offer now also ready for sending.

Customization

Kraft can be customized in most of the graphical user interface and in particular in the output it generates.

Output Document Customization

To create PDF output documents, the document data that was edited in the Kraft app is filled into a template. The template defines how the output document looks like, ie. by font settings, placing of elements and such.

The file that is assembled from data and the template is converted to PDF using a special document creation script. All that is started automatically by Kraft if a document should be printed.

Each document type in Kraft can have it’s own template that is used to create a PDF. Which one can be set in the Settings dialog for document types.

WeasyPrint Documents

With WeasyPrint Kraft uses a very powerful HTML and CSS based generator that makes it very easy to create highly customized documents which fit the users expectations. The general idea is that Weasyprint loads html output that is processed to PDF. Usually it is considering a Cascading Stylesheet file which has a huge impact on how the PDF document looks in the end.

To use a WeasyPrint based template for a document simply create a template file and save it with the extension .gtmpl. With that file extension Kraft automatically uses WeasyPrint and also the Grantlee templating for rendering.

An example for a WeasyPrint document can be found in the Kraft package in the reports directory and is called invoice.gtmpl.

To use a WeasyPrint template with one of the Kraft document types just select the template file name (with the right extension *.gtml) in the Kraft Settings Dialog.

From version 0.95 on Kraft ships with an example document in the Grantlee- and WeasyPrint format. It can be found at /usr/share/kraft/reports/invoice.gtmpl or online on Github.

To effectively change the look of the document kraft.css (on Github) needs to be considered. It defines most of the look.

Template Variables

To generate the PDF, Kraft has to transfer data from the document you have been working on in Kraft to the input document that is processed to an PDF utilising WeasyPrint. For that, Kraft uses a text template. In that, Kraft replaces variables with the actual values.

The syntax is based on the Django syntax for templates described in the the docs.

Main Application Menu

The File Menu

[File]>[Quit]
[Ctrl]+[Q]
Quits the application.

The Document Menu

[Document]>[Show Document]
[Ctrl]+[R]
Opens a window with the selected document for showing it.
[Document]>[Edit Document]
[Ctrl+O]
Opens a window with the selected document for editing it.
[Document]>[Open Archived document]
[Ctrl]+[A]
Opens an archived document.
[Document]>[Create Document]
Opens a window with a wizard for creating a new client-document.
[Document]>[Copy Document]
Makes a copy of the selected client-document to a new client-document
which can belong to an other client or an other documenttype.
[Document]>[Follow Document]
Opens the selected client-document for editing.
[Document]>[Mail document]
[Ctrl]+[M]
Mails a document.

The Settings menu

[Settings]>[Edit Tag Templates]
[Ctrl]+[E]
Opens a window where you add, edit or translate the tags (like work,
material, plants or discounts).
[Settings]>[Redo initial setup]
[Ctrl+R]
Redoes the initial setup. After this, a restart of Kraft is required.
[Settings]>[Showed toolbars]
Here you can decide if the `main toolbar` and the toolbar `Document Actions`
are shown.
[Settings]>[Configure Kraft]
[Ctrl]+[Shft]+[,]
Here you can configure Kraft.

Document Edit Window

The context Menu

 [Context]>[Item kind]
 change the status from this item between
* Normal
* Alternative
* On demand
[Context]>[Tax]
Seems not working.
[Context]>[Move up]
Moves this item a place up in document.
[Context]>[Move down]
Moves this item a place down in document.
[Context]>[Lock item]
It is not clear what is does.
[Context]>[Unlock item]
It is not clear what is does.
[Context]>[Delete item]
Removes this item from document.

Fortgeschrittene Themen

Dieses Kapitel beschreibt fortgeschrittene Themen um Kraft. Dabei wird etwas Linux-Kenntnis vorausgesetzt, und das Aufsetzen sollte von erfahreneren Linux Administratoren durchgeführt und gut testet werden.

Kraft kollaborativ verwenden

Kraft kann kollaborativ in einer verteilten Umgebung verwendet werden. Das heisst, dass mehrere Benutzer an ihren jeweiligen Arbeitsplatzrechnern mit ihrer eigenen Kraft Instanz arbeiten können und dabei die selben Daten verwenden.

Das ganze Thema ist Veränderung unterworfen, da Kraft in naher Zukunft ownCloud als private Cloud Lösung zur Datenspeicherung verwenden wird.

Datenbank und Dokumentpool teilen

Der einfachste Fall ist das zwei oder wenige mehr Kraft Instanzen die Datenbank gemeinsam benutzen und den Pool von PDF Dokumenten gemeinsam verwenden. Um es einfach zu halten werden hier zwei Instanzen beschrieben.

Ein typischer Anwendungsfall könnte sein: Zwei verschiedene Linux user möchten Kraft verwenden. Sie haben beide ihren eigenen Computer und arbeiten im selben Netzwerk. Dieses Beispiel beschreibt eine Situation mit einem Hauptbüro das Kraft im normalen Modus betreibt, und einem Notebook mit Kraft, das im NurLesen Modus um Dokumente anzusehen, Kataloge zu überprüfen und ähnliches.

Dafür müssen die folgenden Voraussetzungen erfüllt sein:

  1. Als Datenbank-Backend wird MySQL oder MariaDB verwendet. Sqlite wird nicht unterstützt.

  2. Die Datenbank ist mit dem MySQL Benutzer von beiden Rechnern aus erreichbar.

  3. Das Dokument-Speicher-Verzeichnis muss geteilt werden.

Achtung: Es gibt keinen Schutz dagegen, dass beide Benutzer das gleiche Dokument zur gleichen Zeit bearbeiten. Weil das gefährlich ist und zu unvorhersehbaren Resultaten führen kann, ist es empfohlen, Kraft in allen ausser der Haupt-Instanz im Nur-Lese Modus zu betreiben. Der Nur-Lese Modus wird mit Krafts Kommandozeilenschalter -r eingeschaltet.

Die Datenbank teilen

Der Datenbankserver sollte auf der Haupt-Maschine installiert sein, oder es sollte ein spezialisiertes Gerät wie ein NAS verwendet werden. Die Netzwerk Geschwindigkeit beeinflusst die Benutzbarkeit natürlich erheblich.

Howtos um MySQL aufzusetzen sind im Internet zu finden.

Den Dokument Pool teilen

Kraft schreibt generierte PDFs in ein lokales Verzeichnis. Welches Verzeichnis das ist kann im Kraft Konfigfile eingerichtet werden. Das Konfigfile muss auf allen Instanzen angepasst werden.

Es ist in jedem Benutzer Homeverzeichnis unter dem releativen Pfad .config/kraftrc. Es muss den folgenden Konfigurations-Wert enthalten:

[reporting]
PdfOutputDir=/data/space/kraftdoc/pdf

Es gibt verschiedene Wege wie das Verzeichnis geteilt werden kann, zum Beispiel NFS und SMB Server. Es ist entscheidend, dass beide Benutzer von beiden Maschinen die Dateien auflisten und zugreifen können. Der Hauptbenutzer braucht Schreib- und Leserecht., Nur-Lese Bentuzer brauchen nur Lesezugriff auf die Dateien.

Ein empfohlenes Setup benutzt ein NFS Share über autofs, das auf der Hauptmaschine aufgesetzt werden muss. Um Dateizugriff zu verwalten, sollte eine Gruppe aufgesetzt werden.

Kraft im Nur-Lesen Modus

Um Kraft im Nur-Lese Modus zu starten, muss das Programm mit dem Kommandozeilenschalter -r gestartet werden.

Credits und Lizenz

Software und Dokumentation Copyright 2004-2021 Klaas Freitag

Dokumentation Copyright 2020 Ronald Stroethoff

kraft-0.97/manual/kraft-en.html000066400000000000000000002254121410616450300164400ustar00rootroot00000000000000 The Kraft Handbook

Introduction

Kraft is a Qt and KDE application to organize office documents like quotes and invoices in a small business. It eases the creation of these of documents and helps with repeating tasks.

With Kraft, there is no need for fiddling with a text processor any more. Documents are created by a few clicks and archived automatically. Kraft generates high quality PDF output for printing, mailing and archiving.

Feature Overview
  • Customer management, deeply integrated in the KDE infrastructure using KAddressbook.

  • Automated creation of offers, invoices and similar documents.

  • Templates for document header- and footertexts and for document items.

  • Calculation of items.

  • Material management.

  • Configurable document creation in PDF format for print and email.

Kraft is designed to use the data entries from the KDE address book which is a module of the KDE PIM, an information management application. All addresses are collected in the KAdressBook.

The code of Kraft is open source and is released under the GNU General Public License.

Note
Kraft is driven by community of users, coders and others.
Also this manual needs contributions!

Learn more on how to contribute!

First Use and Basic Configuration

During the initial setup you are asked to select a database to use and give the address of your company.

You can fill in your company address (that appears on the printed documents) on two ways: use in the setup procedure the first tab Select from Addressbook for to select your on address in KAddressBook (if you have filled your own address in KaddressBook) or use the second tab Manual entry for to fill in the information of the address from your company by hand. This step is necessary for the correct generation of your documents.

After the initial setup, select Preferences  Settings. That allows to prepare Kraft correctly so it can be used in a useful way.

In the Preferences window we have the tabs:

*Document Defaults
*Taxes
*Documunt Types
*Wages
*Units
*Own identity

Each of the tabs allows to enter useful values for our use case.

Document Types

At the first use you find a list of different document types, such as:

  • Acceptance of order

  • Delivery receipt

  • Invoice

  • Offer

Translate this types to your own language. You can also add new ones and remove document types you wont use.

Numbercycles

Numbercycles Numbercycles are used to define the document number which is printed on every document. The document number is an important unique identifier of the document and often must follow regulations.

Different document types can use the same number cycle to generate ids from. Number cycles are identified by their name. User can create new number cycles and edit them clicking on the button Edit Number Cycles…​

Kraft supports a counter that is incremented for every new document of a certain type. In addition to the counter, more information can be added to form an useful number, such as constant text or parts of the date.

See the following table for available variables which can be used:

%y or %yyyy

the year of the document date.

%yy

the year of the document (two digits).

%w

the week number of the document date.

%ww

the week number of the document date with leading zero.

%d

the day number of the document date.

%dd

the day number of the document date with leading zero.

%m or %M

the month number of the document date.

%MM

the month number with leading zero.

%c

the customer id from kaddressbook

%i

the unique counter (mandatory)

%type

the localised doc type (offer, invoice etc.)

%uid

the contact id of the client.

Taxes

In many countries there are two kinds of VAT-taxes for sold products.

A high level and a low level.

Fill here the appropriate amounts in for the high level and the low level. If the tax-level is changing, then you add here the start date with the new tax-levels.

Wages

A list of wage costs is maintained in Kraft. The items are used in templates and during calculation.

All data can be edited, customized and new items can be added in the Kraft Configuration Dialog reachable through the Settings menu.

Remember that these units are later used in the documents, it is therefor important that you translate them to your own language and to fill in the correct prices.

Units of measurement

A list of units of measurement is maintained in Kraft. In Kraft Configuration Dialog reachable through the Settings menu can you edit and customize items already in the list, and also can you add new items to the list.

Remember that these units are later used in the documents, it is therefor important that you translate them to your own language.

Own identity

Check here if the information that you have given during the initial setup is correct for the use in the documents.

WARNING: If you made the choice to use the information from KaddressBook then is the information from a later manual entry ignored.

After we have made some corrections to the configuration, we go back to the main window.Here we see three tabs:

  • Documents

  • Timeline

  • Catalogs

We go first to catalogs.

Catalog

In the tab catalog are two different catalogs:

Material

A catalog of material that are sold, with their purchase prices, the profit and the sell-price.

and Standard Templates

A catalog of standard recipes of work like planting trees.

Both catalogs can have chapters and sub-chapters for to organize your templates. First we are going to fill in the

Material-catalog

A catalog of material that are sold, with their purchase prices, the profit and the sell-price. First we are going to add new chapters and subchapters.

New chapters

Select with the mouse the column-name material, select now in the context-menu [Add a sub chapter]

and add an extra chapter like Trees

New sub chapters

We are going to ad sub chapters in the map Trees. Select with the mouse the name of the chapter where you like to add a subchapter, select now in the context-menu [Add a sub chapter] and ad an extra subchapters like Loaf trees and needle trees. After adding the extra chapters and subchapters for dividing the material, we are going to add the material themself.

New template

Select with the mouse the name of the sub-chapter or chapter where you like to add a material. Select the sub map Loaf trees and select now in the context-menu

Add the extra materials coconut tree, apple tree and pine-apple tree.

Fill in the price that we have paid.

Fill in the profit that we want to have on the material

And fill in how much is in a packet.

Now we are going to:

Standard Templates

This is a catalog of standard recipes of work like:

  • planting trees

  • cutting grass

  • transport costs

  • planting grass

  • sowing grass-seed

We add here the standard work of planting a tree.

Select with the mouse the name of the chapter [Work] where you like to add the new template,

select now the context-menu [New template]

and the extra templates Plant tree and cut grass.

After we made the new template, a window opens with 4 tabs:

  • Template

  • Time calculation

  • Fix costs

  • Material

First we go to the tab:

Template

We give here the name of the new standard template like Plant tree

WARNING: be careful, this name is later used in the invoice

we select that this is per piece and that the margin is 8% and that the full VAT is applicable.

Time calculation

We fill here in a number of work with the time:

Table 1. Spent time

Dig hole

32 min.

worker

Place tree

12 min.

worker

Fill hole

17 min.

worker

give water

5 min.

worker

The cost for worker which we have earlier filled in is now used.

NOTE: in the invoice we see later only Plant tree, we will not see the parts dig hole,place tree,fill hole,give water

Now we go to the tab

Fixed costs

and fill in:

Table 2. Fixed item

Transportcost

35 euro

1 pcs.

After this we go to the tab:

Material

Here we fill in:

Table 3. Used materials

1

support pole

3,5 euro

We go now back to the first tab template

On the first tab [template], we can now see the overall cost per one unit

Click on [OK] for saving the result or on [cancel] for discarding the result.

We make a second template cut grass

we fill in cut grass, as unit we choose sm (square meter), on the second tab we fill in that we need 3 min per square meter.

Click on [OK] for saving the result or on [Cancel] for discarding the result.

We are now ready for the first invoice.

Creating Documents

The first Invoice

Open the tab documents

Click on create document

The window document [creation wizard opens].

Select in document type invoice.

Fill in on the whiteboard content a short text about what the invoice is, like: cut grass and planted tree for mister Jonson

Click on next

Select on the new window the name and address from the client.

(if the name and address is not there, click then on new contact or on edit contact if you want to edit the contact)

Click on OK.

Now opens the window document [items].

this window has 2 tabs and the 3 buttons on the top:

  • Add item…​,

  • Add discount item,

  • Show templates.

In the left tab you can see all the items that we want to place on the invoice, on the right tab we see the text from the header, the total price and the footer.

If you click on the text of the header or the footer on the right side then the window changes in such a way that you can edit the header or the footer.

Adapt the header and the footer to your situation, on the footer you can place a text: We make your garden-dream come to reality..

Click on the button Show templates.

The right tab changes and show now the earlier made templates, we select in the group Work, the subgroup Plant tree and click then on the button with the to the left pointing arrow on the bottom side.

A new window [Create Item from Template] opens.

Because we have planted 2 trees, we go to the field [insert] and change this to 2 pcs.

Click on OK for saving the result or on cancel for discarding the result.

The window close and we go back to the main window.

We click again on Show templates and select this time cut grass, we click again on the button with the arrow, in the opened window we select that the grass-field was 24 square meter.

Click on OK for saving the result or on Cancel for discarding the result.

We add now manually an item by clicking on the button Add item… and the window [create new item] opens.

Because we have delivered a special tree, we fill here in the name of the special tree liguster, at the field insert we fill in the number of the special trees that we have delivered and the price of them.

WARNING: Remind that in the catalog we can add a profit on the price of the material, in the invoice and in the offer we can not add a profit on the price of the material.

We have now an invoice with 3 items.

Click on OK for saving the invoice or on Cancel for discarding the invoice.

We click on OK and save the result.

Your first invoice is now ready for sending.

In the window documents we see our first invoice, notice that this document has a document number which we can see on the left side.

On top of the window with all the invoices we see the button [Print Document], on which we click.

From the invoice will now a PDF be made which we can print on paper or send by email to the client.

After this we are going to create a offer for some work in a garden.

Creating an Offer

The client has asked to plant a tree, we will offer 3 different trees which we can plant.

Beside this, we have seen that there is a lifeless three, which we will offer to remove as extra work. Numbercycles

For the total price we do not want to show the price of the removal of the lifeless tree and we want for the total price only to show the price of one tree and not three.

Open again the tab documents.

Click on create document

The window Document Creation Wizard opens.

select in document type > Offer.

Fill in on the whiteboard content a short text about what the offer is, like: plant one tree and removal of lifeless tree

Click on next

Select on the new window the name and address from the client.

(if the name and address is not there, click then on new contact or on edit contact if you want to edit the contact)

Click on OK.

Now the window [edit document] opens.

This window has 2 tabs and the 3 buttons on the top:

  • Add item…​,

  • Add discount item,

  • Show templates.

Click on the button Show templates.

The right tab changes and show now the earlier made templates, we select in the group Work, the subgroup Plant tree and click then on the button with the to the left pointing arrow on the bottom side.

A new window [Create Item from Template] opens.

Because we want to plant 1 tree, we go to the field [insert] and keep this on 1 pcs.

Click on OK for saving the result or on Cancel for discarding the result.

The window close and we go back to the main window.

We click on the button Show templates and this time we select in catalog Material

The material-catalog opens, and we can select in the chapter trees the subchapter loaf trees in which we select the apple tree which we made earlier.

Click on we OK for saving the result or on cancel for discarding the result.

The window close and we go back to the main window.

We add now manually an item by clicking on the button Add item….

the window [create new item] opens.

We want that the client can make a choice from an apple, a pear tree and the liguster.

Therefor we are going to add also a pear tree manually.

We click on the button Add item… and the window [create new item] opens.

We fill here in the name of the tree Pear tree, at the field insert we fill in the number of the special trees that we have delivered and the price of them.

We want add this to the material catalog for future use, therefor we select also [select this item as template for future documents] and we select in trees.

Click on OK for saving the result or on Cancel for discarding the result.

We does this again but then for the liguster.

We have now 3 items with trees in the offer.

As last item we add an item with remove tree with 0,5 hour for 32 euro.

On the left side of an item we can see 2 buttons:

a button with a flag and a button with what looks like a page.

We select the upper button with the page after which opens a context-menu with the items:

[Item kind]->[Normal]
[Item kind]>[Alternative]
[Item kind]>[On demand]
[Tax]
[Move up]
[Move down]
[Lock item]
[Unlock item]
[Delete item]

We choose here [Item kind] and change for pear tree from [normal] to [alternative].

We do this also for [liguster] and for [remove tree] we change this from [normal] to [on demand].

Click on OK for saving the result or on Cancel for discarding the result.

We want to see the result and therefor we click on the button [show document].

We see now that the prize of the pear tree, the liguster and the removal of the tree is not used for the total prize. When we are happy with the result, we can click on the button close after which we click on the button Print Document for making a PDF what we can print out or send to the client.

After your first invoice is now your first offer now also ready for sending.

Customization

Kraft can be customized in most of the graphical user interface and in particular in the output it generates.

Output Document Customization

To create PDF output documents, the document data that was edited in the Kraft app is filled into a template. The template defines how the output document looks like, ie. by font settings, placing of elements and such.

The file that is assembled from data and the template is converted to PDF using a special document creation script. All that is started automatically by Kraft if a document should be printed.

Each document type in Kraft can have it’s own template that is used to create a PDF. Which one can be set in the Settings dialog for document types.

WeasyPrint Documents

With WeasyPrint Kraft uses a very powerful HTML and CSS based generator that makes it very easy to create highly customized documents which fit the users expectations. The general idea is that Weasyprint loads html output that is processed to PDF. Usually it is considering a Cascading Stylesheet file which has a huge impact on how the PDF document looks in the end.

To use a WeasyPrint based template for a document simply create a template file and save it with the extension .gtmpl. With that file extension Kraft automatically uses WeasyPrint and also the Grantlee templating for rendering.

An example for a WeasyPrint document can be found in the Kraft package in the reports directory and is called invoice.gtmpl.

To use a WeasyPrint template with one of the Kraft document types just select the template file name (with the right extension *.gtml) in the Kraft Settings Dialog.

From version 0.95 on Kraft ships with an example document in the Grantlee- and WeasyPrint format. It can be found at /usr/share/kraft/reports/invoice.gtmpl or online on Github.

To effectively change the look of the document kraft.css (on Github) needs to be considered. It defines most of the look.

Template Variables

To generate the PDF, Kraft has to transfer data from the document you have been working on in Kraft to the input document that is processed to an PDF utilising WeasyPrint. For that, Kraft uses a text template. In that, Kraft replaces variables with the actual values.

The syntax is based on the Django syntax for templates described in the the docs.

Main Application Menu

The File Menu

[File]>[Quit]
[Ctrl]+[Q]
Quits the application.

The Document Menu

[Document]>[Show Document]
[Ctrl]+[R]
Opens a window with the selected document for showing it.
[Document]>[Edit Document]
[Ctrl+O]
Opens a window with the selected document for editing it.
[Document]>[Open Archived document]
[Ctrl]+[A]
Opens an archived document.
[Document]>[Create Document]
Opens a window with a wizard for creating a new client-document.
[Document]>[Copy Document]
Makes a copy of the selected client-document to a new client-document
which can belong to an other client or an other documenttype.
[Document]>[Follow Document]
Opens the selected client-document for editing.
[Document]>[Mail document]
[Ctrl]+[M]
Mails a document.

The Settings menu

[Settings]>[Edit Tag Templates]
[Ctrl]+[E]
Opens a window where you add, edit or translate the tags (like work,
material, plants or discounts).
[Settings]>[Redo initial setup]
[Ctrl+R]
Redoes the initial setup. After this, a restart of Kraft is required.
[Settings]>[Showed toolbars]
Here you can decide if the `main toolbar` and the toolbar `Document Actions`
are shown.
[Settings]>[Configure Kraft]
[Ctrl]+[Shft]+[,]
Here you can configure Kraft.

Document Edit Window

The context Menu

 [Context]>[Item kind]
 change the status from this item between
* Normal
* Alternative
* On demand
[Context]>[Tax]
Seems not working.
[Context]>[Move up]
Moves this item a place up in document.
[Context]>[Move down]
Moves this item a place down in document.
[Context]>[Lock item]
It is not clear what is does.
[Context]>[Unlock item]
It is not clear what is does.
[Context]>[Delete item]
Removes this item from document.

Advanced Topics

This chapter describes advanced topics around Kraft. Some Linux knowledge is required, and setups should be done by experienced Linux administrators and should be tested carefully.

Using Kraft Collaborative

Kraft can be used collaborative in a distributed environment. That means that multiple users work on their desktops with their own Kraft instance on the same data.

This whole topic is a subject to change, as Kraft will evolve to use ownCloud as a private cloud solution to store the data.

Sharing Database and Document Pool

The simplest case is that two or more Kraft instances use a database together and access the same pool of PDF documents on the harddisk. For simplicity this describes only two Kraft instances.

A typical use case would be: Two different Linux users want to use Kraft. They both have their own computer but only work in the same network. For example this would describe a situation with one main office machine that runs Kraft in normal mode, plus a notebook with Kraft in read only mode to view documents, check catalogs and such.

For that, the following prerequisites have to be met:

  1. MySQL or MariaDB is used as database backend. Sqlite is not supported.

  2. The database is accessible with a mysql user and from each machine for both users.

  3. The document store directory has to be shared.

WARNING: There is no protection against having both users editing the same document. Because that is dangerous and can lead to unpredictable results, it is recommended to run all instances of Kraft except the main one in read only mode. This is done by starting Kraft with the -r command line switch.

Sharing the Database

The database server should be installed on the main machine or a dedicated device like a NAS. Networking speed influences the comfort to use obviously.

Find howtos on the internet how to setup MySQL accordingly.

Sharing the Document Pool Directory

Kraft writes generated PDF documents into a local directory. Where that is can be configured in the Kraft Config file. The config file has to be adopted on all instances.

That is located in each users home directory, in the path .config/kraftrc. It has to contain the following config value:

[reporting]
PdfOutputDir=/data/space/kraftdoc/pdf

There are different ways how share that directory, ie. NFS or SMB storages. It is important that both users from both machines can list and access the files. The main user needs read and write access, read only users only need read access to the files.

A recommended setup is a NFS share with autofs which is set up on the main machine. To manage file access a certain group should be set up on the machines with which access can be managed.

Starting Kraft in read-only mode

To start Kraft in read-only mode, start the binary with the -r command line switch.

Credits and License

Program and documentation copyright 2004–2021 Klaas Freitag

Documentation copyright 2020 Ronald Stroethoff

kraft-0.97/manual/kraft.adoc000066400000000000000000000575331410616450300160110ustar00rootroot00000000000000= The Kraft Handbook :author: Ronald Stroethoff :email: :toc: :description: Kraft is a Qt program for organizing documents like quotes and invoices in a small business. :keywords: Qt;office;bookkeeping :experimental: :imagesdir: images/ :toc: include::{path}/locale/attributes.adoc[] == Introduction Kraft is a Qt and KDE application to organize office documents like quotes and invoices in a small business. It eases the creation of these of documents and helps with repeating tasks. With Kraft, there is no need for fiddling with a text processor any more. Documents are created by a few clicks and archived automatically. Kraft generates high quality PDF output for printing, mailing and archiving. Feature Overview:: * Customer management, deeply integrated in the KDE infrastructure using KAddressbook. * Automated creation of offers, invoices and similar documents. * Templates for document header- and footertexts and for document items. * Calculation of items. * Material management. * Configurable document creation in PDF format for print and email. Kraft is designed to use the data entries from the KDE address book which is a module of the https://community.kde.org/KDE_PIM[KDE PIM], an information management application. All addresses are collected in the https://userbase.kde.org/KAddressBook[KAdressBook]. The code of Kraft is open source and is released under the https://en.wikipedia.org/wiki/GNU_General_Public_License[GNU General Public License]. NOTE: Kraft is driven by community of users, coders and others. + Also this manual needs contributions! + + Learn more on https://github.com/dragotin/kraft/blob/master/manual/Readme.md[how to contribute]! == First Use and Basic Configuration During the initial setup you are asked to select a database to use and give the address of your company. You can fill in your company address (that appears on the printed documents) on two ways: use in the setup procedure the first tab Select from Addressbook for to select your on address in KAddressBook (if you have filled your own address in KaddressBook) or use the second tab Manual entry for to fill in the information of the address from your company by hand. This step is necessary for the correct generation of your documents. After the initial setup, select menu:Preferences[Settings]. That allows to prepare Kraft correctly so it can be used in a useful way. In the Preferences window we have the tabs: *Document Defaults *Taxes *Documunt Types *Wages *Units *Own identity Each of the tabs allows to enter useful values for our use case. === Document Types At the first use you find a list of different document types, such as: * Acceptance of order * Delivery receipt * Invoice * Offer Translate this types to your own language. You can also add new ones and remove document types you wont use. ==== Numbercycles image:numbercycles.png[Numbercycles,float="right"] Numbercycles are used to define the *document number* which is printed on every document. The document number is an important unique identifier of the document and often must follow regulations. Different document types can use the same number cycle to generate ids from. Number cycles are identified by their name. User can create new number cycles and edit them clicking on the button btn:[Edit Number Cycles...] Kraft supports a counter that is incremented for every new document of a certain type. In addition to the counter, more information can be added to form an useful number, such as constant text or parts of the date. See the following table for available variables which can be used: |=== | `%y` or `%yyyy` | the year of the document date. | `%yy` | the year of the document (two digits). | `%w` | the week number of the document date. | `%ww` | the week number of the document date with leading zero. | `%d` | the day number of the document date. | `%dd` | the day number of the document date with leading zero. | `%m` or `%M` | the month number of the document date. | `%MM` | the month number with leading zero. | `%c` | the customer id from kaddressbook | `%i` | the unique counter *(mandatory)* | `%type` | the localised doc type (offer, invoice etc.) | `%uid` | the contact id of the client. |=== === Taxes In many countries there are two kinds of VAT-taxes for sold products. A high level and a low level. Fill here the appropriate amounts in for the high level and the low level. If the tax-level is changing, then you add here the start date with the new tax-levels. === Wages A list of wage costs is maintained in Kraft. The items are used in templates and during calculation. All data can be edited, customized and new items can be added in the Kraft Configuration Dialog reachable through the Settings menu. Remember that these units are later used in the documents, it is therefor important that you translate them to your own language and to fill in the correct prices. === Units of measurement A list of units of measurement is maintained in Kraft. In Kraft Configuration Dialog reachable through the Settings menu can you edit and customize items already in the list, and also can you add new items to the list. Remember that these units are later used in the documents, it is therefor important that you translate them to your own language. === Own identity Check here if the information that you have given during the initial setup is correct for the use in the documents. ____ WARNING: If you made the choice to use the information from KaddressBook then is the information from a later manual entry ignored. ____ After we have made some corrections to the configuration, we go back to the main window.Here we see three tabs: * Documents * Timeline * Catalogs We go first to catalogs. === Catalog In the tab catalog are two different catalogs: `Material` A catalog of material that are sold, with their purchase prices, the profit and the sell-price. and `Standard Templates` A catalog of standard recipes of work like planting trees. Both catalogs can have chapters and sub-chapters for to organize your templates. First we are going to fill in the ==== Material-catalog A catalog of material that are sold, with their purchase prices, the profit and the sell-price. First we are going to add new chapters and subchapters. ===== New chapters Select with the mouse the column-name `material`, select now in the context-menu [Add a sub chapter] and add an extra chapter like `Trees` ===== New sub chapters We are going to ad sub chapters in the map `Trees`. Select with the mouse the name of the chapter where you like to add a subchapter, select now in the context-menu [Add a sub chapter] and ad an extra subchapters like `Loaf trees` and `needle trees`. After adding the extra chapters and subchapters for dividing the material, we are going to add the material themself. ===== New template Select with the mouse the name of the sub-chapter or chapter where you like to add a material. Select the sub map Loaf trees and select now in the context-menu [Add a template] Add the extra materials `coconut tree`, `apple tree` and `pine-apple tree`. Fill in the price that we have paid. Fill in the profit that we want to have on the material And fill in how much is in a packet. Now we are going to: ==== Standard Templates This is a catalog of standard recipes of work like: * planting trees * cutting grass * transport costs * planting grass * sowing grass-seed We add here the standard work of planting a tree. Select with the mouse the name of the chapter [Work] where you like to add the new template, select now the context-menu [New template] and the extra templates `Plant tree` and `cut grass`. After we made the new template, a window opens with 4 tabs: * Template * Time calculation * Fix costs * Material First we go to the tab: ===== Template We give here the name of the new standard template like `Plant tree` ____ WARNING: be careful, this name is later used in the invoice ____ we select that this is per piece and that the margin is 8% and that the full VAT is applicable. ===== Time calculation We fill here in a number of work with the time: .Spent time [cols=",,",] |=== |Dig hole |32 min. |worker |Place tree |12 min. |worker |Fill hole |17 min. |worker |give water |5 min. |worker |=== The cost for worker which we have earlier filled in is now used. ____ NOTE: in the invoice we see later only Plant tree, we will not see the parts dig hole,place tree,fill hole,give water ____ Now we go to the tab ===== Fixed costs and fill in: .Fixed item [cols=",,",] |=== |Transportcost |35 euro |1 pcs. |=== After this we go to the tab: ===== Material Here we fill in: .Used materials [cols=",,",] |=== |1 |support pole |3,5 euro |=== We go now back to the first tab template On the first tab [template], we can now see the overall cost per one unit Click on [OK] for saving the result or on [cancel] for discarding the result. We make a second template `cut grass` we fill in `cut grass`, as unit we choose sm (square meter), on the second tab we fill in that we need 3 min per square meter. Click on [OK] for saving the result or on [Cancel] for discarding the result. We are now ready for the first invoice. [[Invoice]] == Creating Documents === The first Invoice Open the tab btn:[documents] Click on btn:[create document] The window document [creation wizard opens]. Select in document type `invoice`. Fill in on the whiteboard content a short text about what the invoice is, like: `cut grass and planted tree for mister Jonson` Click on btn:[next] Select on the new window the name and address from the client. (if the name and address is not there, click then on btn:[new contact] or on btn:[edit contact] if you want to edit the contact) Click on btn:[OK]. Now opens the window document [items]. this window has 2 tabs and the 3 buttons on the top: * btn:[Add item...], * btn:[Add discount item], * btn:[Show templates]. In the left tab you can see all the items that we want to place on the invoice, on the right tab we see the text from the header, the total price and the footer. If you click on the text of the header or the footer on the right side then the window changes in such a way that you can edit the header or the footer. Adapt the header and the footer to your situation, on the footer you can place a text: `We make your garden-dream come to reality.`. Click on the button btn:[Show templates]. The right tab changes and show now the earlier made templates, we select in the group Work, the subgroup Plant tree and click then on the button with the to the left pointing arrow on the bottom side. A new window [Create Item from Template] opens. Because we have planted 2 trees, we go to the field [insert] and change this to 2 pcs. Click on btn:[OK] for saving the result or on btn:[cancel] for discarding the result. The window close and we go back to the main window. We click again on btn:[Show templates] and select this time `cut grass`, we click again on the button with the arrow, in the opened window we select that the grass-field was 24 square meter. Click on btn:[OK] for saving the result or on btn:[Cancel] for discarding the result. We add now manually an item by clicking on the button btn:[Add item…] and the window [create new item] opens. Because we have delivered a special tree, we fill here in the name of the special tree `liguster`, at the field insert we fill in the number of the special trees that we have delivered and the price of them. ____ WARNING: Remind that in the catalog we can add a profit on the price of the material, in the invoice and in the offer we can not add a profit on the price of the material. ____ We have now an invoice with 3 items. Click on btn:[OK] for saving the invoice or on btn:[Cancel] for discarding the invoice. We click on btn:[OK] and save the result. Your first invoice is now ready for sending. In the window documents we see our first invoice, notice that this document has a document number which we can see on the left side. On top of the window with all the invoices we see the button [Print Document], on which we click. From the invoice will now a PDF be made which we can print on paper or send by email to the client. After this we are going to create a offer for some work in a garden. [[Offer]] === Creating an Offer The client has asked to plant a tree, we will offer 3 different trees which we can plant. Beside this, we have seen that there is a lifeless three, which we will offer to remove as extra work. image:create_new_doc.png[Numbercycles,float="right"] For the total price we do not want to show the price of the removal of the lifeless tree and we want for the total price only to show the price of one tree and not three. Open again the tab btn:[documents]. Click on btn:[create document] The window _Document Creation Wizard_ opens. select in btn:[document type] > btn:[Offer]. Fill in on the whiteboard content a short text about what the offer is, like: `plant one tree and removal of lifeless tree` Click on btn:[next] Select on the new window the name and address from the client. (if the name and address is not there, click then on btn:[new contact] or on btn:[edit contact] if you want to edit the contact) Click on btn:[OK]. Now the window [edit document] opens. This window has 2 tabs and the 3 buttons on the top: * btn:[Add item...], * btn:[Add discount item], * btn:[Show templates]. Click on the button btn:[Show templates]. The right tab changes and show now the earlier made templates, we select in the group `Work`, the subgroup `Plant tree` and click then on the button with the to the left pointing arrow on the bottom side. A new window [Create Item from Template] opens. Because we want to plant 1 tree, we go to the field [insert] and keep this on 1 pcs. Click on btn:[OK] for saving the result or on btn:[Cancel] for discarding the result. The window close and we go back to the main window. We click on the button btn:[Show templates] and this time we select in catalog Material The material-catalog opens, and we can select in the chapter `trees` the subchapter `loaf trees` in which we select the `apple tree` which we made earlier. Click on we btn:[OK] for saving the result or on btn:[cancel] for discarding the result. The window close and we go back to the main window. We add now manually an item by clicking on the button `Add item…`. the window [create new item] opens. We want that the client can make a choice from an apple, a pear tree and the liguster. Therefor we are going to add also a pear tree manually. We click on the button btn:[Add item…] and the window [create new item] opens. We fill here in the name of the tree `Pear tree`, at the field insert we fill in the number of the special trees that we have delivered and the price of them. We want add this to the material catalog for future use, therefor we select also [select this item as template for future documents] and we select in [save in chapter]`trees`. Click on btn:[OK] for saving the result or on btn:[Cancel] for discarding the result. We does this again but then for the liguster. We have now 3 items with trees in the offer. As last item we add an item with `remove tree` with 0,5 hour for 32 euro. On the left side of an item we can see 2 buttons: a button with a flag and a button with what looks like a page. We select the upper button with the page after which opens a context-menu with the items: [Item kind]->[Normal] [Item kind]>[Alternative] [Item kind]>[On demand] [Tax] [Move up] [Move down] [Lock item] [Unlock item] [Delete item] We choose here [Item kind] and change for `pear tree` from [normal] to [alternative]. We do this also for [liguster] and for [remove tree] we change this from [normal] to [on demand]. Click on btn:[OK] for saving the result or on btn:[Cancel] for discarding the result. We want to see the result and therefor we click on the button [show document]. We see now that the prize of the pear tree, the liguster and the removal of the tree is not used for the total prize. When we are happy with the result, we can click on the button btn:[close] after which we click on the button btn:[Print Document] for making a PDF what we can print out or send to the client. After your first invoice is now your first offer now also ready for sending. [[Customization]] == Customization Kraft can be customized in most of the graphical user interface and in particular in the output it generates. === Output Document Customization To create PDF output documents, the document data that was edited in the Kraft app is filled into a template. The template defines how the output document looks like, ie. by font settings, placing of elements and such. The file that is assembled from data and the template is converted to PDF using a special document creation script. All that is started automatically by Kraft if a document should be printed. Each document type in Kraft can have it's own template that is used to create a PDF. Which one can be set in the Settings dialog for document types. ==== WeasyPrint Documents With https://weasyprint.org[WeasyPrint] Kraft uses a very powerful HTML and CSS based generator that makes it very easy to create highly customized documents which fit the users expectations. The general idea is that Weasyprint loads html output that is processed to PDF. Usually it is considering a Cascading Stylesheet file which has a huge impact on how the PDF document looks in the end. To use a WeasyPrint based template for a document simply create a template file and save it with the extension *.gtmpl*. With that file extension Kraft automatically uses WeasyPrint and also the Grantlee templating for rendering. An example for a WeasyPrint document can be found in the Kraft package in the reports directory and is called invoice.gtmpl. To use a WeasyPrint template with one of the Kraft document types just select the template file name (with the right extension `*.gtml`) in the Kraft Settings Dialog. From version 0.95 on Kraft ships with an example document in the Grantlee- and WeasyPrint format. It can be found at `/usr/share/kraft/reports/invoice.gtmpl` or https://github.com/dragotin/kraft/blob/master/reports/invoice.gtmpl[online on Github]. To effectively change the look of the document `kraft.css` (https://github.com/dragotin/kraft/blob/master/reports/kraft.css[on Github]) needs to be considered. It defines most of the look. ==== Template Variables To generate the PDF, Kraft has to transfer data from the document you have been working on in Kraft to the input document that is processed to an PDF utilising WeasyPrint. For that, Kraft uses a text template. In that, Kraft replaces variables with the actual values. The syntax is based on the Django syntax for templates described in the https://docs.djangoproject.com/en/3.1/topics/templates/[the docs]. [[Menu]] == Menus and Shortcuts === Main Application Menu [[File]] ==== The File Menu [File]>[Quit] [Ctrl]+[Q] Quits the application. [[Document]] ==== The Document Menu [[Show_document]] [Document]>[Show Document] [Ctrl]+[R] Opens a window with the selected document for showing it. [[Edit_document]] [Document]>[Edit Document] [Ctrl+O] Opens a window with the selected document for editing it. [[Open_document]] [Document]>[Open Archived document] [Ctrl]+[A] Opens an archived document. [[Create_document]] [Document]>[Create Document] Opens a window with a wizard for creating a new client-document. [[Copy_document]] [Document]>[Copy Document] Makes a copy of the selected client-document to a new client-document which can belong to an other client or an other documenttype. [[Follow_document]] [Document]>[Follow Document] Opens the selected client-document for editing. [[Print_document]] [Document]>[Print document] Makes a PDf from the selected client-document for to be mailed or printed. [[Mail_document]] [Document]>[Mail document] [Ctrl]+[M] Mails a document. [[settings]] ==== The Settings menu [[edit_template]] [Settings]>[Edit Tag Templates] [Ctrl]+[E] Opens a window where you add, edit or translate the tags (like work, material, plants or discounts). [[redo]] [Settings]>[Redo initial setup] [Ctrl+R] Redoes the initial setup. After this, a restart of Kraft is required. [[toolbars]] [Settings]>[Showed toolbars] Here you can decide if the `main toolbar` and the toolbar `Document Actions` are shown. [[configure]] [Settings]>[Configure Kraft] [Ctrl]+[Shft]+[,] Here you can configure Kraft. === Document Edit Window [[context]] ==== The context Menu [Context]>[Item kind] change the status from this item between * Normal * Alternative * On demand [[Tax]] [Context]>[Tax] Seems not working. [[Move_up]] [Context]>[Move up] Moves this item a place up in document. [[Move_down]] [Context]>[Move down] Moves this item a place down in document. [[Lock_item]] [Context]>[Lock item] It is not clear what is does. [[Unlock_item]] [Context]>[Unlock item] It is not clear what is does. [[Delete_item]] [Context]>[Delete item] Removes this item from document. [[AdvancedTopics]] == Advanced Topics This chapter describes advanced topics around Kraft. Some Linux knowledge is required, and setups should be done by experienced Linux administrators and should be tested carefully. === Using Kraft Collaborative Kraft can be used collaborative in a distributed environment. That means that multiple users work on their desktops with their own Kraft instance on the same data. This whole topic is a subject to change, as Kraft will evolve to use ownCloud as a private cloud solution to store the data. ==== Sharing Database and Document Pool The simplest case is that two or more Kraft instances use a database together and access the same pool of PDF documents on the harddisk. For simplicity this describes only two Kraft instances. A typical use case would be: Two different Linux users want to use Kraft. They both have their own computer but only work in the same network. For example this would describe a situation with one main office machine that runs Kraft in normal mode, plus a notebook with Kraft in read only mode to view documents, check catalogs and such. For that, the following prerequisites have to be met: 1. MySQL or MariaDB is used as database backend. Sqlite is not supported. 2. The database is accessible with a mysql user and from each machine for both users. 3. The document store directory has to be shared. ____ WARNING: There is no protection against having both users editing the same document. Because that is dangerous and can lead to unpredictable results, it is recommended to run all instances of Kraft except the main one in read only mode. This is done by starting Kraft with the `-r` command line switch. ____ **Sharing the Database** The database server should be installed on the main machine or a dedicated device like a NAS. Networking speed influences the comfort to use obviously. Find howtos on the internet how to setup MySQL accordingly. **Sharing the Document Pool Directory** Kraft writes generated PDF documents into a local directory. Where that is can be configured in the Kraft Config file. The config file has to be adopted on all instances. That is located in each users home directory, in the path `.config/kraftrc`. It has to contain the following config value: ``` [reporting] PdfOutputDir=/data/space/kraftdoc/pdf ``` There are different ways how share that directory, ie. NFS or SMB storages. It is important that both users from both machines can list and access the files. The main user needs read and write access, read only users only need read access to the files. A recommended setup is a NFS share with autofs which is set up on the main machine. To manage file access a certain group should be set up on the machines with which access can be managed. **Starting Kraft in read-only mode** To start Kraft in read-only mode, start the binary with the `-r` command line switch. [[Credits]] == Credits and License Program and documentation copyright 2004–2021 Klaas Freitag Documentation copyright 2020 Ronald Stroethoff kraft-0.97/manual/kraftmanual.css000066400000000000000000001037561410616450300170700ustar00rootroot00000000000000@import url(http://cdnjs.cloudflare.com/ajax/libs/font-awesome/3.2.0/css/font-awesome.css); /* normalize.css v2.1.1 | MIT License | git.io/normalize */ /* ========================================================================== HTML5 display definitions ========================================================================== */ /** Correct `block` display not defined in IE 8/9. */ article, aside, details, figcaption, figure, footer, header, hgroup, main, nav, section, summary { display: block; } /** Correct `inline-block` display not defined in IE 8/9. */ audio, canvas, video { display: inline-block; } /** Prevent modern browsers from displaying `audio` without controls. Remove excess height in iOS 5 devices. */ audio:not([controls]) { display: none; height: 0; } /** Address styling not present in IE 8/9. */ [hidden] { display: none; } /* ========================================================================== Base ========================================================================== */ /** 1. Prevent system color scheme's background color being used in Firefox, IE, and Opera. 2. Prevent system color scheme's text color being used in Firefox, IE, and Opera. 3. Set default font family to sans-serif. 4. Prevent iOS text size adjust after orientation change, without disabling user zoom. */ html { background: #fff; /* 1 */ color: #000; /* 2 */ font-family: sans-serif; /* 3 */ -ms-text-size-adjust: 100%; /* 4 */ -webkit-text-size-adjust: 100%; /* 4 */ } /** Remove default margin. */ body { margin: 0; } /* ========================================================================== Links ========================================================================== */ /** Address `outline` inconsistency between Chrome and other browsers. */ a:focus { outline: thin dotted; } /** Improve readability when focused and also mouse hovered in all browsers. */ a:active, a:hover { outline: 0; } /* ========================================================================== Typography ========================================================================== */ /** Address variable `h1` font-size and margin within `section` and `article` contexts in Firefox 4+, Safari 5, and Chrome. */ h1 { font-size: 2em; margin: 0.67em 0; } /** Address styling not present in IE 8/9, Safari 5, and Chrome. */ abbr[title] { border-bottom: 1px dotted; } /** Address style set to `bolder` in Firefox 4+, Safari 5, and Chrome. */ b, strong { font-weight: bold; } /** Address styling not present in Safari 5 and Chrome. */ dfn { font-style: italic; } /** Address differences between Firefox and other browsers. */ hr { -moz-box-sizing: content-box; box-sizing: content-box; height: 0; } /** Address styling not present in IE 8/9. */ mark { background: #ff0; color: #000; } /** Correct font family set oddly in Safari 5 and Chrome. */ code, kbd, pre, samp { font-family: monospace, serif; font-size: 1em; } /** Improve readability of pre-formatted text in all browsers. */ pre { white-space: pre-wrap; } /** Set consistent quote types. */ q { quotes: "\201C" "\201D" "\2018" "\2019"; } /** Address inconsistent and variable font size in all browsers. */ small { font-size: 80%; } /** Prevent `sub` and `sup` affecting `line-height` in all browsers. */ sub, sup { font-size: 75%; line-height: 0; position: relative; vertical-align: baseline; } sup { top: -0.5em; } sub { bottom: -0.25em; } /* ========================================================================== Embedded content ========================================================================== */ /** Remove border when inside `a` element in IE 8/9. */ img { border: 0; } /** Correct overflow displayed oddly in IE 9. */ svg:not(:root) { overflow: hidden; } /* ========================================================================== Figures ========================================================================== */ /** Address margin not present in IE 8/9 and Safari 5. */ figure { margin: 0; } /* ========================================================================== Forms ========================================================================== */ /** Define consistent border, margin, and padding. */ fieldset { border: 1px solid #c0c0c0; margin: 0 2px; padding: 0.35em 0.625em 0.75em; } /** 1. Correct `color` not being inherited in IE 8/9. 2. Remove padding so people aren't caught out if they zero out fieldsets. */ legend { border: 0; /* 1 */ padding: 0; /* 2 */ } /** 1. Correct font family not being inherited in all browsers. 2. Correct font size not being inherited in all browsers. 3. Address margins set differently in Firefox 4+, Safari 5, and Chrome. */ button, input, select, textarea { font-family: inherit; /* 1 */ font-size: 100%; /* 2 */ margin: 0; /* 3 */ } /** Address Firefox 4+ setting `line-height` on `input` using `!important` in the UA stylesheet. */ button, input { line-height: normal; } /** Address inconsistent `text-transform` inheritance for `button` and `select`. All other form control elements do not inherit `text-transform` values. Correct `button` style inheritance in Chrome, Safari 5+, and IE 8+. Correct `select` style inheritance in Firefox 4+ and Opera. */ button, select { text-transform: none; } /** 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` and `video` controls. 2. Correct inability to style clickable `input` types in iOS. 3. Improve usability and consistency of cursor style between image-type `input` and others. */ button, html input[type="button"], input[type="reset"], input[type="submit"] { -webkit-appearance: button; /* 2 */ cursor: pointer; /* 3 */ } /** Re-set default cursor for disabled elements. */ button[disabled], html input[disabled] { cursor: default; } /** 1. Address box sizing set to `content-box` in IE 8/9. 2. Remove excess padding in IE 8/9. */ input[type="checkbox"], input[type="radio"] { box-sizing: border-box; /* 1 */ padding: 0; /* 2 */ } /** 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome. 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome (include `-moz` to future-proof). */ input[type="search"] { -webkit-appearance: textfield; /* 1 */ -moz-box-sizing: content-box; -webkit-box-sizing: content-box; /* 2 */ box-sizing: content-box; } /** Remove inner padding and search cancel button in Safari 5 and Chrome on OS X. */ input[type="search"]::-webkit-search-cancel-button, input[type="search"]::-webkit-search-decoration { -webkit-appearance: none; } /** Remove inner padding and border in Firefox 4+. */ button::-moz-focus-inner, input::-moz-focus-inner { border: 0; padding: 0; } /** 1. Remove default vertical scrollbar in IE 8/9. 2. Improve readability and alignment in all browsers. */ textarea { overflow: auto; /* 1 */ vertical-align: top; /* 2 */ } /* ========================================================================== Tables ========================================================================== */ /** Remove most spacing between table cells. */ table { border-collapse: collapse; border-spacing: 0; } *, *:before, *:after { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; } html, body { font-size: 100%; } body { background: white; color: #222222; padding: 0; margin: 0; font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; font-weight: normal; font-style: normal; line-height: 1; position: relative; cursor: auto; } a:hover { cursor: pointer; } a:focus { outline: none; } img, object, embed { max-width: 100%; height: auto; } object, embed { height: 100%; } img { -ms-interpolation-mode: bicubic; } #map_canvas img, #map_canvas embed, #map_canvas object, .map_canvas img, .map_canvas embed, .map_canvas object { max-width: none !important; } .left { float: left !important; } .right { float: right !important; } .text-left { text-align: left !important; } .text-right { text-align: right !important; } .text-center { text-align: center !important; } .text-justify { text-align: justify !important; } .hide { display: none; } .antialiased, body { -webkit-font-smoothing: antialiased; } img { display: inline-block; vertical-align: middle; } textarea { height: auto; min-height: 50px; } select { width: 100%; } p.lead, .paragraph.lead > p, #preamble > .sectionbody > .paragraph:first-of-type p { font-size: 1.21875em; line-height: 1.6; } .subheader, #content #toctitle, .admonitionblock td.content > .title, .exampleblock > .title, .imageblock > .title, .videoblock > .title, .listingblock > .title, .literalblock > .title, .openblock > .title, .paragraph > .title, .quoteblock > .title, .sidebarblock > .title, .tableblock > .title, .verseblock > .title, .dlist > .title, .olist > .title, .ulist > .title, .qlist > .title, .hdlist > .title, .tableblock > caption { line-height: 1.4; color: #777777; font-weight: 300; margin-top: 0.2em; margin-bottom: 0.5em; } /* Typography resets */ div, dl, dt, dd, ul, ol, li, h1, h2, h3, #toctitle, .sidebarblock > .content > .title, h4, h5, h6, pre, form, p, blockquote, th, td { margin: 0; padding: 0; direction: ltr; } /* Default Link Styles */ a { color: #417b06; text-decoration: none; line-height: inherit; } a:hover, a:focus { color: #417b06; } a img { border: none; } /* Default paragraph styles */ p { font-family: inherit; font-weight: normal; font-size: 1em; line-height: 1.6; margin-bottom: 1.25em; text-rendering: optimizeLegibility; } p aside { font-size: 0.875em; line-height: 1.35; font-style: italic; } /* Default header styles */ h1, h2, h3, #toctitle, .sidebarblock > .content > .title, h4, h5, h6 { font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; font-weight: normal; font-style: normal; color: #417b06; text-rendering: optimizeLegibility; margin-top: 1em; margin-bottom: 0.5em; line-height: 1.2125em; } h1 small, h2 small, h3 small, #toctitle small, .sidebarblock > .content > .title small, h4 small, h5 small, h6 small { font-size: 60%; color: gray; line-height: 0; } h1 { font-size: 2.125em; } h2 { font-size: 1.6875em; } h3, #toctitle, .sidebarblock > .content > .title { font-size: 1.375em; } h4 { font-size: 1.125em; } h5 { font-size: 1.125em; } h6 { font-size: 1em; } hr { border: dotted #cccccc; border-width: 1px 0 0; clear: both; margin: 1.25em 0 1.1875em; height: 0; } /* Helpful Typography Defaults */ em, i { font-style: italic; line-height: inherit; } strong, b { font-weight: bold; line-height: inherit; } small { font-size: 60%; line-height: inherit; } code { font-family: Consolas, "Liberation Mono", Courier, monospace; font-weight: bold; color: #417b06; } /* Lists */ ul, ol, dl { font-size: 1em; line-height: 1.6; margin-bottom: 1.25em; list-style-position: outside; font-family: inherit; } ul, ol { margin-left: 1.5em; } /* Unordered Lists */ ul li ul, ul li ol { margin-left: 1.25em; margin-bottom: 0; font-size: 1em; /* Override nested font-size change */ } ul.square li ul, ul.circle li ul, ul.disc li ul { list-style: inherit; } ul.square { list-style-type: square; } ul.circle { list-style-type: circle; } ul.disc { list-style-type: disc; } ul.no-bullet { list-style: none; } /* Ordered Lists */ ol li ul, ol li ol { margin-left: 1.25em; margin-bottom: 0; } /* Definition Lists */ dl dt { margin-bottom: 0.3125em; font-weight: bold; } dl dd { margin-bottom: 1.25em; } /* Abbreviations */ abbr, acronym { text-transform: uppercase; font-size: 90%; color: #555555; border-bottom: 1px dotted #dddddd; cursor: help; } abbr { text-transform: none; } /* Blockquotes */ blockquote { margin: 0 0 1.25em; padding: 0.5625em 1.25em 0 1.1875em; border-left: 1px solid #efefef; } blockquote cite { display: block; font-size: 0.8125em; color: #5e5e5e; } blockquote cite:before { content: "\2014 \0020"; } blockquote cite a, blockquote cite a:visited { color: #5e5e5e; } blockquote, blockquote p { line-height: 1.6; color: #777777; } /* Microformats */ .vcard { display: inline-block; margin: 0 0 1.25em 0; border: 1px solid #dddddd; padding: 0.625em 0.75em; } .vcard li { margin: 0; display: block; } .vcard .fn { font-weight: bold; font-size: 0.9375em; } .vevent .summary { font-weight: bold; } .vevent abbr { cursor: auto; text-decoration: none; font-weight: bold; border: none; padding: 0 0.0625em; } @media only screen and (min-width: 768px) { h1, h2, h3, #toctitle, .sidebarblock > .content > .title, h4, h5, h6 { line-height: 1.4; } h1 { font-size: 2.75em; } h2 { font-size: 2.3125em; } h3, #toctitle, .sidebarblock > .content > .title { font-size: 1.6875em; } h4 { font-size: 1.4375em; } } /* Print styles. Inlined to avoid required HTTP connection: www.phpied.com/delay-loading-your-print-css/ Credit to Paul Irish and HTML5 Boilerplate (html5boilerplate.com) */ .print-only { display: none !important; } @media print { * { background: transparent !important; color: #000 !important; /* Black prints faster: h5bp.com/s */ box-shadow: none !important; text-shadow: none !important; } a, a:visited { text-decoration: underline; } a[href]:after { content: " (" attr(href) ")"; } abbr[title]:after { content: " (" attr(title) ")"; } .ir a:after, a[href^="javascript:"]:after, a[href^="#"]:after { content: ""; } pre, blockquote { border: 1px solid #999; page-break-inside: avoid; } thead { display: table-header-group; /* h5bp.com/t */ } tr, img { page-break-inside: avoid; } img { max-width: 100% !important; } @page { margin: 0.5cm; } p, h2, h3, #toctitle, .sidebarblock > .content > .title { orphans: 3; widows: 3; } h2, h3, #toctitle, .sidebarblock > .content > .title { page-break-after: avoid; } .hide-on-print { display: none !important; } .print-only { display: block !important; } .hide-for-print { display: none !important; } .show-for-print { display: inherit !important; } } /* Tables */ table { background: white; margin-bottom: 1.25em; border: solid 1px #dddddd; } table thead, table tfoot { background: whitesmoke; font-weight: normal; } table thead tr th, table thead tr td, table tfoot tr th, table tfoot tr td { padding: 0.5em 0.625em 0.625em; font-size: inherit; color: #333333; text-align: left; } table tr th, table tr td { padding: 0.5625em 0.625em; font-size: inherit; color: #555555; } table tr.even, table tr.alt, table tr:nth-of-type(even) { background: #f9f9f9; } table thead tr th, table tfoot tr th, table tbody tr td, table tr td, table tfoot tr td { display: table-cell; line-height: 1.4; } .clearfix:before, .clearfix:after, .float-group:before, .float-group:after { content: " "; display: table; } .clearfix:after, .float-group:after { clear: both; } *:not(pre) > code { font-size: inherit; padding: 0; white-space: nowrap; background-color: inherit; border: 0 solid #dddddd; -webkit-border-radius: 0; border-radius: 0; text-shadow: none; } pre, pre > code { line-height: 1.4; color: black; font-family: monospace, serif; font-weight: normal; } kbd.keyseq { color: #888888; } kbd:not(.keyseq) { display: inline-block; color: #555555; font-size: 0.75em; line-height: 1.4; background-color: #F7F7F7; border: 1px solid #ccc; -webkit-border-radius: 3px; border-radius: 3px; -webkit-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.2), 0 0 0 2px white inset; box-shadow: 0 1px 0 rgba(0, 0, 0, 0.2), 0 0 0 2px white inset; margin: -0.15em 0.15em 0 0.15em; padding: 0.2em 0.6em 0.2em 0.5em; vertical-align: middle; white-space: nowrap; } kbd kbd:first-child { margin-left: 0; } kbd kbd:last-child { margin-right: 0; } .menuseq, .menu { color: #3b3b3b; } #header, #content, #footnotes, #footer { width: 100%; margin-left: auto; margin-right: auto; margin-top: 0; margin-bottom: 0; max-width: 62.5em; *zoom: 1; position: relative; padding-left: 0.9375em; padding-right: 0.9375em; } #header:before, #header:after, #content:before, #content:after, #footnotes:before, #footnotes:after, #footer:before, #footer:after { content: " "; display: table; } #header:after, #content:after, #footnotes:after, #footer:after { clear: both; } #header { margin-bottom: 2.5em; } #header > h1 { color: #417b06; font-weight: bold; border-bottom: 1px dotted #cccccc; margin-bottom: -28px; padding-bottom: 32px; } #header span { color: #777777; } #header #revnumber { text-transform: capitalize; } #header br { display: none; } #header br + span { padding-left: 3px; } #header br + span:before { content: "\2013 \0020"; } #header br + span.author { padding-left: 0; } #header br + span.author:before { content: ", "; } #toc { border-bottom: 1px solid #cccccc; padding-bottom: 1.25em; } #toc > ul { margin-left: 0.25em; } #toc ul.sectlevel0 > li > a { font-style: italic; } #toc ul.sectlevel0 ul.sectlevel1 { margin-left: 0; margin-top: 0.5em; margin-bottom: 0.5em; } #toc ul { list-style-type: none; } #toctitle { color: #777777; } @media only screen and (min-width: 1280px) { body.toc2 { padding-left: 20em; } #toc.toc2 { position: fixed; width: 20em; left: 0; top: 0; border-right: 1px solid #cccccc; border-bottom: 0; z-index: 1000; padding: 1em; height: 100%; overflow: auto; } #toc.toc2 #toctitle { margin-top: 0; } #toc.toc2 > ul { font-size: .95em; } #toc.toc2 ul ul { margin-left: 0; padding-left: 1.25em; } #toc.toc2 ul.sectlevel0 ul.sectlevel1 { padding-left: 0; margin-top: 0.5em; margin-bottom: 0.5em; } body.toc2.toc-right { padding-left: 0; padding-right: 20em; } body.toc2.toc-right #toc.toc2 { border-right: 0; border-left: 1px solid #cccccc; left: auto; right: 0; } } #content #toc { border-style: solid; border-width: 1px; border-color: #d9d9d9; margin-bottom: 1.25em; padding: 1.25em; background: #f2f2f2; border-width: 0; -webkit-border-radius: 0; border-radius: 0; } #content #toc > :first-child { margin-top: 0; } #content #toc > :last-child { margin-bottom: 0; } #content #toc a { text-decoration: none; } #content #toctitle { font-weight: bold; font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; font-size: 1em; padding-left: 0.125em; } #footer { max-width: 100%; background-color: #272727; padding: 1.25em; } #footer-text { color: #bcbcbc; line-height: 1.44; } .sect1 { padding-bottom: 1.25em; } .sect1 + .sect1 { border-top: 1px solid #cccccc; } #content h1 > a.anchor, h2 > a.anchor, h3 > a.anchor, #toctitle > a.anchor, .sidebarblock > .content > .title > a.anchor, h4 > a.anchor, h5 > a.anchor, h6 > a.anchor { position: absolute; width: 1em; margin-left: -1em; display: block; text-decoration: none; visibility: hidden; text-align: center; font-weight: normal; } #content h1 > a.anchor:before, h2 > a.anchor:before, h3 > a.anchor:before, #toctitle > a.anchor:before, .sidebarblock > .content > .title > a.anchor:before, h4 > a.anchor:before, h5 > a.anchor:before, h6 > a.anchor:before { content: '\00A7'; font-size: .85em; vertical-align: text-top; display: block; margin-top: 0.05em; } #content h1:hover > a.anchor, #content h1 > a.anchor:hover, h2:hover > a.anchor, h2 > a.anchor:hover, h3:hover > a.anchor, #toctitle:hover > a.anchor, .sidebarblock > .content > .title:hover > a.anchor, h3 > a.anchor:hover, #toctitle > a.anchor:hover, .sidebarblock > .content > .title > a.anchor:hover, h4:hover > a.anchor, h4 > a.anchor:hover, h5:hover > a.anchor, h5 > a.anchor:hover, h6:hover > a.anchor, h6 > a.anchor:hover { visibility: visible; } #content h1 > a.link, h2 > a.link, h3 > a.link, #toctitle > a.link, .sidebarblock > .content > .title > a.link, h4 > a.link, h5 > a.link, h6 > a.link { color: #333333; text-decoration: none; } #content h1 > a.link:hover, h2 > a.link:hover, h3 > a.link:hover, #toctitle > a.link:hover, .sidebarblock > .content > .title > a.link:hover, h4 > a.link:hover, h5 > a.link:hover, h6 > a.link:hover { color: #262626; } .imageblock, .literalblock, .listingblock, .verseblock, .videoblock { margin-bottom: 1.25em; } .admonitionblock td.content > .title, .exampleblock > .title, .imageblock > .title, .videoblock > .title, .listingblock > .title, .literalblock > .title, .openblock > .title, .paragraph > .title, .quoteblock > .title, .sidebarblock > .title, .tableblock > .title, .verseblock > .title, .dlist > .title, .olist > .title, .ulist > .title, .qlist > .title, .hdlist > .title { text-align: left; font-weight: bold; } .tableblock > caption { text-align: left; font-weight: bold; white-space: nowrap; overflow: visible; max-width: 0; } table.tableblock #preamble > .sectionbody > .paragraph:first-of-type p { font-size: inherit; } .admonitionblock > table { border: 0; background: none; width: 100%; } .admonitionblock > table td.icon { text-align: center; width: 80px; } .admonitionblock > table td.icon img { max-width: none; } .admonitionblock > table td.icon .title { font-weight: bold; text-transform: uppercase; } .admonitionblock > table td.content { padding-left: 1.125em; padding-right: 1.25em; border-left: 1px dotted #cccccc; color: #777777; } .admonitionblock > table td.content > :last-child > :last-child { margin-bottom: 0; } .exampleblock > .content { border-style: solid; border-width: 1px; border-color: #e6e6e6; margin-bottom: 1.25em; padding: 1.25em; background: white; -webkit-border-radius: 0; border-radius: 0; } .exampleblock > .content > :first-child { margin-top: 0; } .exampleblock > .content > :last-child { margin-bottom: 0; } .exampleblock > .content h1, .exampleblock > .content h2, .exampleblock > .content h3, .exampleblock > .content #toctitle, .sidebarblock.exampleblock > .content > .title, .exampleblock > .content h4, .exampleblock > .content h5, .exampleblock > .content h6, .exampleblock > .content p { color: #555555; } .exampleblock > .content h1, .exampleblock > .content h2, .exampleblock > .content h3, .exampleblock > .content #toctitle, .sidebarblock.exampleblock > .content > .title, .exampleblock > .content h4, .exampleblock > .content h5, .exampleblock > .content h6 { line-height: 1; margin-bottom: 0.625em; } .exampleblock > .content h1.subheader, .exampleblock > .content h2.subheader, .exampleblock > .content h3.subheader, .exampleblock > .content .subheader#toctitle, .sidebarblock.exampleblock > .content > .subheader.title, .exampleblock > .content h4.subheader, .exampleblock > .content h5.subheader, .exampleblock > .content h6.subheader { line-height: 1.4; } .exampleblock.result > .content { -webkit-box-shadow: 0 1px 8px #d9d9d9; box-shadow: 0 1px 8px #d9d9d9; } .sidebarblock { border-style: solid; border-width: 1px; border-color: #d9d9d9; margin-bottom: 1.25em; padding: 1.25em; background: #f2f2f2; -webkit-border-radius: 0; border-radius: 0; } .sidebarblock > :first-child { margin-top: 0; } .sidebarblock > :last-child { margin-bottom: 0; } .sidebarblock h1, .sidebarblock h2, .sidebarblock h3, .sidebarblock #toctitle, .sidebarblock > .content > .title, .sidebarblock h4, .sidebarblock h5, .sidebarblock h6, .sidebarblock p { color: #555555; } .sidebarblock h1, .sidebarblock h2, .sidebarblock h3, .sidebarblock #toctitle, .sidebarblock > .content > .title, .sidebarblock h4, .sidebarblock h5, .sidebarblock h6 { line-height: 1; margin-bottom: 0.625em; } .sidebarblock h1.subheader, .sidebarblock h2.subheader, .sidebarblock h3.subheader, .sidebarblock .subheader#toctitle, .sidebarblock > .content > .subheader.title, .sidebarblock h4.subheader, .sidebarblock h5.subheader, .sidebarblock h6.subheader { line-height: 1.4; } .sidebarblock > .content > .title { color: #777777; margin-top: 0; line-height: 1.6; } .exampleblock > .content > :last-child > :last-child, .exampleblock > .content .olist > ol > li:last-child > :last-child, .exampleblock > .content .ulist > ul > li:last-child > :last-child, .exampleblock > .content .qlist > ol > li:last-child > :last-child, .sidebarblock > .content > :last-child > :last-child, .sidebarblock > .content .olist > ol > li:last-child > :last-child, .sidebarblock > .content .ulist > ul > li:last-child > :last-child, .sidebarblock > .content .qlist > ol > li:last-child > :last-child { margin-bottom: 0; } .literalblock > .content pre, .listingblock > .content pre { background: #efefef; border-width: 1px; border-style: solid; border-color: #cccccc; -webkit-border-radius: 0; border-radius: 0; padding: 0.75em 0.75em 0.625em 0.75em; word-wrap: break-word; } .literalblock > .content pre.nowrap, .listingblock > .content pre.nowrap { overflow-x: auto; white-space: pre; word-wrap: normal; } .literalblock > .content pre > code, .listingblock > .content pre > code { display: block; } @media only screen { .literalblock > .content pre, .listingblock > .content pre { font-size: 0.72em; } } @media only screen and (min-width: 768px) { .literalblock > .content pre, .listingblock > .content pre { font-size: 0.81em; } } @media only screen and (min-width: 1280px) { .literalblock > .content pre, .listingblock > .content pre { font-size: 0.9em; } } .listingblock > .content { position: relative; } .listingblock:hover code[class*=" language-"]:before { text-transform: uppercase; font-size: 0.9em; color: #999; position: absolute; top: 0.375em; right: 0.375em; } .listingblock:hover code.asciidoc:before { content: "asciidoc"; } .listingblock:hover code.clojure:before { content: "clojure"; } .listingblock:hover code.css:before { content: "css"; } .listingblock:hover code.groovy:before { content: "groovy"; } .listingblock:hover code.html:before { content: "html"; } .listingblock:hover code.java:before { content: "java"; } .listingblock:hover code.javascript:before { content: "javascript"; } .listingblock:hover code.python:before { content: "python"; } .listingblock:hover code.ruby:before { content: "ruby"; } .listingblock:hover code.scss:before { content: "scss"; } .listingblock:hover code.xml:before { content: "xml"; } .listingblock:hover code.yaml:before { content: "yaml"; } .listingblock.terminal pre .command:before { content: attr(data-prompt); padding-right: 0.5em; color: #999; } .listingblock.terminal pre .command:not([data-prompt]):before { content: '$'; } table.pyhltable { border: 0; margin-bottom: 0; } table.pyhltable td { vertical-align: top; padding-top: 0; padding-bottom: 0; } table.pyhltable td.code { padding-left: .75em; padding-right: 0; } .highlight.pygments .lineno, table.pyhltable td:not(.code) { color: #999; padding-left: 0; padding-right: .5em; border-right: 1px solid #cccccc; } .highlight.pygments .lineno { display: inline-block; margin-right: .25em; } table.pyhltable .linenodiv { background-color: transparent !important; padding-right: 0 !important; } .quoteblock { margin: 0 0 1.25em; padding: 0.5625em 1.25em 0 1.1875em; border-left: 1px solid #efefef; } .quoteblock blockquote { margin: 0 0 1.25em 0; padding: 0 0 0.5625em 0; border: 0; } .quoteblock blockquote > .paragraph:last-child p { margin-bottom: 0; } .quoteblock .attribution { margin-top: -.25em; padding-bottom: 0.5625em; font-size: 0.8125em; color: #5e5e5e; } .quoteblock .attribution br { display: none; } .quoteblock .attribution cite { display: block; margin-bottom: 0.625em; } table thead th, table tfoot th { font-weight: normal; } table.tableblock.grid-all { border-collapse: separate; border-spacing: 1px; -webkit-border-radius: 0; border-radius: 0; border-top: 1px solid #dddddd; border-bottom: 1px solid #dddddd; } table.tableblock.frame-topbot, table.tableblock.frame-none { border-left: 0; border-right: 0; } table.tableblock.frame-sides, table.tableblock.frame-none { border-top: 0; border-bottom: 0; } table.tableblock td .paragraph:last-child p, table.tableblock td > p:last-child { margin-bottom: 0; } th.tableblock.halign-left, td.tableblock.halign-left { text-align: left; } th.tableblock.halign-right, td.tableblock.halign-right { text-align: right; } th.tableblock.halign-center, td.tableblock.halign-center { text-align: center; } th.tableblock.valign-top, td.tableblock.valign-top { vertical-align: top; } th.tableblock.valign-bottom, td.tableblock.valign-bottom { vertical-align: bottom; } th.tableblock.valign-middle, td.tableblock.valign-middle { vertical-align: middle; } p.tableblock.header { color: #333333; font-weight: normal; } td > div.verse { white-space: pre; } ol { margin-left: 1.75em; } ul li ol { margin-left: 1.5em; } dl dd { margin-left: 1.125em; } dl dd:last-child, dl dd:last-child > :last-child { margin-bottom: 0; } ol > li p, ul > li p, ul dd, ol dd, .olist .olist, .ulist .ulist, .ulist .olist, .olist .ulist { margin-bottom: 0.625em; } ul.unstyled, ol.unnumbered, ul.checklist, ul.none { list-style-type: none; } ul.unstyled, ol.unnumbered, ul.checklist { margin-left: 0.625em; } ul.checklist li > p:first-child > i[class^="icon-check"]:first-child, ul.checklist li > p:first-child > input[type="checkbox"]:first-child { margin-right: 0.25em; } ul.checklist li > p:first-child > input[type="checkbox"]:first-child { position: relative; top: 1px; } ul.inline { margin: 0 auto 0.625em auto; margin-left: -1.375em; margin-right: 0; padding: 0; list-style: none; overflow: hidden; } ul.inline > li { list-style: none; float: left; margin-left: 1.375em; display: block; } ul.inline > li > * { display: block; } .unstyled dl dt { font-weight: normal; font-style: normal; } ol.arabic { list-style-type: decimal; } ol.decimal { list-style-type: decimal-leading-zero; } ol.loweralpha { list-style-type: lower-alpha; } ol.upperalpha { list-style-type: upper-alpha; } ol.lowerroman { list-style-type: lower-roman; } ol.upperroman { list-style-type: upper-roman; } ol.lowergreek { list-style-type: lower-greek; } .hdlist > table, .colist > table { border: 0; background: none; } .hdlist > table > tbody > tr, .colist > table > tbody > tr { background: none; } td.hdlist1 { padding-right: .8em; font-weight: bold; } td.hdlist1, td.hdlist2 { vertical-align: top; } .literalblock + .colist, .listingblock + .colist { margin-top: -0.5em; } .colist > table tr > td:first-of-type { padding: 0 .8em; line-height: 1; } .colist > table tr > td:last-of-type { padding: 0.25em 0; } .qanda > ol > li > p > em:only-child { color: #cde62c; } .thumb, .th { line-height: 0; display: inline-block; border: solid 4px white; -webkit-box-shadow: 0 0 0 1px #dddddd; box-shadow: 0 0 0 1px #dddddd; } .imageblock.left, .imageblock[style*="float: left"] { margin: 0.25em 0.625em 1.25em 0; } .imageblock.right, .imageblock[style*="float: right"] { margin: 0.25em 0 1.25em 0.625em; } .imageblock > .title { margin-bottom: 0; } .imageblock.thumb, .imageblock.th { border-width: 6px; } .imageblock.thumb > .title, .imageblock.th > .title { padding: 0 0.125em; } .image.left, .image.right { margin-top: 0.25em; margin-bottom: 0.25em; display: inline-block; line-height: 0; } .image.left { margin-right: 0.625em; } .image.right { margin-left: 0.625em; } a.image { text-decoration: none; } span.footnote, span.footnoteref { vertical-align: super; font-size: 0.875em; } span.footnote a, span.footnoteref a { text-decoration: none; } #footnotes { padding-top: 0.75em; padding-bottom: 0.75em; margin-bottom: 0.625em; } #footnotes hr { width: 20%; min-width: 6.25em; margin: -.25em 0 .75em 0; border-width: 1px 0 0 0; } #footnotes .footnote { padding: 0 0.375em; line-height: 1.3; font-size: 0.875em; margin-left: 1.2em; text-indent: -1.2em; margin-bottom: .2em; } #footnotes .footnote a:first-of-type { font-weight: bold; text-decoration: none; } #footnotes .footnote:last-of-type { margin-bottom: 0; } #content #footnotes { margin-top: -0.625em; margin-bottom: 0; padding: 0.75em 0; } .gist .file-data > table { border: none; background: #fff; width: 100%; margin-bottom: 0; } .gist .file-data > table td.line-data { width: 99%; } div.unbreakable { page-break-inside: avoid; } .big { font-size: larger; } .small { font-size: smaller; } .underline { text-decoration: underline; } .overline { text-decoration: overline; } .line-through { text-decoration: line-through; } .aqua { color: #00bfbf; } .aqua-background { background-color: #00fafa; } .black { color: black; } .black-background { background-color: black; } .blue { color: #0000bf; } .blue-background { background-color: #0000fa; } .fuchsia { color: #bf00bf; } .fuchsia-background { background-color: #fa00fa; } .gray { color: #606060; } .gray-background { background-color: #7d7d7d; } .green { color: #006000; } .green-background { background-color: #007d00; } .lime { color: #00bf00; } .lime-background { background-color: #00fa00; } .maroon { color: #600000; } .maroon-background { background-color: #7d0000; } .navy { color: #000060; } .navy-background { background-color: #00007d; } .olive { color: #606000; } .olive-background { background-color: #7d7d00; } .purple { color: #600060; } .purple-background { background-color: #7d007d; } .red { color: #bf0000; } .red-background { background-color: #fa0000; } .silver { color: #909090; } .silver-background { background-color: #bcbcbc; } .teal { color: #006060; } .teal-background { background-color: #007d7d; } .white { color: #bfbfbf; } .white-background { background-color: #fafafa; } .yellow { color: #bfbf00; } .yellow-background { background-color: #fafa00; } span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; } .admonitionblock td.icon [class^="icon-"]:before { font-size: 2.5em; text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5); cursor: default; } .admonitionblock td.icon .icon-note:before { content: "\f05a"; color: #d2e943; color: #b1c918; } .admonitionblock td.icon .icon-tip:before { content: "\f0eb"; text-shadow: 1px 1px 2px rgba(155, 155, 0, 0.8); color: #111; } .admonitionblock td.icon .icon-warning:before { content: "\f071"; color: #bf6900; } .admonitionblock td.icon .icon-caution:before { content: "\f06d"; color: #bf3400; } .admonitionblock td.icon .icon-important:before { content: "\f06a"; color: #bf0000; } .conum { display: inline-block; color: white !important; background-color: #555555; -webkit-border-radius: 100px; border-radius: 100px; text-align: center; width: 20px; height: 20px; font-size: 12px; font-weight: bold; line-height: 20px; font-family: Arial, sans-serif; font-style: normal; position: relative; top: -2px; letter-spacing: -1px; } .conum * { color: white !important; } .conum + b { display: none; } .conum:after { content: attr(data-value); } .conum:not([data-value]):empty { display: none; } #header > h1 { border-bottom-style: solid; } #toctitle { color: #333333; } .listingblock > .content > pre, .literalblock > .content > pre { background: -moz-linear-gradient(top, white 0%, #f4f4f4 100%); background: -webkit-linear-gradient(top, white 0%, #f4f4f4 100%); background: linear-gradient(to bottom, #ffffff 0%, #f4f4f4 100%); -webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.15); box-shadow: 0 2px 5px rgba(0, 0, 0, 0.15); } .sidebarblock { background: none; border: none; -webkit-box-shadow: 0 0 5px rgba(118, 137, 4, 0.5); box-shadow: 0 0 5px rgba(118, 137, 4, 0.5); } .sidebarblock > .content > .title { color: #181818; } kraft-0.97/manual/locale/000077500000000000000000000000001410616450300152745ustar00rootroot00000000000000kraft-0.97/manual/locale/attributes-de.adoc000066400000000000000000000013161410616450300207010ustar00rootroot00000000000000// German translation, courtesy of Florian Wilhelm :appendix-caption: Anhang :appendix-refsig: {appendix-caption} :caution-caption: Achtung :chapter-label: Kapitel :chapter-refsig: {chapter-label} :example-caption: Beispiel :figure-caption: Abbildung :important-caption: Wichtig :last-update-label: Zuletzt aktualisiert ifdef::listing-caption[:listing-caption: Listing] ifdef::manname-title[:manname-title: Bezeichnung] :note-caption: Anmerkung :part-label: Teil :part-refsig: {part-label} ifdef::preface-title[:preface-title: Vorwort] :section-refsig: Abschnitt :table-caption: Tabelle :tip-caption: Hinweis :toc-title: Inhaltsverzeichnis :untitled-label: Ohne Titel :version-label: Version :warning-caption: Warnung kraft-0.97/manual/locale/attributes-en.adoc000066400000000000000000000013161410616450300207130ustar00rootroot00000000000000// English translation, for reference only; matches the built-in behavior of core :appendix-caption: Appendix :appendix-refsig: {appendix-caption} :caution-caption: Caution :chapter-label: Chapter :chapter-refsig: {chapter-label} :example-caption: Example :figure-caption: Figure :important-caption: Important :last-update-label: Last updated ifdef::listing-caption[:listing-caption: Listing] ifdef::manname-title[:manname-title: Name] :note-caption: Note :part-label: Part :part-refsig: {part-label} ifdef::preface-title[:preface-title: Preface] :section-refsig: Section :table-caption: Table :tip-caption: Tip :toc-title: Table of Contents :untitled-label: Untitled :version-label: Version :warning-caption: Warning kraft-0.97/manual/locale/attributes.adoc000066400000000000000000000013751410616450300203200ustar00rootroot00000000000000// This directory provides translations for all built-in attributes in Asciidoctor that emit translatable strings. // See http://asciidoctor.org/docs/user-manual/#customizing-labels to learn how to apply this file. // // If you're introducing a new translation, create a file named attributes-.adoc, where is the IANA subtag for the language. // Next, assign a translation for each attribute, using attributes-en.adoc as a reference. // // IMPORTANT: Do not include any blank lines in the transation file. // // NOTE: Please wrap the listing-caption and preface-title entries in a preprocessor conditional directive. // These attributes should only be updated if set explicitly by the user. ifdef::lang[include::attributes-{lang}.adoc[]] kraft-0.97/manual/makeman.sh000077500000000000000000000031651410616450300160120ustar00rootroot00000000000000#!/bin/bash # set -euxo pipefail # FIXME: Check if the tools ascidoctor and po4a-translate are installed # FIXME: Be compatible with other ascii doc generators # ===================================================================== showhelp() { echo "Usage: $0 " >&2 echo echo " Creates the Kraft manual and its translations." echo " asciidoctor and po4trans need to be installed." echo "" echo " To read the source from elsewhere, the script" echo " takes a source directory as argument" echo "" echo " Output happens to the current directory." exit 1 } # ===================================================================== srcdir="${1:-.}" srcfile="${srcdir}/kraft.adoc" version="1.0" outfile="kraft-en.html" if [ -n "$1" ] && [ "$1" == "-h" ]; then showhelp fi echo "Building $srcfile in ${srcdir}" outdir=`pwd` pushd "${srcdir}" lang=en asciidocargs="-D ${outdir} -a path=${srcdir} -a VERSION=${version} -a lang=${lang} -a stylesheet=kraftmanual.css" # english master doc asciidoctor ${asciidocargs} -o ${outfile} ${srcfile} echo "built ${outfile}" # build the internationalized versions languages="de" for lang in ${languages} do transsrc="${srcdir}/po/kraft-${lang}.po" if [ -f "${transsrc}" ]; then po4a-translate -f asciidoc -M utf-8 -m ${srcfile} -p ${transsrc} -k 0 -l kraft-${lang}.adoc outfile="kraft-${lang}.html" asciidoctor ${asciidocargs} -o ${outfile} kraft-${lang}.adoc rm kraft-${lang}.adoc echo "built ${transsrc} to ${outfile}" else echo "File ${transsrc} does not exist!" fi done popd kraft-0.97/manual/po/000077500000000000000000000000001410616450300144535ustar00rootroot00000000000000kraft-0.97/manual/po/kraft-de.po000066400000000000000000001344431410616450300165210ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE # Copyright (C) YEAR Free Software Foundation, Inc. # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "POT-Creation-Date: 2021-06-12 15:30+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Language: de_DE\n" "X-Source-Language: en\n" #. type: Title = #: kraft.adoc:1 #, no-wrap msgid "The Kraft Handbook" msgstr "Das Kraft Handbuch" #. type: Title == #: kraft.adoc:14 #, no-wrap msgid "Introduction" msgstr "Einführung" #. type: Plain text #: kraft.adoc:17 msgid "" "Kraft is a Qt and KDE application to organize office documents like quotes " "and invoices in a small business. It eases the creation of these of " "documents and helps with repeating tasks." msgstr "" "Kraft ist ein Qt- und KDE-Programm zum Organisieren von Bürodokumenten wie " "Angeboten und Rechnungen in kleinen Unternehmen. Es erleichtert die " "Erstellung der Dokument und vereinfacht wiederkehrende Aufgaben." #. type: Plain text #: kraft.adoc:19 msgid "" "With Kraft, there is no need for fiddling with a text processor any more. " "Documents are created by a few clicks and archived automatically. Kraft " "generates high quality PDF output for printing, mailing and archiving." msgstr "" "Mit Kraft wird kein Textbearbeitungsprogramm mehr benötigt. Rechnungs- und " "Angebotsdokumente werden mit ein paar Klicks erstellt und automatisch " "abgelegt. Kraft generiert hochqualitative PDF Dokumente für Druck, EMail und " "Archivierung." #. type: Labeled list #: kraft.adoc:20 #, no-wrap msgid "Feature Overview" msgstr "Funktionen" #. type: Plain text #: kraft.adoc:23 msgid "" "Customer management, deeply integrated in the KDE infrastructure using " "KAddressbook." msgstr "" "Kundenverwaltung, durch die Verwendung von KAdressbuch tief in die KDE " "Infrastruktur integriert." #. type: Plain text #: kraft.adoc:24 msgid "Automated creation of offers, invoices and similar documents." msgstr "" "Unterstütztes Erstellen von Angeboten, Rechnungen und ähnlichen Dokumenten." #. type: Plain text #: kraft.adoc:25 msgid "Templates for document header- and footertexts and for document items." msgstr "Vorlagen für Dokument-Kopf- und Fusstexte sowie Dokumentposten." #. type: Plain text #: kraft.adoc:26 msgid "Calculation of items." msgstr "Kalkulation von einzelnen Posten." #. type: Plain text #: kraft.adoc:27 msgid "Material management." msgstr "Materialverwaltung." #. type: Plain text #: kraft.adoc:28 msgid "Configurable document creation in PDF format for print and email." msgstr "" "Konfigurierbare Dokument-Erstellung im PDF Format zum Druck und Versenden " "per Email." #. type: Plain text #: kraft.adoc:32 msgid "" "Kraft is designed to use the data entries from the KDE address book which is " "a module of the https://community.kde.org/KDE_PIM[KDE PIM], an information " "management application. All addresses are collected in the " "https://userbase.kde.org/KAddressBook[KAdressBook]." msgstr "" "Kraft arbeitet mit den Adressdaten aus dem KDE Adressbuch, das ein Modul von " "https://community.kde.org/KDE_PIM[KDE PIM], einer " "Informations-Management-Anwendung ist. Alle Adressen werden in der Anwendung " "https://userbase.kde.org/KAddressBook[KAdressBook] gesammelt." #. type: Plain text #: kraft.adoc:34 msgid "" "The code of Kraft is open source and is released under the " "https://en.wikipedia.org/wiki/GNU_General_Public_License[GNU General Public " "License]." msgstr "" "Der Quelltext zu Kraft ist open source and wird unter der Lizenz " "https://en.wikipedia.org/wiki/GNU_General_Public_License[GNU General Public " "License] veröffentlicht." #. type: Plain text #: kraft.adoc:39 #, no-wrap msgid "" "Kraft is driven by community of users, coders and others. +\n" "Also this manual needs contributions! +\n" " +\n" " Learn more on https://github.com/dragotin/kraft/blob/master/manual/Readme.md[how to contribute]!\n" msgstr "" "Kraft wird durch eine Gemeinschaft von Benutzern, Entwicklern und anderen vorangetrieben. +\n" "Dieses Benutzerhandbuch benötigt Ihre Hilfe zur Verbesserung. +\n" " +\n" "Lernen Sie mehr auf https://github.com/dragotin/kraft/blob/master/manual/Readme.md[how to contribute]!\n" #. type: Title == #: kraft.adoc:41 #, no-wrap msgid "First Use and Basic Configuration" msgstr "Erster Start und Grundkonfiguration" #. type: Plain text #: kraft.adoc:44 msgid "" "During the initial setup you are asked to select a database to use and give " "the address of your company." msgstr "" "Während des initialen Setup wird der Datenbank-Typ und die Adresse der Firma " "abgefragt." #. type: Plain text #: kraft.adoc:46 msgid "" "You can fill in your company address (that appears on the printed documents) " "on two ways: use in the setup procedure the first tab Select from " "Addressbook for to select your on address in KAddressBook (if you have " "filled your own address in KaddressBook) or use the second tab Manual entry " "for to fill in the information of the address from your company by hand. " "This step is necessary for the correct generation of your documents." msgstr "" "Die Adresse der eigenen Firma, die automatisch auf dem Ausgabedokument " "erscheint, kann auf zwei Arten eingegeben werden: In der Prozedur nach dem " "ersten Start im entsprechenden Tab, oder später über den Einstellungsdialog. " "Die Adresse kann entweder aus dem Adressbuch gewählt werden oder per Hand " "eingegeben werden. Dieser Schritt ist für die fehlerfreie Erstellung der " "Dokumente nötig." #. type: Plain text #: kraft.adoc:49 msgid "" "After the initial setup, select menu:Preferences[Settings]. That allows to " "prepare Kraft correctly so it can be used in a useful way." msgstr "" "Nach dem initialen Setup kann Kraft über das Menü Preferences[Settings] " "konfiguriert werden. Das erlaubt, Kraft für die eigenen Bedürfnisse " "anzupassen." #. type: Plain text #: kraft.adoc:51 msgid "In the Preferences window we have the tabs:" msgstr "Im Einstellungsfenster befinden sich die Tabs:" #. type: Plain text #: kraft.adoc:58 #, no-wrap msgid "" " *Document Defaults\n" " *Taxes\n" " *Documunt Types\n" " *Wages\n" " *Units\n" " *Own identity\n" msgstr "" " *Dokument Voreinstellungen\n" " *Steuern\n" " *Dokumenttypen\n" " *Stundensätze\n" " *Einheiten\n" " *die eigene Identität\n" #. type: Plain text #: kraft.adoc:60 msgid "Each of the tabs allows to enter useful values for our use case." msgstr "" "Jeder der Tabs erlaub es, sinnvolle Werte für die spezielle Anforderung " "einzugeben." #. type: Title === #: kraft.adoc:61 #, no-wrap msgid "Document Types" msgstr "Dokumenttypen" #. type: Plain text #: kraft.adoc:64 msgid "At the first use you find a list of different document types, such as:" msgstr "Beim ersten Start finden sich folgende Dokumenttypen:" #. type: Plain text #: kraft.adoc:66 msgid "Acceptance of order" msgstr "Auftragsbestätigung" #. type: Plain text #: kraft.adoc:67 msgid "Delivery receipt" msgstr "Lieferschein" #. type: Plain text #: kraft.adoc:68 msgid "Invoice" msgstr "Rechnung" #. type: Plain text #: kraft.adoc:69 msgid "Offer" msgstr "Angebot" #. type: Plain text #: kraft.adoc:72 msgid "" "Translate this types to your own language. You can also add new ones and " "remove document types you wont use." msgstr "" "Übersetze diese Typen in die eigene Sprache. Du kannst ebenso neue " "hinzufügen und Einträge entfernen, die Du nicht benutzen wirst." #. type: Title ==== #: kraft.adoc:73 #, no-wrap msgid "Numbercycles" msgstr "Nummernkreise" #. type: Plain text #: kraft.adoc:78 msgid "" "image:numbercycles.png[Numbercycles,float=\"right\"] Numbercycles are used " "to define the *document number* which is printed on every document. The " "document number is an important unique identifier of the document and often " "must follow regulations." msgstr "" "image:numbercycles.png[Numbercycles,float=\"right\"] Nummernkreise werden " "zur Definition der *Dokumentnummern* benötigt, die auf jedem Dokument " "gedruckt wird. Die Dokumentnummer ist eine wichtige und eindeutige Kennzahl " "des Dokuments und muss Regularien gehorchen." #. type: Plain text #: kraft.adoc:82 msgid "" "Different document types can use the same number cycle to generate ids from. " " Number cycles are identified by their name. User can create new number " "cycles and edit them clicking on the button btn:[Edit Number Cycles...]" msgstr "" "Verschiedene Dokumenttypen können die selben Nummernkreise verwenden um Ids " "aus ihnen zu generieren. Nummernkreise werden durch ihren Namen " "identifiziert. Benutzer können neue Nummernkreise anlegen und sie durch " "Klicken auf btn:[Nummernkreise bearbeiten...] bearbeiten" #. type: Plain text #: kraft.adoc:85 msgid "" "Kraft supports a counter that is incremented for every new document of a " "certain type. In addition to the counter, more information can be added to " "form an useful number, such as constant text or parts of the date." msgstr "" "Kraft unterstützt Zähler, die automatisch für jedes neue Dokument eines " "bestimmten Dokumenttypes erhöht werden. Zusätzlich zu dem Zähler können " "weitere Informationen hinzugefügt werden um eine nützliche Dokumentnummer zu " "erzeugen, so wie konstanten Text oder Teile des Datums." #. type: Plain text #: kraft.adoc:87 msgid "See the following table for available variables which can be used:" msgstr "" #. type: Table #: kraft.adoc:103 #, no-wrap msgid "" "| `%y` or `%yyyy` | the year of the document date.\n" "| `%yy` | the year of the document (two digits).\n" "| `%w` | the week number of the document date.\n" "| `%ww` | the week number of the document date with leading zero.\n" "| `%d` | the day number of the document date.\n" "| `%dd` | the day number of the document date with leading zero.\n" "| `%m` or `%M` | the month number of the document date.\n" "| `%MM` | the month number with leading zero.\n" "| `%c` | the customer id from kaddressbook\n" "| `%i` | the unique counter *(mandatory)*\n" "| `%type` | the localised doc type (offer, invoice etc.)\n" "| `%uid` | the contact id of the client.\n" "\n" msgstr "" #. type: Title === #: kraft.adoc:105 #, no-wrap msgid "Taxes" msgstr "Steuern" #. type: Plain text #: kraft.adoc:108 msgid "In many countries there are two kinds of VAT-taxes for sold products." msgstr "" "In vielen Ländern gibt es zwei Arten von Mehrwertsteuer für veräusserte " "Produkte." #. type: Plain text #: kraft.adoc:111 msgid "A high level and a low level." msgstr "" #. type: Plain text #: kraft.adoc:114 msgid "" "Fill here the appropriate amounts in for the high level and the low level. " "If the tax-level is changing, then you add here the start date with the new " "tax-levels." msgstr "" #. type: Title === #: kraft.adoc:115 #, no-wrap msgid "Wages" msgstr "" #. type: Plain text #: kraft.adoc:119 msgid "" "A list of wage costs is maintained in Kraft. The items are used in templates " "and during calculation." msgstr "" #. type: Plain text #: kraft.adoc:122 msgid "" "All data can be edited, customized and new items can be added in the Kraft " "Configuration Dialog reachable through the Settings menu." msgstr "" #. type: Plain text #: kraft.adoc:126 msgid "" "Remember that these units are later used in the documents, it is therefor " "important that you translate them to your own language and to fill in the " "correct prices." msgstr "" #. type: Title === #: kraft.adoc:127 #, no-wrap msgid "Units of measurement" msgstr "" #. type: Plain text #: kraft.adoc:131 msgid "" "A list of units of measurement is maintained in Kraft. In Kraft " "Configuration Dialog reachable through the Settings menu can you edit and " "customize items already in the list, and also can you add new items to the " "list." msgstr "" #. type: Plain text #: kraft.adoc:134 msgid "" "Remember that these units are later used in the documents, it is therefor " "important that you translate them to your own language." msgstr "" #. type: Title === #: kraft.adoc:135 #, no-wrap msgid "Own identity" msgstr "" #. type: Plain text #: kraft.adoc:139 msgid "" "Check here if the information that you have given during the initial setup " "is correct for the use in the documents." msgstr "" #. type: delimited block _ #: kraft.adoc:144 msgid "" "WARNING: If you made the choice to use the information from KaddressBook " "then is the information from a later manual entry ignored." msgstr "" #. type: Plain text #: kraft.adoc:148 msgid "" "After we have made some corrections to the configuration, we go back to the " "main window.Here we see three tabs:" msgstr "" #. type: Plain text #: kraft.adoc:150 msgid "Documents" msgstr "" #. type: Plain text #: kraft.adoc:151 msgid "Timeline" msgstr "" #. type: Plain text #: kraft.adoc:152 msgid "Catalogs" msgstr "" #. type: Plain text #: kraft.adoc:154 msgid "We go first to catalogs." msgstr "" #. type: Title === #: kraft.adoc:155 #, no-wrap msgid "Catalog" msgstr "" #. type: Plain text #: kraft.adoc:158 msgid "In the tab catalog are two different catalogs:" msgstr "" #. type: Plain text #: kraft.adoc:160 msgid "`Material`" msgstr "" #. type: Plain text #: kraft.adoc:163 msgid "" "A catalog of material that are sold, with their purchase prices, the profit " "and the sell-price." msgstr "" #. type: Plain text #: kraft.adoc:165 msgid "and `Standard Templates`" msgstr "" #. type: Plain text #: kraft.adoc:167 msgid "A catalog of standard recipes of work like planting trees." msgstr "" #. type: Plain text #: kraft.adoc:170 msgid "" "Both catalogs can have chapters and sub-chapters for to organize your " "templates. First we are going to fill in the" msgstr "" #. type: Title ==== #: kraft.adoc:171 #, no-wrap msgid "Material-catalog" msgstr "" #. type: Plain text #: kraft.adoc:176 msgid "" "A catalog of material that are sold, with their purchase prices, the profit " "and the sell-price. First we are going to add new chapters and subchapters." msgstr "" #. type: Title ===== #: kraft.adoc:177 #, no-wrap msgid "New chapters" msgstr "" #. type: Plain text #: kraft.adoc:181 msgid "" "Select with the mouse the column-name `material`, select now in the " "context-menu [Add a sub chapter]" msgstr "" #. type: Plain text #: kraft.adoc:183 msgid "and add an extra chapter like `Trees`" msgstr "" #. type: Title ===== #: kraft.adoc:184 #, no-wrap msgid "New sub chapters" msgstr "" #. type: Plain text #: kraft.adoc:192 msgid "" "We are going to ad sub chapters in the map `Trees`. Select with the mouse " "the name of the chapter where you like to add a subchapter, select now in " "the context-menu [Add a sub chapter] and ad an extra subchapters like `Loaf " "trees` and `needle trees`. After adding the extra chapters and subchapters " "for dividing the material, we are going to add the material themself." msgstr "" #. type: Title ===== #: kraft.adoc:193 #, no-wrap msgid "New template" msgstr "" #. type: Plain text #: kraft.adoc:198 msgid "" "Select with the mouse the name of the sub-chapter or chapter where you like " "to add a material. Select the sub map Loaf trees and select now in the " "context-menu" msgstr "" #. type: Plain text #: kraft.adoc:201 msgid "" "Add the extra materials `coconut tree`, `apple tree` and `pine-apple tree`." msgstr "" #. type: Plain text #: kraft.adoc:203 msgid "Fill in the price that we have paid." msgstr "" #. type: Plain text #: kraft.adoc:205 msgid "Fill in the profit that we want to have on the material" msgstr "" #. type: Plain text #: kraft.adoc:207 msgid "And fill in how much is in a packet." msgstr "" #. type: Plain text #: kraft.adoc:209 msgid "Now we are going to:" msgstr "" #. type: Title ==== #: kraft.adoc:210 #, no-wrap msgid "Standard Templates" msgstr "" #. type: Plain text #: kraft.adoc:213 msgid "This is a catalog of standard recipes of work like:" msgstr "" #. type: Plain text #: kraft.adoc:215 msgid "planting trees" msgstr "" #. type: Plain text #: kraft.adoc:216 msgid "cutting grass" msgstr "" #. type: Plain text #: kraft.adoc:217 msgid "transport costs" msgstr "" #. type: Plain text #: kraft.adoc:218 msgid "planting grass" msgstr "" #. type: Plain text #: kraft.adoc:219 msgid "sowing grass-seed" msgstr "" #. type: Plain text #: kraft.adoc:221 msgid "We add here the standard work of planting a tree." msgstr "" #. type: Plain text #: kraft.adoc:224 msgid "" "Select with the mouse the name of the chapter [Work] where you like to add " "the new template," msgstr "" #. type: Plain text #: kraft.adoc:226 msgid "select now the context-menu [New template]" msgstr "" #. type: Plain text #: kraft.adoc:228 msgid "and the extra templates `Plant tree` and `cut grass`." msgstr "" #. type: Plain text #: kraft.adoc:230 msgid "After we made the new template, a window opens with 4 tabs:" msgstr "" #. type: Title ===== #: kraft.adoc:232 kraft.adoc:238 #, no-wrap msgid "Template" msgstr "" #. type: Title ===== #: kraft.adoc:233 kraft.adoc:250 #, no-wrap msgid "Time calculation" msgstr "" #. type: Plain text #: kraft.adoc:234 msgid "Fix costs" msgstr "" #. type: Title ===== #: kraft.adoc:235 kraft.adoc:285 #, no-wrap msgid "Material" msgstr "" #. type: Plain text #: kraft.adoc:237 msgid "First we go to the tab:" msgstr "" #. type: Plain text #: kraft.adoc:241 msgid "We give here the name of the new standard template like `Plant tree`" msgstr "" #. type: delimited block _ #: kraft.adoc:245 msgid "WARNING: be careful, this name is later used in the invoice" msgstr "" #. type: Plain text #: kraft.adoc:249 msgid "" "we select that this is per piece and that the margin is 8% and that the full " "VAT is applicable." msgstr "" #. type: Plain text #: kraft.adoc:253 msgid "We fill here in a number of work with the time:" msgstr "" #. type: Block title #: kraft.adoc:254 #, no-wrap msgid "Spent time" msgstr "" #. type: Table #: kraft.adoc:261 #, no-wrap msgid "" "|Dig hole |32 min. |worker\n" "|Place tree |12 min. |worker\n" "|Fill hole |17 min. |worker\n" "|give water |5 min. |worker\n" msgstr "" #. type: Plain text #: kraft.adoc:264 msgid "The cost for worker which we have earlier filled in is now used." msgstr "" #. type: delimited block _ #: kraft.adoc:269 msgid "" "NOTE: in the invoice we see later only Plant tree, we will not see the parts " "dig hole,place tree,fill hole,give water" msgstr "" #. type: Plain text #: kraft.adoc:272 msgid "Now we go to the tab" msgstr "" #. type: Title ===== #: kraft.adoc:273 #, no-wrap msgid "Fixed costs" msgstr "" #. type: Plain text #: kraft.adoc:276 msgid "and fill in:" msgstr "" #. type: Block title #: kraft.adoc:277 #, no-wrap msgid "Fixed item" msgstr "" #. type: Table #: kraft.adoc:281 #, no-wrap msgid "|Transportcost |35 euro |1 pcs.\n" msgstr "" #. type: Plain text #: kraft.adoc:284 msgid "After this we go to the tab:" msgstr "" #. type: Plain text #: kraft.adoc:288 msgid "Here we fill in:" msgstr "" #. type: Block title #: kraft.adoc:289 #, no-wrap msgid "Used materials" msgstr "" #. type: Table #: kraft.adoc:293 #, no-wrap msgid "|1 |support pole |3,5 euro\n" msgstr "" #. type: Plain text #: kraft.adoc:296 msgid "We go now back to the first tab template" msgstr "" #. type: Plain text #: kraft.adoc:299 msgid "" "On the first tab [template], we can now see the overall cost per one unit" msgstr "" #. type: Plain text #: kraft.adoc:302 msgid "" "Click on [OK] for saving the result or on [cancel] for discarding the result." msgstr "" #. type: Plain text #: kraft.adoc:304 msgid "We make a second template `cut grass`" msgstr "" #. type: Plain text #: kraft.adoc:307 msgid "" "we fill in `cut grass`, as unit we choose sm (square meter), on the second " "tab we fill in that we need 3 min per square meter." msgstr "" #. type: Plain text #: kraft.adoc:310 msgid "" "Click on [OK] for saving the result or on [Cancel] for discarding the result." msgstr "" #. type: Plain text #: kraft.adoc:312 msgid "We are now ready for the first invoice." msgstr "" #. type: Title == #: kraft.adoc:314 #, no-wrap msgid "Creating Documents" msgstr "" #. type: Title === #: kraft.adoc:316 #, no-wrap msgid "The first Invoice" msgstr "" #. type: Plain text #: kraft.adoc:319 msgid "Open the tab btn:[documents]" msgstr "" #. type: Plain text #: kraft.adoc:321 kraft.adoc:426 msgid "Click on btn:[create document]" msgstr "" #. type: Plain text #: kraft.adoc:323 msgid "The window document [creation wizard opens]." msgstr "" #. type: Plain text #: kraft.adoc:325 msgid "Select in document type `invoice`." msgstr "" #. type: Plain text #: kraft.adoc:328 msgid "" "Fill in on the whiteboard content a short text about what the invoice is, " "like: `cut grass and planted tree for mister Jonson`" msgstr "" #. type: Plain text #: kraft.adoc:330 kraft.adoc:435 msgid "Click on btn:[next]" msgstr "" #. type: Plain text #: kraft.adoc:332 kraft.adoc:437 msgid "Select on the new window the name and address from the client." msgstr "" #. type: Plain text #: kraft.adoc:335 kraft.adoc:440 msgid "" "(if the name and address is not there, click then on btn:[new contact] or on " "btn:[edit contact] if you want to edit the contact)" msgstr "" #. type: Plain text #: kraft.adoc:337 kraft.adoc:442 msgid "Click on btn:[OK]." msgstr "" #. type: Plain text #: kraft.adoc:339 msgid "Now opens the window document [items]." msgstr "" #. type: Plain text #: kraft.adoc:341 msgid "this window has 2 tabs and the 3 buttons on the top:" msgstr "" #. type: Plain text #: kraft.adoc:343 kraft.adoc:448 msgid "btn:[Add item...]," msgstr "" #. type: Plain text #: kraft.adoc:344 kraft.adoc:449 msgid "btn:[Add discount item]," msgstr "" #. type: Plain text #: kraft.adoc:345 kraft.adoc:450 msgid "btn:[Show templates]." msgstr "" #. type: Plain text #: kraft.adoc:349 msgid "" "In the left tab you can see all the items that we want to place on the " "invoice, on the right tab we see the text from the header, the total price " "and the footer." msgstr "" #. type: Plain text #: kraft.adoc:353 msgid "" "If you click on the text of the header or the footer on the right side then " "the window changes in such a way that you can edit the header or the footer." msgstr "" #. type: Plain text #: kraft.adoc:356 msgid "" "Adapt the header and the footer to your situation, on the footer you can " "place a text: `We make your garden-dream come to reality.`." msgstr "" #. type: Plain text #: kraft.adoc:358 kraft.adoc:452 msgid "Click on the button btn:[Show templates]." msgstr "" #. type: Plain text #: kraft.adoc:362 msgid "" "The right tab changes and show now the earlier made templates, we select in " "the group Work, the subgroup Plant tree and click then on the button with " "the to the left pointing arrow on the bottom side." msgstr "" #. type: Plain text #: kraft.adoc:364 kraft.adoc:458 msgid "A new window [Create Item from Template] opens." msgstr "" #. type: Plain text #: kraft.adoc:367 msgid "" "Because we have planted 2 trees, we go to the field [insert] and change this " "to 2 pcs." msgstr "" #. type: Plain text #: kraft.adoc:370 msgid "" "Click on btn:[OK] for saving the result or on btn:[cancel] for discarding " "the result." msgstr "" #. type: Plain text #: kraft.adoc:372 kraft.adoc:466 kraft.adoc:477 msgid "The window close and we go back to the main window." msgstr "" #. type: Plain text #: kraft.adoc:376 msgid "" "We click again on btn:[Show templates] and select this time `cut grass`, we " "click again on the button with the arrow, in the opened window we select " "that the grass-field was 24 square meter." msgstr "" #. type: Plain text #: kraft.adoc:379 kraft.adoc:464 kraft.adoc:497 kraft.adoc:529 msgid "" "Click on btn:[OK] for saving the result or on btn:[Cancel] for discarding " "the result." msgstr "" #. type: Plain text #: kraft.adoc:381 msgid "" "We add now manually an item by clicking on the button btn:[Add item…] and " "the window [create new item] opens." msgstr "" #. type: Plain text #: kraft.adoc:385 msgid "" "Because we have delivered a special tree, we fill here in the name of the " "special tree `liguster`, at the field insert we fill in the number of the " "special trees that we have delivered and the price of them." msgstr "" #. type: delimited block _ #: kraft.adoc:391 msgid "" "WARNING: Remind that in the catalog we can add a profit on the price of the " "material, in the invoice and in the offer we can not add a profit on the " "price of the material." msgstr "" #. type: Plain text #: kraft.adoc:394 msgid "We have now an invoice with 3 items." msgstr "" #. type: Plain text #: kraft.adoc:397 msgid "" "Click on btn:[OK] for saving the invoice or on btn:[Cancel] for discarding " "the invoice." msgstr "" #. type: Plain text #: kraft.adoc:399 msgid "We click on btn:[OK] and save the result." msgstr "" #. type: Plain text #: kraft.adoc:401 msgid "Your first invoice is now ready for sending." msgstr "" #. type: Plain text #: kraft.adoc:404 msgid "" "In the window documents we see our first invoice, notice that this document " "has a document number which we can see on the left side." msgstr "" #. type: Plain text #: kraft.adoc:407 msgid "" "On top of the window with all the invoices we see the button [Print " "Document], on which we click." msgstr "" #. type: Plain text #: kraft.adoc:410 msgid "" "From the invoice will now a PDF be made which we can print on paper or send " "by email to the client." msgstr "" #. type: Plain text #: kraft.adoc:412 msgid "After this we are going to create a offer for some work in a garden." msgstr "" #. type: Title === #: kraft.adoc:414 #, no-wrap msgid "Creating an Offer" msgstr "" #. type: Plain text #: kraft.adoc:417 msgid "" "The client has asked to plant a tree, we will offer 3 different trees which " "we can plant." msgstr "" #. type: Plain text #: kraft.adoc:420 msgid "" "Beside this, we have seen that there is a lifeless three, which we will " "offer to remove as extra work. " "image:create_new_doc.png[Numbercycles,float=\"right\"]" msgstr "" #. type: Plain text #: kraft.adoc:422 msgid "" "For the total price we do not want to show the price of the removal of the " "lifeless tree and we want for the total price only to show the price of one " "tree and not three." msgstr "" #. type: Plain text #: kraft.adoc:424 msgid "Open again the tab btn:[documents]." msgstr "" #. type: Plain text #: kraft.adoc:428 msgid "The window _Document Creation Wizard_ opens." msgstr "" #. type: Plain text #: kraft.adoc:430 msgid "select in btn:[document type] > btn:[Offer]." msgstr "" #. type: Plain text #: kraft.adoc:433 msgid "" "Fill in on the whiteboard content a short text about what the offer is, " "like: `plant one tree and removal of lifeless tree`" msgstr "" #. type: Plain text #: kraft.adoc:444 msgid "Now the window [edit document] opens." msgstr "" #. type: Plain text #: kraft.adoc:446 msgid "This window has 2 tabs and the 3 buttons on the top:" msgstr "" #. type: Plain text #: kraft.adoc:456 msgid "" "The right tab changes and show now the earlier made templates, we select in " "the group `Work`, the subgroup `Plant tree` and click then on the button " "with the to the left pointing arrow on the bottom side." msgstr "" #. type: Plain text #: kraft.adoc:461 msgid "" "Because we want to plant 1 tree, we go to the field [insert] and keep this " "on 1 pcs." msgstr "" #. type: Plain text #: kraft.adoc:469 msgid "" "We click on the button btn:[Show templates] and this time we select in " "catalog Material" msgstr "" #. type: Plain text #: kraft.adoc:473 msgid "" "The material-catalog opens, and we can select in the chapter `trees` the " "subchapter `loaf trees` in which we select the `apple tree` which we made " "earlier." msgstr "" #. type: Plain text #: kraft.adoc:475 msgid "" "Click on we btn:[OK] for saving the result or on btn:[cancel] for discarding " "the result." msgstr "" #. type: Plain text #: kraft.adoc:479 msgid "We add now manually an item by clicking on the button `Add item…`." msgstr "" #. type: Plain text #: kraft.adoc:481 msgid "the window [create new item] opens." msgstr "" #. type: Plain text #: kraft.adoc:483 msgid "" "We want that the client can make a choice from an apple, a pear tree and the " "liguster." msgstr "" #. type: Plain text #: kraft.adoc:485 msgid "Therefor we are going to add also a pear tree manually." msgstr "" #. type: Plain text #: kraft.adoc:487 msgid "" "We click on the button btn:[Add item…] and the window [create new item] " "opens." msgstr "" #. type: Plain text #: kraft.adoc:491 msgid "" "We fill here in the name of the tree `Pear tree`, at the field insert we " "fill in the number of the special trees that we have delivered and the price " "of them." msgstr "" #. type: Plain text #: kraft.adoc:494 msgid "" "We want add this to the material catalog for future use, therefor we select " "also [select this item as template for future documents] and we select in " "[save in chapter]`trees`." msgstr "" #. type: Plain text #: kraft.adoc:499 msgid "We does this again but then for the liguster." msgstr "" #. type: Plain text #: kraft.adoc:501 msgid "We have now 3 items with trees in the offer." msgstr "" #. type: Plain text #: kraft.adoc:503 msgid "" "As last item we add an item with `remove tree` with 0,5 hour for 32 euro." msgstr "" #. type: Plain text #: kraft.adoc:505 msgid "On the left side of an item we can see 2 buttons:" msgstr "" #. type: Plain text #: kraft.adoc:507 msgid "a button with a flag and a button with what looks like a page." msgstr "" #. type: Plain text #: kraft.adoc:510 msgid "" "We select the upper button with the page after which opens a context-menu " "with the items:" msgstr "" #. type: Plain text #: kraft.adoc:520 #, no-wrap msgid "" " [Item kind]->[Normal]\n" " [Item kind]>[Alternative]\n" " [Item kind]>[On demand]\n" " [Tax]\n" " [Move up]\n" " [Move down]\n" " [Lock item]\n" " [Unlock item]\n" " [Delete item]\n" msgstr "" #. type: Plain text #: kraft.adoc:523 msgid "" "We choose here [Item kind] and change for `pear tree` from [normal] to " "[alternative]." msgstr "" #. type: Plain text #: kraft.adoc:526 msgid "" "We do this also for [liguster] and for [remove tree] we change this from " "[normal] to [on demand]." msgstr "" #. type: Plain text #: kraft.adoc:532 msgid "" "We want to see the result and therefor we click on the button [show " "document]." msgstr "" #. type: Plain text #: kraft.adoc:538 msgid "" "We see now that the prize of the pear tree, the liguster and the removal of " "the tree is not used for the total prize. When we are happy with the result, " "we can click on the button btn:[close] after which we click on the button " "btn:[Print Document] for making a PDF what we can print out or send to the " "client." msgstr "" #. type: Plain text #: kraft.adoc:541 msgid "" "After your first invoice is now your first offer now also ready for sending." msgstr "" #. type: Title == #: kraft.adoc:543 #, no-wrap msgid "Customization" msgstr "" #. type: Plain text #: kraft.adoc:547 msgid "" "Kraft can be customized in most of the graphical user interface and in " "particular in the output it generates." msgstr "" #. type: Title === #: kraft.adoc:548 #, no-wrap msgid "Output Document Customization" msgstr "" #. type: Plain text #: kraft.adoc:551 msgid "" "To create PDF output documents, the document data that was edited in the " "Kraft app is filled into a template. The template defines how the output " "document looks like, ie. by font settings, placing of elements and such." msgstr "" #. type: Plain text #: kraft.adoc:553 msgid "" "The file that is assembled from data and the template is converted to PDF " "using a special document creation script. All that is started automatically " "by Kraft if a document should be printed." msgstr "" #. type: Plain text #: kraft.adoc:555 msgid "" "Each document type in Kraft can have it's own template that is used to " "create a PDF. Which one can be set in the Settings dialog for document types." msgstr "" #. type: Title ==== #: kraft.adoc:556 #, no-wrap msgid "WeasyPrint Documents" msgstr "" #. type: Plain text #: kraft.adoc:559 msgid "" "With https://weasyprint.org[WeasyPrint] Kraft uses a very powerful HTML and " "CSS based generator that makes it very easy to create highly customized " "documents which fit the users expectations. The general idea is that " "Weasyprint loads html output that is processed to PDF. Usually it is " "considering a Cascading Stylesheet file which has a huge impact on how the " "PDF document looks in the end." msgstr "" #. type: Plain text #: kraft.adoc:561 msgid "" "To use a WeasyPrint based template for a document simply create a template " "file and save it with the extension *.gtmpl*. With that file extension Kraft " "automatically uses WeasyPrint and also the Grantlee templating for rendering." msgstr "" #. type: Plain text #: kraft.adoc:563 msgid "" "An example for a WeasyPrint document can be found in the Kraft package in " "the reports directory and is called invoice.gtmpl." msgstr "" #. type: Plain text #: kraft.adoc:565 msgid "" "To use a WeasyPrint template with one of the Kraft document types just " "select the template file name (with the right extension `*.gtml`) in the " "Kraft Settings Dialog." msgstr "" #. type: Plain text #: kraft.adoc:567 msgid "" "From version 0.95 on Kraft ships with an example document in the Grantlee- " "and WeasyPrint format. It can be found at " "`/usr/share/kraft/reports/invoice.gtmpl` or " "https://github.com/dragotin/kraft/blob/master/reports/invoice.gtmpl[online " "on Github]." msgstr "" #. type: Plain text #: kraft.adoc:569 msgid "" "To effectively change the look of the document `kraft.css` " "(https://github.com/dragotin/kraft/blob/master/reports/kraft.css[on Github]) " "needs to be considered. It defines most of the look." msgstr "" #. type: Title ==== #: kraft.adoc:570 #, no-wrap msgid "Template Variables" msgstr "" #. type: Plain text #: kraft.adoc:573 msgid "" "To generate the PDF, Kraft has to transfer data from the document you have " "been working on in Kraft to the input document that is processed to an PDF " "utilising WeasyPrint. For that, Kraft uses a text template. In that, Kraft " "replaces variables with the actual values." msgstr "" #. type: Plain text #: kraft.adoc:575 msgid "" "The syntax is based on the Django syntax for templates described in the " "https://docs.djangoproject.com/en/3.1/topics/templates/[the docs]." msgstr "" #. type: Title == #: kraft.adoc:579 #, no-wrap msgid "Menus and Shortcuts" msgstr "" #. type: Title === #: kraft.adoc:581 #, no-wrap msgid "Main Application Menu" msgstr "" #. type: Title ==== #: kraft.adoc:584 #, no-wrap msgid "The File Menu" msgstr "" #. type: Plain text #: kraft.adoc:589 #, no-wrap msgid "" " [File]>[Quit]\n" " [Ctrl]+[Q]\n" " Quits the application.\n" msgstr "" #. type: Title ==== #: kraft.adoc:591 #, no-wrap msgid "The Document Menu" msgstr "" #. type: Plain text #: kraft.adoc:597 #, no-wrap msgid "" " [Document]>[Show Document]\n" " [Ctrl]+[R]\n" " Opens a window with the selected document for showing it.\n" msgstr "" #. type: Plain text #: kraft.adoc:601 #, no-wrap msgid "" " [Document]>[Edit Document]\n" " [Ctrl+O]\n" " Opens a window with the selected document for editing it.\n" msgstr "" #. type: Plain text #: kraft.adoc:605 #, no-wrap msgid "" " [Document]>[Open Archived document]\n" " [Ctrl]+[A]\n" " Opens an archived document.\n" msgstr "" #. type: Plain text #: kraft.adoc:608 #, no-wrap msgid "" " [Document]>[Create Document]\n" " Opens a window with a wizard for creating a new client-document.\n" msgstr "" #. type: Plain text #: kraft.adoc:612 #, no-wrap msgid "" " [Document]>[Copy Document]\n" " Makes a copy of the selected client-document to a new client-document\n" " which can belong to an other client or an other documenttype.\n" msgstr "" #. type: Plain text #: kraft.adoc:615 #, no-wrap msgid "" " [Document]>[Follow Document]\n" " Opens the selected client-document for editing.\n" msgstr "" #. type: Plain text #: kraft.adoc:619 #, no-wrap msgid "" " [Document]>[Print document]\n" " Makes a PDf from the selected client-document for to be mailed or\n" " printed.\n" msgstr "" #. type: Plain text #: kraft.adoc:623 #, no-wrap msgid "" " [Document]>[Mail document]\n" " [Ctrl]+[M]\n" " Mails a document.\n" msgstr "" #. type: Title ==== #: kraft.adoc:626 #, no-wrap msgid "The Settings menu" msgstr "" #. type: Plain text #: kraft.adoc:632 #, no-wrap msgid "" " [Settings]>[Edit Tag Templates]\n" " [Ctrl]+[E]\n" " Opens a window where you add, edit or translate the tags (like work,\n" " material, plants or discounts).\n" msgstr "" #. type: Plain text #: kraft.adoc:636 #, no-wrap msgid "" " [Settings]>[Redo initial setup]\n" " [Ctrl+R]\n" " Redoes the initial setup. After this, a restart of Kraft is required.\n" msgstr "" #. type: Plain text #: kraft.adoc:640 #, no-wrap msgid "" " [Settings]>[Showed toolbars]\n" " Here you can decide if the `main toolbar` and the toolbar `Document Actions`\n" " are shown.\n" msgstr "" #. type: Plain text #: kraft.adoc:644 #, no-wrap msgid "" " [Settings]>[Configure Kraft]\n" " [Ctrl]+[Shft]+[,]\n" " Here you can configure Kraft.\n" msgstr "" #. type: Title === #: kraft.adoc:645 #, no-wrap msgid "Document Edit Window" msgstr "" #. type: Title ==== #: kraft.adoc:648 #, no-wrap msgid "The context Menu" msgstr "" #. type: Plain text #: kraft.adoc:652 #, no-wrap msgid "" " [Context]>[Item kind]\n" " change the status from this item between\n" msgstr "" #. type: Plain text #: kraft.adoc:653 #, no-wrap msgid "Normal\n" msgstr "" #. type: Plain text #: kraft.adoc:654 #, no-wrap msgid "Alternative\n" msgstr "" #. type: Plain text #: kraft.adoc:655 #, no-wrap msgid "On demand\n" msgstr "" #. type: Plain text #: kraft.adoc:658 #, no-wrap msgid "" " [Context]>[Tax]\n" " Seems not working.\n" msgstr "" #. type: Plain text #: kraft.adoc:661 #, no-wrap msgid "" " [Context]>[Move up]\n" " Moves this item a place up in document.\n" msgstr "" #. type: Plain text #: kraft.adoc:664 #, no-wrap msgid "" " [Context]>[Move down]\n" " Moves this item a place down in document.\n" msgstr "" #. type: Plain text #: kraft.adoc:667 #, no-wrap msgid "" " [Context]>[Lock item]\n" " It is not clear what is does.\n" msgstr "" #. type: Plain text #: kraft.adoc:670 #, no-wrap msgid "" " [Context]>[Unlock item]\n" " It is not clear what is does.\n" msgstr "" #. type: Plain text #: kraft.adoc:673 #, no-wrap msgid "" " [Context]>[Delete item]\n" " Removes this item from document.\n" msgstr "" #. type: Title == #: kraft.adoc:676 #, no-wrap msgid "Advanced Topics" msgstr "Fortgeschrittene Themen" #. type: Plain text #: kraft.adoc:679 msgid "" "This chapter describes advanced topics around Kraft. Some Linux knowledge is " "required, and setups should be done by experienced Linux administrators and " "should be tested carefully." msgstr "" "Dieses Kapitel beschreibt fortgeschrittene Themen um Kraft. Dabei wird etwas " "Linux-Kenntnis vorausgesetzt, und das Aufsetzen sollte von erfahreneren " "Linux Administratoren durchgeführt und gut testet werden." #. type: Title === #: kraft.adoc:680 #, no-wrap msgid "Using Kraft Collaborative" msgstr "Kraft kollaborativ verwenden" #. type: Plain text #: kraft.adoc:683 msgid "" "Kraft can be used collaborative in a distributed environment. That means " "that multiple users work on their desktops with their own Kraft instance on " "the same data." msgstr "" "Kraft kann kollaborativ in einer verteilten Umgebung verwendet werden. Das " "heisst, dass mehrere Benutzer an ihren jeweiligen Arbeitsplatzrechnern mit " "ihrer eigenen Kraft Instanz arbeiten können und dabei die selben Daten " "verwenden." #. type: Plain text #: kraft.adoc:685 msgid "" "This whole topic is a subject to change, as Kraft will evolve to use " "ownCloud as a private cloud solution to store the data." msgstr "" "Das ganze Thema ist Veränderung unterworfen, da Kraft in naher Zukunft " "ownCloud als private Cloud Lösung zur Datenspeicherung verwenden wird." #. type: Title ==== #: kraft.adoc:686 #, no-wrap msgid "Sharing Database and Document Pool" msgstr "Datenbank und Dokumentpool teilen" #. type: Plain text #: kraft.adoc:689 msgid "" "The simplest case is that two or more Kraft instances use a database " "together and access the same pool of PDF documents on the harddisk. For " "simplicity this describes only two Kraft instances." msgstr "" "Der einfachste Fall ist das zwei oder wenige mehr Kraft Instanzen die " "Datenbank gemeinsam benutzen und den Pool von PDF Dokumenten gemeinsam " "verwenden. Um es einfach zu halten werden hier zwei Instanzen beschrieben." #. type: Plain text #: kraft.adoc:691 msgid "" "A typical use case would be: Two different Linux users want to use Kraft. " "They both have their own computer but only work in the same network. For " "example this would describe a situation with one main office machine that " "runs Kraft in normal mode, plus a notebook with Kraft in read only mode to " "view documents, check catalogs and such." msgstr "" "Ein typischer Anwendungsfall könnte sein: Zwei verschiedene Linux user " "möchten Kraft verwenden. Sie haben beide ihren eigenen Computer und arbeiten " "im selben Netzwerk. Dieses Beispiel beschreibt eine Situation mit einem " "Hauptbüro das Kraft im normalen Modus betreibt, und einem Notebook mit " "Kraft, das im NurLesen Modus um Dokumente anzusehen, Kataloge zu überprüfen " "und ähnliches." #. type: Plain text #: kraft.adoc:693 msgid "For that, the following prerequisites have to be met:" msgstr "Dafür müssen die folgenden Voraussetzungen erfüllt sein:" #. type: Plain text #: kraft.adoc:695 msgid "MySQL or MariaDB is used as database backend. Sqlite is not supported." msgstr "" "Als Datenbank-Backend wird MySQL oder MariaDB verwendet. Sqlite wird nicht " "unterstützt." #. type: Plain text #: kraft.adoc:696 msgid "" "The database is accessible with a mysql user and from each machine for both " "users." msgstr "" "Die Datenbank ist mit dem MySQL Benutzer von beiden Rechnern aus erreichbar." #. type: Plain text #: kraft.adoc:697 msgid "The document store directory has to be shared." msgstr "Das Dokument-Speicher-Verzeichnis muss geteilt werden." #. type: delimited block _ #: kraft.adoc:701 msgid "" "WARNING: There is no protection against having both users editing the same " "document. Because that is dangerous and can lead to unpredictable results, " "it is recommended to run all instances of Kraft except the main one in read " "only mode. This is done by starting Kraft with the `-r` command line switch." msgstr "" "Achtung: Es gibt keinen Schutz dagegen, dass beide Benutzer das gleiche " "Dokument zur gleichen Zeit bearbeiten. Weil das gefährlich ist und zu " "unvorhersehbaren Resultaten führen kann, ist es empfohlen, Kraft in allen " "ausser der Haupt-Instanz im Nur-Lese Modus zu betreiben. Der Nur-Lese Modus " "wird mit Krafts Kommandozeilenschalter -r eingeschaltet." #. type: Plain text #: kraft.adoc:704 #, no-wrap msgid "**Sharing the Database**\n" msgstr "**Die Datenbank teilen**\n" #. type: Plain text #: kraft.adoc:706 msgid "" "The database server should be installed on the main machine or a dedicated " "device like a NAS. Networking speed influences the comfort to use obviously." msgstr "" "Der Datenbankserver sollte auf der Haupt-Maschine installiert sein, oder es " "sollte ein spezialisiertes Gerät wie ein NAS verwendet werden. Die Netzwerk " "Geschwindigkeit beeinflusst die Benutzbarkeit natürlich erheblich." #. type: Plain text #: kraft.adoc:708 msgid "Find howtos on the internet how to setup MySQL accordingly." msgstr "Howtos um MySQL aufzusetzen sind im Internet zu finden." #. type: Plain text #: kraft.adoc:710 #, no-wrap msgid "**Sharing the Document Pool Directory**\n" msgstr "**Den Dokument Pool teilen**\n" #. type: Plain text #: kraft.adoc:712 msgid "" "Kraft writes generated PDF documents into a local directory. Where that is " "can be configured in the Kraft Config file. The config file has to be " "adopted on all instances." msgstr "" "Kraft schreibt generierte PDFs in ein lokales Verzeichnis. Welches " "Verzeichnis das ist kann im Kraft Konfigfile eingerichtet werden. Das " "Konfigfile muss auf allen Instanzen angepasst werden." #. type: Plain text #: kraft.adoc:714 msgid "" "That is located in each users home directory, in the path `.config/kraftrc`. " "It has to contain the following config value:" msgstr "" "Es ist in jedem Benutzer Homeverzeichnis unter dem releativen Pfad " "`.config/kraftrc`. Es muss den folgenden Konfigurations-Wert enthalten:" #. type: Plain text #: kraft.adoc:716 kraft.adoc:720 msgid "```" msgstr "```" #. type: Plain text #: kraft.adoc:718 msgid "PdfOutputDir=/data/space/kraftdoc/pdf" msgstr "PdfOutputDir=/data/space/kraftdoc/pdf" #. type: Plain text #: kraft.adoc:722 msgid "" "There are different ways how share that directory, ie. NFS or SMB storages. " "It is important that both users from both machines can list and access the " "files. The main user needs read and write access, read only users only need " "read access to the files." msgstr "" "Es gibt verschiedene Wege wie das Verzeichnis geteilt werden kann, zum " "Beispiel NFS und SMB Server. Es ist entscheidend, dass beide Benutzer von " "beiden Maschinen die Dateien auflisten und zugreifen können. Der " "Hauptbenutzer braucht Schreib- und Leserecht., Nur-Lese Bentuzer brauchen " "nur Lesezugriff auf die Dateien." #. type: Plain text #: kraft.adoc:724 msgid "" "A recommended setup is a NFS share with autofs which is set up on the main " "machine. To manage file access a certain group should be set up on the " "machines with which access can be managed." msgstr "" "Ein empfohlenes Setup benutzt ein NFS Share über autofs, das auf der " "Hauptmaschine aufgesetzt werden muss. Um Dateizugriff zu verwalten, sollte " "eine Gruppe aufgesetzt werden." #. type: Plain text #: kraft.adoc:726 #, no-wrap msgid "**Starting Kraft in read-only mode**\n" msgstr "**Kraft im Nur-Lesen Modus**\n" #. type: Plain text #: kraft.adoc:728 msgid "" "To start Kraft in read-only mode, start the binary with the `-r` command " "line switch." msgstr "" "Um Kraft im Nur-Lese Modus zu starten, muss das Programm mit dem " "Kommandozeilenschalter `-r` gestartet werden." #. type: Title == #: kraft.adoc:730 #, no-wrap msgid "Credits and License" msgstr "Credits und Lizenz" #. type: Plain text #: kraft.adoc:733 msgid "Program and documentation copyright 2004–2021 Klaas Freitag" msgstr "Software und Dokumentation Copyright 2004-2021 Klaas Freitag" #. type: Plain text #: kraft.adoc:734 msgid "Documentation copyright 2020 Ronald Stroethoff" msgstr "Dokumentation Copyright 2020 Ronald Stroethoff" #, no-wrap #~ msgid "Features" #~ msgstr "Funktionen" #~ msgid "Text templates" #~ msgstr "Textvorlagen" kraft-0.97/manual/po/kraft.pot000066400000000000000000001157571410616450300163260ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE # Copyright (C) YEAR Free Software Foundation, Inc. # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "POT-Creation-Date: 2021-06-12 15:30+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #. type: Title = #: kraft.adoc:1 #, no-wrap msgid "The Kraft Handbook" msgstr "" #. type: Title == #: kraft.adoc:14 #, no-wrap msgid "Introduction" msgstr "" #. type: Plain text #: kraft.adoc:17 msgid "" "Kraft is a Qt and KDE application to organize office documents like quotes " "and invoices in a small business. It eases the creation of these of " "documents and helps with repeating tasks." msgstr "" #. type: Plain text #: kraft.adoc:19 msgid "" "With Kraft, there is no need for fiddling with a text processor any " "more. Documents are created by a few clicks and archived " "automatically. Kraft generates high quality PDF output for printing, mailing " "and archiving." msgstr "" #. type: Labeled list #: kraft.adoc:20 #, no-wrap msgid "Feature Overview" msgstr "" #. type: Plain text #: kraft.adoc:23 msgid "" "Customer management, deeply integrated in the KDE infrastructure using " "KAddressbook." msgstr "" #. type: Plain text #: kraft.adoc:24 msgid "Automated creation of offers, invoices and similar documents." msgstr "" #. type: Plain text #: kraft.adoc:25 msgid "Templates for document header- and footertexts and for document items." msgstr "" #. type: Plain text #: kraft.adoc:26 msgid "Calculation of items." msgstr "" #. type: Plain text #: kraft.adoc:27 msgid "Material management." msgstr "" #. type: Plain text #: kraft.adoc:28 msgid "Configurable document creation in PDF format for print and email." msgstr "" #. type: Plain text #: kraft.adoc:32 msgid "" "Kraft is designed to use the data entries from the KDE address book which is " "a module of the https://community.kde.org/KDE_PIM[KDE PIM], an information " "management application. All addresses are collected in the " "https://userbase.kde.org/KAddressBook[KAdressBook]." msgstr "" #. type: Plain text #: kraft.adoc:34 msgid "" "The code of Kraft is open source and is released under the " "https://en.wikipedia.org/wiki/GNU_General_Public_License[GNU General Public " "License]." msgstr "" #. type: Plain text #: kraft.adoc:39 #, no-wrap msgid "" "Kraft is driven by community of users, coders and others. +\n" "Also this manual needs contributions! +\n" " +\n" " Learn more on " "https://github.com/dragotin/kraft/blob/master/manual/Readme.md[how to " "contribute]!\n" msgstr "" #. type: Title == #: kraft.adoc:41 #, no-wrap msgid "First Use and Basic Configuration" msgstr "" #. type: Plain text #: kraft.adoc:44 msgid "" "During the initial setup you are asked to select a database to use and give " "the address of your company." msgstr "" #. type: Plain text #: kraft.adoc:46 msgid "" "You can fill in your company address (that appears on the printed documents) " "on two ways: use in the setup procedure the first tab Select from " "Addressbook for to select your on address in KAddressBook (if you have " "filled your own address in KaddressBook) or use the second tab Manual entry " "for to fill in the information of the address from your company by " "hand. This step is necessary for the correct generation of your documents." msgstr "" #. type: Plain text #: kraft.adoc:49 msgid "" "After the initial setup, select menu:Preferences[Settings]. That allows to " "prepare Kraft correctly so it can be used in a useful way." msgstr "" #. type: Plain text #: kraft.adoc:51 msgid "In the Preferences window we have the tabs:" msgstr "" #. type: Plain text #: kraft.adoc:58 #, no-wrap msgid "" " *Document Defaults\n" " *Taxes\n" " *Documunt Types\n" " *Wages\n" " *Units\n" " *Own identity\n" msgstr "" #. type: Plain text #: kraft.adoc:60 msgid "Each of the tabs allows to enter useful values for our use case." msgstr "" #. type: Title === #: kraft.adoc:61 #, no-wrap msgid "Document Types" msgstr "" #. type: Plain text #: kraft.adoc:64 msgid "At the first use you find a list of different document types, such as:" msgstr "" #. type: Plain text #: kraft.adoc:66 msgid "Acceptance of order" msgstr "" #. type: Plain text #: kraft.adoc:67 msgid "Delivery receipt" msgstr "" #. type: Plain text #: kraft.adoc:68 msgid "Invoice" msgstr "" #. type: Plain text #: kraft.adoc:69 msgid "Offer" msgstr "" #. type: Plain text #: kraft.adoc:72 msgid "" "Translate this types to your own language. You can also add new ones and " "remove document types you wont use." msgstr "" #. type: Title ==== #: kraft.adoc:73 #, no-wrap msgid "Numbercycles" msgstr "" #. type: Plain text #: kraft.adoc:78 msgid "" "image:numbercycles.png[Numbercycles,float=\"right\"] Numbercycles are used " "to define the *document number* which is printed on every document. The " "document number is an important unique identifier of the document and often " "must follow regulations." msgstr "" #. type: Plain text #: kraft.adoc:82 msgid "" "Different document types can use the same number cycle to generate ids " "from. Number cycles are identified by their name. User can create new " "number cycles and edit them clicking on the button btn:[Edit Number " "Cycles...]" msgstr "" #. type: Plain text #: kraft.adoc:85 msgid "" "Kraft supports a counter that is incremented for every new document of a " "certain type. In addition to the counter, more information can be added to " "form an useful number, such as constant text or parts of the date." msgstr "" #. type: Plain text #: kraft.adoc:87 msgid "See the following table for available variables which can be used:" msgstr "" #. type: Table #: kraft.adoc:103 #, no-wrap msgid "" "| `%y` or `%yyyy` | the year of the document date.\n" "| `%yy` | the year of the document (two digits).\n" "| `%w` | the week number of the document date.\n" "| `%ww` | the week number of the document date with leading " "zero.\n" "| `%d` | the day number of the document date.\n" "| `%dd` | the day number of the document date with leading zero.\n" "| `%m` or `%M` | the month number of the document date.\n" "| `%MM` | the month number with leading zero.\n" "| `%c` | the customer id from kaddressbook\n" "| `%i` | the unique counter *(mandatory)*\n" "| `%type` | the localised doc type (offer, invoice etc.)\n" "| `%uid` | the contact id of the client.\n" "\n" msgstr "" #. type: Title === #: kraft.adoc:105 #, no-wrap msgid "Taxes" msgstr "" #. type: Plain text #: kraft.adoc:108 msgid "In many countries there are two kinds of VAT-taxes for sold products." msgstr "" #. type: Plain text #: kraft.adoc:111 msgid "A high level and a low level." msgstr "" #. type: Plain text #: kraft.adoc:114 msgid "" "Fill here the appropriate amounts in for the high level and the low level. " "If the tax-level is changing, then you add here the start date with the new " "tax-levels." msgstr "" #. type: Title === #: kraft.adoc:115 #, no-wrap msgid "Wages" msgstr "" #. type: Plain text #: kraft.adoc:119 msgid "" "A list of wage costs is maintained in Kraft. The items are used in templates " "and during calculation." msgstr "" #. type: Plain text #: kraft.adoc:122 msgid "" "All data can be edited, customized and new items can be added in the Kraft " "Configuration Dialog reachable through the Settings menu." msgstr "" #. type: Plain text #: kraft.adoc:126 msgid "" "Remember that these units are later used in the documents, it is therefor " "important that you translate them to your own language and to fill in the " "correct prices." msgstr "" #. type: Title === #: kraft.adoc:127 #, no-wrap msgid "Units of measurement" msgstr "" #. type: Plain text #: kraft.adoc:131 msgid "" "A list of units of measurement is maintained in Kraft. In Kraft " "Configuration Dialog reachable through the Settings menu can you edit and " "customize items already in the list, and also can you add new items to the " "list." msgstr "" #. type: Plain text #: kraft.adoc:134 msgid "" "Remember that these units are later used in the documents, it is therefor " "important that you translate them to your own language." msgstr "" #. type: Title === #: kraft.adoc:135 #, no-wrap msgid "Own identity" msgstr "" #. type: Plain text #: kraft.adoc:139 msgid "" "Check here if the information that you have given during the initial setup " "is correct for the use in the documents." msgstr "" #. type: delimited block _ #: kraft.adoc:144 msgid "" "WARNING: If you made the choice to use the information from KaddressBook " "then is the information from a later manual entry ignored." msgstr "" #. type: Plain text #: kraft.adoc:148 msgid "" "After we have made some corrections to the configuration, we go back to the " "main window.Here we see three tabs:" msgstr "" #. type: Plain text #: kraft.adoc:150 msgid "Documents" msgstr "" #. type: Plain text #: kraft.adoc:151 msgid "Timeline" msgstr "" #. type: Plain text #: kraft.adoc:152 msgid "Catalogs" msgstr "" #. type: Plain text #: kraft.adoc:154 msgid "We go first to catalogs." msgstr "" #. type: Title === #: kraft.adoc:155 #, no-wrap msgid "Catalog" msgstr "" #. type: Plain text #: kraft.adoc:158 msgid "In the tab catalog are two different catalogs:" msgstr "" #. type: Plain text #: kraft.adoc:160 msgid "`Material`" msgstr "" #. type: Plain text #: kraft.adoc:163 msgid "" "A catalog of material that are sold, with their purchase prices, the profit " "and the sell-price." msgstr "" #. type: Plain text #: kraft.adoc:165 msgid "and `Standard Templates`" msgstr "" #. type: Plain text #: kraft.adoc:167 msgid "A catalog of standard recipes of work like planting trees." msgstr "" #. type: Plain text #: kraft.adoc:170 msgid "" "Both catalogs can have chapters and sub-chapters for to organize your " "templates. First we are going to fill in the" msgstr "" #. type: Title ==== #: kraft.adoc:171 #, no-wrap msgid "Material-catalog" msgstr "" #. type: Plain text #: kraft.adoc:176 msgid "" "A catalog of material that are sold, with their purchase prices, the profit " "and the sell-price. First we are going to add new chapters and subchapters." msgstr "" #. type: Title ===== #: kraft.adoc:177 #, no-wrap msgid "New chapters" msgstr "" #. type: Plain text #: kraft.adoc:181 msgid "" "Select with the mouse the column-name `material`, select now in the " "context-menu [Add a sub chapter]" msgstr "" #. type: Plain text #: kraft.adoc:183 msgid "and add an extra chapter like `Trees`" msgstr "" #. type: Title ===== #: kraft.adoc:184 #, no-wrap msgid "New sub chapters" msgstr "" #. type: Plain text #: kraft.adoc:192 msgid "" "We are going to ad sub chapters in the map `Trees`. Select with the mouse " "the name of the chapter where you like to add a subchapter, select now in " "the context-menu [Add a sub chapter] and ad an extra subchapters like `Loaf " "trees` and `needle trees`. After adding the extra chapters and subchapters " "for dividing the material, we are going to add the material themself." msgstr "" #. type: Title ===== #: kraft.adoc:193 #, no-wrap msgid "New template" msgstr "" #. type: Plain text #: kraft.adoc:198 msgid "" "Select with the mouse the name of the sub-chapter or chapter where you like " "to add a material. Select the sub map Loaf trees and select now in the " "context-menu" msgstr "" #. type: Plain text #: kraft.adoc:201 msgid "Add the extra materials `coconut tree`, `apple tree` and `pine-apple tree`." msgstr "" #. type: Plain text #: kraft.adoc:203 msgid "Fill in the price that we have paid." msgstr "" #. type: Plain text #: kraft.adoc:205 msgid "Fill in the profit that we want to have on the material" msgstr "" #. type: Plain text #: kraft.adoc:207 msgid "And fill in how much is in a packet." msgstr "" #. type: Plain text #: kraft.adoc:209 msgid "Now we are going to:" msgstr "" #. type: Title ==== #: kraft.adoc:210 #, no-wrap msgid "Standard Templates" msgstr "" #. type: Plain text #: kraft.adoc:213 msgid "This is a catalog of standard recipes of work like:" msgstr "" #. type: Plain text #: kraft.adoc:215 msgid "planting trees" msgstr "" #. type: Plain text #: kraft.adoc:216 msgid "cutting grass" msgstr "" #. type: Plain text #: kraft.adoc:217 msgid "transport costs" msgstr "" #. type: Plain text #: kraft.adoc:218 msgid "planting grass" msgstr "" #. type: Plain text #: kraft.adoc:219 msgid "sowing grass-seed" msgstr "" #. type: Plain text #: kraft.adoc:221 msgid "We add here the standard work of planting a tree." msgstr "" #. type: Plain text #: kraft.adoc:224 msgid "" "Select with the mouse the name of the chapter [Work] where you like to add " "the new template," msgstr "" #. type: Plain text #: kraft.adoc:226 msgid "select now the context-menu [New template]" msgstr "" #. type: Plain text #: kraft.adoc:228 msgid "and the extra templates `Plant tree` and `cut grass`." msgstr "" #. type: Plain text #: kraft.adoc:230 msgid "After we made the new template, a window opens with 4 tabs:" msgstr "" #. type: Title ===== #: kraft.adoc:232 kraft.adoc:238 #, no-wrap msgid "Template" msgstr "" #. type: Title ===== #: kraft.adoc:233 kraft.adoc:250 #, no-wrap msgid "Time calculation" msgstr "" #. type: Plain text #: kraft.adoc:234 msgid "Fix costs" msgstr "" #. type: Title ===== #: kraft.adoc:235 kraft.adoc:285 #, no-wrap msgid "Material" msgstr "" #. type: Plain text #: kraft.adoc:237 msgid "First we go to the tab:" msgstr "" #. type: Plain text #: kraft.adoc:241 msgid "We give here the name of the new standard template like `Plant tree`" msgstr "" #. type: delimited block _ #: kraft.adoc:245 msgid "WARNING: be careful, this name is later used in the invoice" msgstr "" #. type: Plain text #: kraft.adoc:249 msgid "" "we select that this is per piece and that the margin is 8% and that the full " "VAT is applicable." msgstr "" #. type: Plain text #: kraft.adoc:253 msgid "We fill here in a number of work with the time:" msgstr "" #. type: Block title #: kraft.adoc:254 #, no-wrap msgid "Spent time" msgstr "" #. type: Table #: kraft.adoc:261 #, no-wrap msgid "" "|Dig hole |32 min. |worker\n" "|Place tree |12 min. |worker\n" "|Fill hole |17 min. |worker\n" "|give water |5 min. |worker\n" msgstr "" #. type: Plain text #: kraft.adoc:264 msgid "The cost for worker which we have earlier filled in is now used." msgstr "" #. type: delimited block _ #: kraft.adoc:269 msgid "" "NOTE: in the invoice we see later only Plant tree, we will not see the parts " "dig hole,place tree,fill hole,give water" msgstr "" #. type: Plain text #: kraft.adoc:272 msgid "Now we go to the tab" msgstr "" #. type: Title ===== #: kraft.adoc:273 #, no-wrap msgid "Fixed costs" msgstr "" #. type: Plain text #: kraft.adoc:276 msgid "and fill in:" msgstr "" #. type: Block title #: kraft.adoc:277 #, no-wrap msgid "Fixed item" msgstr "" #. type: Table #: kraft.adoc:281 #, no-wrap msgid "|Transportcost |35 euro |1 pcs.\n" msgstr "" #. type: Plain text #: kraft.adoc:284 msgid "After this we go to the tab:" msgstr "" #. type: Plain text #: kraft.adoc:288 msgid "Here we fill in:" msgstr "" #. type: Block title #: kraft.adoc:289 #, no-wrap msgid "Used materials" msgstr "" #. type: Table #: kraft.adoc:293 #, no-wrap msgid "|1 |support pole |3,5 euro\n" msgstr "" #. type: Plain text #: kraft.adoc:296 msgid "We go now back to the first tab template" msgstr "" #. type: Plain text #: kraft.adoc:299 msgid "On the first tab [template], we can now see the overall cost per one unit" msgstr "" #. type: Plain text #: kraft.adoc:302 msgid "" "Click on [OK] for saving the result or on [cancel] for discarding the " "result." msgstr "" #. type: Plain text #: kraft.adoc:304 msgid "We make a second template `cut grass`" msgstr "" #. type: Plain text #: kraft.adoc:307 msgid "" "we fill in `cut grass`, as unit we choose sm (square meter), on the second " "tab we fill in that we need 3 min per square meter." msgstr "" #. type: Plain text #: kraft.adoc:310 msgid "" "Click on [OK] for saving the result or on [Cancel] for discarding the " "result." msgstr "" #. type: Plain text #: kraft.adoc:312 msgid "We are now ready for the first invoice." msgstr "" #. type: Title == #: kraft.adoc:314 #, no-wrap msgid "Creating Documents" msgstr "" #. type: Title === #: kraft.adoc:316 #, no-wrap msgid "The first Invoice" msgstr "" #. type: Plain text #: kraft.adoc:319 msgid "Open the tab btn:[documents]" msgstr "" #. type: Plain text #: kraft.adoc:321 kraft.adoc:426 msgid "Click on btn:[create document]" msgstr "" #. type: Plain text #: kraft.adoc:323 msgid "The window document [creation wizard opens]." msgstr "" #. type: Plain text #: kraft.adoc:325 msgid "Select in document type `invoice`." msgstr "" #. type: Plain text #: kraft.adoc:328 msgid "" "Fill in on the whiteboard content a short text about what the invoice is, " "like: `cut grass and planted tree for mister Jonson`" msgstr "" #. type: Plain text #: kraft.adoc:330 kraft.adoc:435 msgid "Click on btn:[next]" msgstr "" #. type: Plain text #: kraft.adoc:332 kraft.adoc:437 msgid "Select on the new window the name and address from the client." msgstr "" #. type: Plain text #: kraft.adoc:335 kraft.adoc:440 msgid "" "(if the name and address is not there, click then on btn:[new contact] or on " "btn:[edit contact] if you want to edit the contact)" msgstr "" #. type: Plain text #: kraft.adoc:337 kraft.adoc:442 msgid "Click on btn:[OK]." msgstr "" #. type: Plain text #: kraft.adoc:339 msgid "Now opens the window document [items]." msgstr "" #. type: Plain text #: kraft.adoc:341 msgid "this window has 2 tabs and the 3 buttons on the top:" msgstr "" #. type: Plain text #: kraft.adoc:343 kraft.adoc:448 msgid "btn:[Add item...]," msgstr "" #. type: Plain text #: kraft.adoc:344 kraft.adoc:449 msgid "btn:[Add discount item]," msgstr "" #. type: Plain text #: kraft.adoc:345 kraft.adoc:450 msgid "btn:[Show templates]." msgstr "" #. type: Plain text #: kraft.adoc:349 msgid "" "In the left tab you can see all the items that we want to place on the " "invoice, on the right tab we see the text from the header, the total price " "and the footer." msgstr "" #. type: Plain text #: kraft.adoc:353 msgid "" "If you click on the text of the header or the footer on the right side then " "the window changes in such a way that you can edit the header or the footer." msgstr "" #. type: Plain text #: kraft.adoc:356 msgid "" "Adapt the header and the footer to your situation, on the footer you can " "place a text: `We make your garden-dream come to reality.`." msgstr "" #. type: Plain text #: kraft.adoc:358 kraft.adoc:452 msgid "Click on the button btn:[Show templates]." msgstr "" #. type: Plain text #: kraft.adoc:362 msgid "" "The right tab changes and show now the earlier made templates, we select in " "the group Work, the subgroup Plant tree and click then on the button with " "the to the left pointing arrow on the bottom side." msgstr "" #. type: Plain text #: kraft.adoc:364 kraft.adoc:458 msgid "A new window [Create Item from Template] opens." msgstr "" #. type: Plain text #: kraft.adoc:367 msgid "" "Because we have planted 2 trees, we go to the field [insert] and change this " "to 2 pcs." msgstr "" #. type: Plain text #: kraft.adoc:370 msgid "" "Click on btn:[OK] for saving the result or on btn:[cancel] for discarding " "the result." msgstr "" #. type: Plain text #: kraft.adoc:372 kraft.adoc:466 kraft.adoc:477 msgid "The window close and we go back to the main window." msgstr "" #. type: Plain text #: kraft.adoc:376 msgid "" "We click again on btn:[Show templates] and select this time `cut grass`, we " "click again on the button with the arrow, in the opened window we select " "that the grass-field was 24 square meter." msgstr "" #. type: Plain text #: kraft.adoc:379 kraft.adoc:464 kraft.adoc:497 kraft.adoc:529 msgid "" "Click on btn:[OK] for saving the result or on btn:[Cancel] for discarding " "the result." msgstr "" #. type: Plain text #: kraft.adoc:381 msgid "" "We add now manually an item by clicking on the button btn:[Add item…] and " "the window [create new item] opens." msgstr "" #. type: Plain text #: kraft.adoc:385 msgid "" "Because we have delivered a special tree, we fill here in the name of the " "special tree `liguster`, at the field insert we fill in the number of the " "special trees that we have delivered and the price of them." msgstr "" #. type: delimited block _ #: kraft.adoc:391 msgid "" "WARNING: Remind that in the catalog we can add a profit on the price of the " "material, in the invoice and in the offer we can not add a profit on the " "price of the material." msgstr "" #. type: Plain text #: kraft.adoc:394 msgid "We have now an invoice with 3 items." msgstr "" #. type: Plain text #: kraft.adoc:397 msgid "" "Click on btn:[OK] for saving the invoice or on btn:[Cancel] for discarding " "the invoice." msgstr "" #. type: Plain text #: kraft.adoc:399 msgid "We click on btn:[OK] and save the result." msgstr "" #. type: Plain text #: kraft.adoc:401 msgid "Your first invoice is now ready for sending." msgstr "" #. type: Plain text #: kraft.adoc:404 msgid "" "In the window documents we see our first invoice, notice that this document " "has a document number which we can see on the left side." msgstr "" #. type: Plain text #: kraft.adoc:407 msgid "" "On top of the window with all the invoices we see the button [Print " "Document], on which we click." msgstr "" #. type: Plain text #: kraft.adoc:410 msgid "" "From the invoice will now a PDF be made which we can print on paper or send " "by email to the client." msgstr "" #. type: Plain text #: kraft.adoc:412 msgid "After this we are going to create a offer for some work in a garden." msgstr "" #. type: Title === #: kraft.adoc:414 #, no-wrap msgid "Creating an Offer" msgstr "" #. type: Plain text #: kraft.adoc:417 msgid "" "The client has asked to plant a tree, we will offer 3 different trees which " "we can plant." msgstr "" #. type: Plain text #: kraft.adoc:420 msgid "" "Beside this, we have seen that there is a lifeless three, which we will " "offer to remove as extra work. " "image:create_new_doc.png[Numbercycles,float=\"right\"]" msgstr "" #. type: Plain text #: kraft.adoc:422 msgid "" "For the total price we do not want to show the price of the removal of the " "lifeless tree and we want for the total price only to show the price of one " "tree and not three." msgstr "" #. type: Plain text #: kraft.adoc:424 msgid "Open again the tab btn:[documents]." msgstr "" #. type: Plain text #: kraft.adoc:428 msgid "The window _Document Creation Wizard_ opens." msgstr "" #. type: Plain text #: kraft.adoc:430 msgid "select in btn:[document type] > btn:[Offer]." msgstr "" #. type: Plain text #: kraft.adoc:433 msgid "" "Fill in on the whiteboard content a short text about what the offer is, " "like: `plant one tree and removal of lifeless tree`" msgstr "" #. type: Plain text #: kraft.adoc:444 msgid "Now the window [edit document] opens." msgstr "" #. type: Plain text #: kraft.adoc:446 msgid "This window has 2 tabs and the 3 buttons on the top:" msgstr "" #. type: Plain text #: kraft.adoc:456 msgid "" "The right tab changes and show now the earlier made templates, we select in " "the group `Work`, the subgroup `Plant tree` and click then on the button " "with the to the left pointing arrow on the bottom side." msgstr "" #. type: Plain text #: kraft.adoc:461 msgid "" "Because we want to plant 1 tree, we go to the field [insert] and keep this " "on 1 pcs." msgstr "" #. type: Plain text #: kraft.adoc:469 msgid "" "We click on the button btn:[Show templates] and this time we select in " "catalog Material" msgstr "" #. type: Plain text #: kraft.adoc:473 msgid "" "The material-catalog opens, and we can select in the chapter `trees` the " "subchapter `loaf trees` in which we select the `apple tree` which we made " "earlier." msgstr "" #. type: Plain text #: kraft.adoc:475 msgid "" "Click on we btn:[OK] for saving the result or on btn:[cancel] for discarding " "the result." msgstr "" #. type: Plain text #: kraft.adoc:479 msgid "We add now manually an item by clicking on the button `Add item…`." msgstr "" #. type: Plain text #: kraft.adoc:481 msgid "the window [create new item] opens." msgstr "" #. type: Plain text #: kraft.adoc:483 msgid "" "We want that the client can make a choice from an apple, a pear tree and the " "liguster." msgstr "" #. type: Plain text #: kraft.adoc:485 msgid "Therefor we are going to add also a pear tree manually." msgstr "" #. type: Plain text #: kraft.adoc:487 msgid "" "We click on the button btn:[Add item…] and the window [create new item] " "opens." msgstr "" #. type: Plain text #: kraft.adoc:491 msgid "" "We fill here in the name of the tree `Pear tree`, at the field insert we " "fill in the number of the special trees that we have delivered and the price " "of them." msgstr "" #. type: Plain text #: kraft.adoc:494 msgid "" "We want add this to the material catalog for future use, therefor we select " "also [select this item as template for future documents] and we select in " "[save in chapter]`trees`." msgstr "" #. type: Plain text #: kraft.adoc:499 msgid "We does this again but then for the liguster." msgstr "" #. type: Plain text #: kraft.adoc:501 msgid "We have now 3 items with trees in the offer." msgstr "" #. type: Plain text #: kraft.adoc:503 msgid "As last item we add an item with `remove tree` with 0,5 hour for 32 euro." msgstr "" #. type: Plain text #: kraft.adoc:505 msgid "On the left side of an item we can see 2 buttons:" msgstr "" #. type: Plain text #: kraft.adoc:507 msgid "a button with a flag and a button with what looks like a page." msgstr "" #. type: Plain text #: kraft.adoc:510 msgid "" "We select the upper button with the page after which opens a context-menu " "with the items:" msgstr "" #. type: Plain text #: kraft.adoc:520 #, no-wrap msgid "" " [Item kind]->[Normal]\n" " [Item kind]>[Alternative]\n" " [Item kind]>[On demand]\n" " [Tax]\n" " [Move up]\n" " [Move down]\n" " [Lock item]\n" " [Unlock item]\n" " [Delete item]\n" msgstr "" #. type: Plain text #: kraft.adoc:523 msgid "" "We choose here [Item kind] and change for `pear tree` from [normal] to " "[alternative]." msgstr "" #. type: Plain text #: kraft.adoc:526 msgid "" "We do this also for [liguster] and for [remove tree] we change this from " "[normal] to [on demand]." msgstr "" #. type: Plain text #: kraft.adoc:532 msgid "" "We want to see the result and therefor we click on the button [show " "document]." msgstr "" #. type: Plain text #: kraft.adoc:538 msgid "" "We see now that the prize of the pear tree, the liguster and the removal of " "the tree is not used for the total prize. When we are happy with the result, " "we can click on the button btn:[close] after which we click on the button " "btn:[Print Document] for making a PDF what we can print out or send to the " "client." msgstr "" #. type: Plain text #: kraft.adoc:541 msgid "After your first invoice is now your first offer now also ready for sending." msgstr "" #. type: Title == #: kraft.adoc:543 #, no-wrap msgid "Customization" msgstr "" #. type: Plain text #: kraft.adoc:547 msgid "" "Kraft can be customized in most of the graphical user interface and in " "particular in the output it generates." msgstr "" #. type: Title === #: kraft.adoc:548 #, no-wrap msgid "Output Document Customization" msgstr "" #. type: Plain text #: kraft.adoc:551 msgid "" "To create PDF output documents, the document data that was edited in the " "Kraft app is filled into a template. The template defines how the output " "document looks like, ie. by font settings, placing of elements and such." msgstr "" #. type: Plain text #: kraft.adoc:553 msgid "" "The file that is assembled from data and the template is converted to PDF " "using a special document creation script. All that is started automatically " "by Kraft if a document should be printed." msgstr "" #. type: Plain text #: kraft.adoc:555 msgid "" "Each document type in Kraft can have it's own template that is used to " "create a PDF. Which one can be set in the Settings dialog for document " "types." msgstr "" #. type: Title ==== #: kraft.adoc:556 #, no-wrap msgid "WeasyPrint Documents" msgstr "" #. type: Plain text #: kraft.adoc:559 msgid "" "With https://weasyprint.org[WeasyPrint] Kraft uses a very powerful HTML and " "CSS based generator that makes it very easy to create highly customized " "documents which fit the users expectations. The general idea is that " "Weasyprint loads html output that is processed to PDF. Usually it is " "considering a Cascading Stylesheet file which has a huge impact on how the " "PDF document looks in the end." msgstr "" #. type: Plain text #: kraft.adoc:561 msgid "" "To use a WeasyPrint based template for a document simply create a template " "file and save it with the extension *.gtmpl*. With that file extension Kraft " "automatically uses WeasyPrint and also the Grantlee templating for " "rendering." msgstr "" #. type: Plain text #: kraft.adoc:563 msgid "" "An example for a WeasyPrint document can be found in the Kraft package in " "the reports directory and is called invoice.gtmpl." msgstr "" #. type: Plain text #: kraft.adoc:565 msgid "" "To use a WeasyPrint template with one of the Kraft document types just " "select the template file name (with the right extension `*.gtml`) in the " "Kraft Settings Dialog." msgstr "" #. type: Plain text #: kraft.adoc:567 msgid "" "From version 0.95 on Kraft ships with an example document in the Grantlee- " "and WeasyPrint format. It can be found at " "`/usr/share/kraft/reports/invoice.gtmpl` or " "https://github.com/dragotin/kraft/blob/master/reports/invoice.gtmpl[online " "on Github]." msgstr "" #. type: Plain text #: kraft.adoc:569 msgid "" "To effectively change the look of the document `kraft.css` " "(https://github.com/dragotin/kraft/blob/master/reports/kraft.css[on Github]) " "needs to be considered. It defines most of the look." msgstr "" #. type: Title ==== #: kraft.adoc:570 #, no-wrap msgid "Template Variables" msgstr "" #. type: Plain text #: kraft.adoc:573 msgid "" "To generate the PDF, Kraft has to transfer data from the document you have " "been working on in Kraft to the input document that is processed to an PDF " "utilising WeasyPrint. For that, Kraft uses a text template. In that, Kraft " "replaces variables with the actual values." msgstr "" #. type: Plain text #: kraft.adoc:575 msgid "" "The syntax is based on the Django syntax for templates described in the " "https://docs.djangoproject.com/en/3.1/topics/templates/[the docs]." msgstr "" #. type: Title == #: kraft.adoc:579 #, no-wrap msgid "Menus and Shortcuts" msgstr "" #. type: Title === #: kraft.adoc:581 #, no-wrap msgid "Main Application Menu" msgstr "" #. type: Title ==== #: kraft.adoc:584 #, no-wrap msgid "The File Menu" msgstr "" #. type: Plain text #: kraft.adoc:589 #, no-wrap msgid "" " [File]>[Quit]\n" " [Ctrl]+[Q]\n" " Quits the application.\n" msgstr "" #. type: Title ==== #: kraft.adoc:591 #, no-wrap msgid "The Document Menu" msgstr "" #. type: Plain text #: kraft.adoc:597 #, no-wrap msgid "" " [Document]>[Show Document]\n" " [Ctrl]+[R]\n" " Opens a window with the selected document for showing it.\n" msgstr "" #. type: Plain text #: kraft.adoc:601 #, no-wrap msgid "" " [Document]>[Edit Document]\n" " [Ctrl+O]\n" " Opens a window with the selected document for editing it.\n" msgstr "" #. type: Plain text #: kraft.adoc:605 #, no-wrap msgid "" " [Document]>[Open Archived document]\n" " [Ctrl]+[A]\n" " Opens an archived document.\n" msgstr "" #. type: Plain text #: kraft.adoc:608 #, no-wrap msgid "" " [Document]>[Create Document]\n" " Opens a window with a wizard for creating a new client-document.\n" msgstr "" #. type: Plain text #: kraft.adoc:612 #, no-wrap msgid "" " [Document]>[Copy Document]\n" " Makes a copy of the selected client-document to a new client-document\n" " which can belong to an other client or an other documenttype.\n" msgstr "" #. type: Plain text #: kraft.adoc:615 #, no-wrap msgid "" " [Document]>[Follow Document]\n" " Opens the selected client-document for editing.\n" msgstr "" #. type: Plain text #: kraft.adoc:619 #, no-wrap msgid "" " [Document]>[Print document]\n" " Makes a PDf from the selected client-document for to be mailed or\n" " printed.\n" msgstr "" #. type: Plain text #: kraft.adoc:623 #, no-wrap msgid "" " [Document]>[Mail document]\n" " [Ctrl]+[M]\n" " Mails a document.\n" msgstr "" #. type: Title ==== #: kraft.adoc:626 #, no-wrap msgid "The Settings menu" msgstr "" #. type: Plain text #: kraft.adoc:632 #, no-wrap msgid "" " [Settings]>[Edit Tag Templates]\n" " [Ctrl]+[E]\n" " Opens a window where you add, edit or translate the tags (like work,\n" " material, plants or discounts).\n" msgstr "" #. type: Plain text #: kraft.adoc:636 #, no-wrap msgid "" " [Settings]>[Redo initial setup]\n" " [Ctrl+R]\n" " Redoes the initial setup. After this, a restart of Kraft is required.\n" msgstr "" #. type: Plain text #: kraft.adoc:640 #, no-wrap msgid "" " [Settings]>[Showed toolbars]\n" " Here you can decide if the `main toolbar` and the toolbar `Document " "Actions`\n" " are shown.\n" msgstr "" #. type: Plain text #: kraft.adoc:644 #, no-wrap msgid "" " [Settings]>[Configure Kraft]\n" " [Ctrl]+[Shft]+[,]\n" " Here you can configure Kraft.\n" msgstr "" #. type: Title === #: kraft.adoc:645 #, no-wrap msgid "Document Edit Window" msgstr "" #. type: Title ==== #: kraft.adoc:648 #, no-wrap msgid "The context Menu" msgstr "" #. type: Plain text #: kraft.adoc:652 #, no-wrap msgid "" " [Context]>[Item kind]\n" " change the status from this item between\n" msgstr "" #. type: Plain text #: kraft.adoc:653 #, no-wrap msgid "Normal\n" msgstr "" #. type: Plain text #: kraft.adoc:654 #, no-wrap msgid "Alternative\n" msgstr "" #. type: Plain text #: kraft.adoc:655 #, no-wrap msgid "On demand\n" msgstr "" #. type: Plain text #: kraft.adoc:658 #, no-wrap msgid "" " [Context]>[Tax]\n" " Seems not working.\n" msgstr "" #. type: Plain text #: kraft.adoc:661 #, no-wrap msgid "" " [Context]>[Move up]\n" " Moves this item a place up in document.\n" msgstr "" #. type: Plain text #: kraft.adoc:664 #, no-wrap msgid "" " [Context]>[Move down]\n" " Moves this item a place down in document.\n" msgstr "" #. type: Plain text #: kraft.adoc:667 #, no-wrap msgid "" " [Context]>[Lock item]\n" " It is not clear what is does.\n" msgstr "" #. type: Plain text #: kraft.adoc:670 #, no-wrap msgid "" " [Context]>[Unlock item]\n" " It is not clear what is does.\n" msgstr "" #. type: Plain text #: kraft.adoc:673 #, no-wrap msgid "" " [Context]>[Delete item]\n" " Removes this item from document.\n" msgstr "" #. type: Title == #: kraft.adoc:676 #, no-wrap msgid "Advanced Topics" msgstr "" #. type: Plain text #: kraft.adoc:679 msgid "" "This chapter describes advanced topics around Kraft. Some Linux knowledge is " "required, and setups should be done by experienced Linux administrators and " "should be tested carefully." msgstr "" #. type: Title === #: kraft.adoc:680 #, no-wrap msgid "Using Kraft Collaborative" msgstr "" #. type: Plain text #: kraft.adoc:683 msgid "" "Kraft can be used collaborative in a distributed environment. That means " "that multiple users work on their desktops with their own Kraft instance on " "the same data." msgstr "" #. type: Plain text #: kraft.adoc:685 msgid "" "This whole topic is a subject to change, as Kraft will evolve to use " "ownCloud as a private cloud solution to store the data." msgstr "" #. type: Title ==== #: kraft.adoc:686 #, no-wrap msgid "Sharing Database and Document Pool" msgstr "" #. type: Plain text #: kraft.adoc:689 msgid "" "The simplest case is that two or more Kraft instances use a database " "together and access the same pool of PDF documents on the harddisk. For " "simplicity this describes only two Kraft instances." msgstr "" #. type: Plain text #: kraft.adoc:691 msgid "" "A typical use case would be: Two different Linux users want to use " "Kraft. They both have their own computer but only work in the same " "network. For example this would describe a situation with one main office " "machine that runs Kraft in normal mode, plus a notebook with Kraft in read " "only mode to view documents, check catalogs and such." msgstr "" #. type: Plain text #: kraft.adoc:693 msgid "For that, the following prerequisites have to be met:" msgstr "" #. type: Plain text #: kraft.adoc:695 msgid "MySQL or MariaDB is used as database backend. Sqlite is not supported." msgstr "" #. type: Plain text #: kraft.adoc:696 msgid "" "The database is accessible with a mysql user and from each machine for both " "users." msgstr "" #. type: Plain text #: kraft.adoc:697 msgid "The document store directory has to be shared." msgstr "" #. type: delimited block _ #: kraft.adoc:701 msgid "" "WARNING: There is no protection against having both users editing the same " "document. Because that is dangerous and can lead to unpredictable results, " "it is recommended to run all instances of Kraft except the main one in read " "only mode. This is done by starting Kraft with the `-r` command line switch." msgstr "" #. type: Plain text #: kraft.adoc:704 #, no-wrap msgid "**Sharing the Database**\n" msgstr "" #. type: Plain text #: kraft.adoc:706 msgid "" "The database server should be installed on the main machine or a dedicated " "device like a NAS. Networking speed influences the comfort to use obviously." msgstr "" #. type: Plain text #: kraft.adoc:708 msgid "Find howtos on the internet how to setup MySQL accordingly." msgstr "" #. type: Plain text #: kraft.adoc:710 #, no-wrap msgid "**Sharing the Document Pool Directory**\n" msgstr "" #. type: Plain text #: kraft.adoc:712 msgid "" "Kraft writes generated PDF documents into a local directory. Where that is " "can be configured in the Kraft Config file. The config file has to be " "adopted on all instances." msgstr "" #. type: Plain text #: kraft.adoc:714 msgid "" "That is located in each users home directory, in the path " "`.config/kraftrc`. It has to contain the following config value:" msgstr "" #. type: Plain text #: kraft.adoc:716 kraft.adoc:720 msgid "```" msgstr "" #. type: Plain text #: kraft.adoc:718 msgid "PdfOutputDir=/data/space/kraftdoc/pdf" msgstr "" #. type: Plain text #: kraft.adoc:722 msgid "" "There are different ways how share that directory, ie. NFS or SMB " "storages. It is important that both users from both machines can list and " "access the files. The main user needs read and write access, read only users " "only need read access to the files." msgstr "" #. type: Plain text #: kraft.adoc:724 msgid "" "A recommended setup is a NFS share with autofs which is set up on the main " "machine. To manage file access a certain group should be set up on the " "machines with which access can be managed." msgstr "" #. type: Plain text #: kraft.adoc:726 #, no-wrap msgid "**Starting Kraft in read-only mode**\n" msgstr "" #. type: Plain text #: kraft.adoc:728 msgid "" "To start Kraft in read-only mode, start the binary with the `-r` command " "line switch." msgstr "" #. type: Title == #: kraft.adoc:730 #, no-wrap msgid "Credits and License" msgstr "" #. type: Plain text #: kraft.adoc:733 msgid "Program and documentation copyright 2004–2021 Klaas Freitag" msgstr "" #. type: Plain text #: kraft.adoc:734 msgid "Documentation copyright 2020 Ronald Stroethoff" msgstr "" kraft-0.97/meta/000077500000000000000000000000001410616450300135065ustar00rootroot00000000000000kraft-0.97/meta/CMakeLists.txt000066400000000000000000000004201410616450300162420ustar00rootroot00000000000000########### install files ############### install(FILES kraft.desktop DESTINATION ${XDG_APPS_INSTALL_DIR} RENAME de.volle_kraft_voraus.kraft.desktop) install(FILES kraft.appdata.xml DESTINATION ${KDE_INSTALL_METAINFODIR} RENAME de.volle_kraft_voraus.kraft.appdata.xml) kraft-0.97/meta/kraft.appdata.xml000066400000000000000000000034771410616450300167630ustar00rootroot00000000000000 de.volle_kraft_voraus.kraft.desktop GPL-2.0+ CC0-1.0 Kraft Kraft helps to handle documents in small business

Kraft is free software to help to handle documents like quotes and invoices in your small business. It is a Qt/KF5 based desktop software with a strong focus on ease of use and the just enough feature set for the use case. With Kraft, creating documents will run smooth and free time for more enjoyable things than office work.

Kraft runs on any Linux desktop. There is no cloud involved, your data around your products and customers stays under your control.

With Kraft, writing documents like quotes and invoices is very easy and fast. Repeating tasks are supported, documents can be generated semi automatically, ie. invoices from offers sent out before.

For efficient work, Kraft supports catalogs to organize materials and template texts. It focuses on high quality printouts because paper is still the main communication media in the small business world. However, it also sends documents via email.

https://volle-kraft-voraus.de/images/appmeta1.png Main window of Kraft https://volle-kraft-voraus.de/images/appmeta2.png Document edit window, header section https://volle-kraft-voraus.de/ kraft_AT_volle-kraft-voraus.de kraft
kraft-0.97/meta/kraft.desktop000066400000000000000000000010111410616450300162010ustar00rootroot00000000000000[Desktop Entry] Type=Application Exec=kraft %u Icon=kraft X-DocPath=kraft/index.html Terminal=false Name=Kraft Name[ast]=Kraft Name[bs]=Kraft Name[cs]=Kraft Name[da]=Kraft Name[de]=Kraft Name[es]=Kraft Name[et]=Kraft Name[fi]=Kraft Name[fr]=Kraft Name[ga]=Kraft Name[gl]=Kraft Name[hu]=Kraft Name[mr]=क्राफ्ट Name[nl]=Kraft Name[pl]=Kraft Name[pt]=Kraft Name[pt_BR]=Kraft Name[sk]=Kraft Name[sv]=Kraft Name[tr]=Kraft Name[ug]=Kraft Name[uk]=Kraft Name[x-test]=xxKraftxx Categories=Office;Finance;Database; kraft-0.97/po/000077500000000000000000000000001410616450300131765ustar00rootroot00000000000000kraft-0.97/po/de/000077500000000000000000000000001410616450300135665ustar00rootroot00000000000000kraft-0.97/po/de/kraft.po000066400000000000000000002755401410616450300152520ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # # Translators: # Klaas Freitag , 2021 # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-06-14 21:37+0200\n" "PO-Revision-Date: 2018-10-31 21:56+0000\n" "Last-Translator: Klaas Freitag , 2021\n" "Language-Team: German (https://www.transifex.com/not-applicable-6/teams/93132/de/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: de\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: addeditchapterdialog.cpp:35 #, kde-format msgid "Add/Edit Catalog Chapter" msgstr "Katalog-Kategorien hinzufügen/bearbeiten" #: addeditchapterdialog.cpp:41 #, kde-format msgid "Create a new Catalog Chapter" msgstr "Neues Katalogkapitel erstellen" #: addeditchapterdialog.cpp:45 #, kde-format msgid "Chapter Name:" msgstr "Kapitelname:" #: addeditchapterdialog.cpp:49 #, kde-format msgid "Chapter Description:" msgstr "Kapitelbeschreibung:" #: addeditchapterdialog.cpp:76 #, kde-format msgid "Create new Catalog Chapter below chapter %1" msgstr "Neues Katalogkapitel unter Kapitel %1 erstellen" #: addeditchapterdialog.cpp:83 #, kde-format msgid "Edit name and description of chapter %1" msgstr "Name und Beschreibung von Kapitel %1 bearbeiten" #: alldocsview.cpp:64 #, kde-format msgid "All documents" msgstr "Alle Dokumente" #: alldocsview.cpp:65 #, kde-format msgid "Documents of last week" msgstr "Dokumente der letzten Woche" #: alldocsview.cpp:66 #, kde-format msgid "Documents of last month" msgstr "Dokumente im letzten Monat" #: alldocsview.cpp:72 #, kde-format msgid "&Show: " msgstr "&Anzeigen" #: alldocsview.cpp:79 #, kde-format msgid "&Search: " msgstr "&Suchen: " #: alldocsview.cpp:134 rc.cpp:634 rc.cpp:1328 portal.cpp:289 portal.cpp:290 #, kde-format msgid "Document Actions" msgstr "Dokument-Aktionen" #: archdoc.cpp:70 #, kde-format msgid "%1 for %2 (Id %3)" msgstr "%1 bis %2 (Nummer %3)" #: catalogselection.cpp:50 #, kde-format msgid "Selected &Catalog: " msgstr "&Katalogauswahl: " #: catalogselection.cpp:159 #, kde-format msgid "Append to Document" msgstr "An Dokument anfügen" #: catalogtemplate.cpp:52 #, kde-format msgid "Manual Price" msgstr "Manueller Preis" #: catalogtemplate.cpp:54 #, kde-format msgid "Calculated" msgstr "Kalkuliert" #: catalogtemplate.cpp:56 #, kde-format msgid "AutoCalc" msgstr "Automatische Kalkulation" #: catalogtemplate.cpp:57 #, kde-format msgid "Err: Unknown type %d" msgstr "Fehler: Unbekannter Typ %d" #: docassistant.cpp:52 #, kde-format msgid "Show &Templates" msgstr "Vorlagen anzeigen" #: docassistant.cpp:57 #, kde-format msgid "Show mask to create or select templates to be used in the document" msgstr "" "Den Dialog zum Erstellen oder Auswählen von Dokumentvorlagen anzeigen." #: docassistant.cpp:121 #, kde-format msgid "Add a template to the document" msgstr "Vorlage zum Dokument hinzufügen" #: docassistant.cpp:127 #, kde-format msgid "Create a new template" msgstr "Eine neue Vorlage erstellen" #: docassistant.cpp:134 #, kde-format msgid "Edit the current template" msgstr "Aktuelle Vorlage bearbeiten" #: docassistant.cpp:141 #, kde-format msgid "Delete the current template" msgstr "Aktuelle Vorlage löschen" #: docassistant.cpp:305 #, kde-format msgid "" "Do you really want to delete the template permanently?\n" "It can not be recovered." msgstr "" "Möchten Sie die ausgewählte Vorlage wirklich löschen? Dieser Vorgang kann " "nicht rückgängig gemacht werden." #: docdigestdetailview.cpp:224 rc.cpp:78 rc.cpp:120 rc.cpp:988 rc.cpp:1030 #, kde-format msgid "Amount" msgstr "Menge" #: docdigestdetailview.cpp:225 #, kde-format msgid "Type" msgstr "Art" #: docdigestdetailview.cpp:226 #, kde-format msgid "Sum" msgstr "Summe" #: docdigestdetailview.cpp:255 #, kde-format msgid "Results in %1 %2" msgstr "Resultate in %1 %2" #: docdigestdetailview.cpp:256 docdigestdetailview.cpp:289 #, kde-format msgid "Year" msgstr "Jahr" #: docdigestdetailview.cpp:258 #, kde-format msgid "Month" msgstr "Monat" #: docdigestdetailview.cpp:291 #, kde-format msgid "Results in Year %1" msgstr "Resultate im Jahr %1" #: docdigestdetailview.cpp:310 docdigestdetailview.cpp:319 #, kde-format msgid "Customer" msgstr "Kunde:" #: docdigestdetailview.cpp:314 #, kde-format msgid "not set" msgstr "nicht ausgewählt" #: docdigestdetailview.cpp:330 #, kde-format msgid "The address is not listed in an address book." msgstr "Die Adresse ist in keinem Adressbuch enthalten." #: docdigestdetailview.cpp:332 #, kde-format msgid "" "The client has the address book id %1 but can not found in our address " "books." msgstr "" "Der Kunde hat die Adressbuch-Kennung %1, wurde aber nicht in unseren " "Adressbüchern gefunden." #: docdigestdetailview.cpp:335 #, kde-format msgid "The client can be found in our address books." msgstr "Der Kunde wurde in unseren Adressbüchern gefunden." #: docdigestdetailview.cpp:346 prefsdialog.cpp:636 #, kde-format msgid "preferred address" msgstr "Bevorzugte Adresse" #: docdigestdetailview.cpp:350 prefsdialog.cpp:640 #, kde-format msgid "home address" msgstr "Privatanschrift" #: docdigestdetailview.cpp:354 prefsdialog.cpp:644 #, kde-format msgid "work address" msgstr "Arbeitsplatzadresse" #: docdigestdetailview.cpp:358 prefsdialog.cpp:648 #, kde-format msgid "postal address" msgstr "Postanschrift" #: docdigestdetailview.cpp:362 prefsdialog.cpp:652 #, kde-format msgid "international address" msgstr "Internationale Adresse" #: docdigestdetailview.cpp:366 prefsdialog.cpp:656 #, kde-format msgid "domestic address" msgstr "Privatanschrift" #: docdigestdetailview.cpp:370 prefsdialog.cpp:660 #, kde-format msgid "unknown" msgstr "Unbekannt" #: docdigestdetailview.cpp:407 models/docbasemodel.cpp:32 #, kde-format msgid "Date" msgstr "Datum" #: docdigestdetailview.cpp:412 models/docbasemodel.cpp:35 #, kde-format msgid "Whiteboard" msgstr "Notizen" #: docdigestdetailview.cpp:417 models/docbasemodel.cpp:39 #, kde-format msgid "Project" msgstr "Projekt" #: docdigestdetailview.cpp:427 #, kde-format msgid "This document was never printed." msgstr "Das Dokuments wurde nie gedruckt." #: docdigestdetailview.cpp:434 #, kde-format msgid "Last printed" msgstr "Zuletzt gedruckt" #: docdigestdetailview.cpp:435 #, kde-format msgid "Opens last created PDF document" msgstr "Öffnet das letzte erstellte PDF Dokument" #: docdigestdetailview.cpp:436 #, kde-format msgid "open" msgstr "offen" #: docdigestdetailview.cpp:440 #, kde-format msgid "One older print" msgstr "Ein älterer Ausdruck" #: docdigestdetailview.cpp:442 #, kde-format msgid "%1 older prints" msgstr "%1 alte Ausdrucke" #: docdigestdetailview.cpp:446 #, kde-format msgid "Archived documents can not be found. Check PDF Output dir." msgstr "" "Archivierte Dokumente können nicht gefunden werden. Überprüfen Sie das PDF " "Ausgabe Verzeichnis." #: docpostcard.cpp:34 #, kde-format msgid "Document Overview" msgstr "Dokumenten-Übersicht" #: docpostcard.cpp:122 #, kde-format msgid "Netto:" msgstr "Netto:" #: docpostcard.cpp:133 docpostcard.cpp:140 #, kde-format msgid "+ %1% Tax:" msgstr "+ %1 % Steuer:" #: docpostcard.cpp:146 #, kde-format msgid "Sum Tax:" msgstr "Steuersumme:" #: docpostcard.cpp:151 #, kde-format msgid "Total:" msgstr "Gesamt:" #: docpostcard.cpp:243 #, kde-format msgid "%1 Items" msgstr "%1 Posten" #: docpostcard.cpp:245 #, kde-format msgid "%1 Items, netto %2" msgstr "%1 Posten, Netto %2" #: doctext.cpp:61 #, kde-format msgid "Standard" msgstr "Standard" #: doctext.cpp:76 #, kde-format msgid "Header Text" msgstr "Kopftext" #: doctext.cpp:77 #, kde-format msgid "Footer Text" msgstr "Fußtext" #: doctext.cpp:78 #, kde-format msgid "Items" msgstr "Posten" #: doctext.cpp:80 defaultprovider.cpp:55 positionviewwidget.cpp:413 #, kde-format msgid "Unknown" msgstr "Unbekannt" #: doctypeedit.cpp:48 #, kde-format msgid "" msgstr "" #: doctypeedit.cpp:49 #, kde-format msgid "
" msgstr "" #: doctypeedit.cpp:132 doctypeedit.cpp:158 #, kde-format msgid "Add Document Type" msgstr "Dokumentenart hinzufügen" #: doctypeedit.cpp:133 #, kde-format msgid "Enter the name of a new document type" msgstr "Bitte geben Sie den Namen der neuen Dokumentenart ein." #: doctypeedit.cpp:159 #, kde-format msgid "Edit the name of a document type" msgstr "Den Namen einer Dokumentenart ändern." #: documentman.cpp:93 #, kde-format msgctxt "" "Text to be inserted into a doc, if the sum of another doc needs to be " "substracted ie. in a final invoice if there was a partial invoice before%1 " "is substited by the doc type, %2 by the id of the predecessor doc." msgid "Substract sum from %1 %2" msgstr "Subtrahiere Summe von %1 %2" #: documenttemplate.cpp:112 #, kde-format msgctxt "Sequence number printed on the document" msgid "No." msgstr "Nr." #: documenttemplate.cpp:113 #, kde-format msgctxt "Document item printed on the document" msgid "Item" msgstr "Posten" #: documenttemplate.cpp:114 #, kde-format msgctxt "Abbrev. of Quantity printed on the document" msgid "Qty." msgstr "Menge" #: documenttemplate.cpp:115 #, kde-format msgctxt "Unit printed on the document" msgid "Unit" msgstr "Einh." #: documenttemplate.cpp:116 #, kde-format msgctxt "Price of an item printed on the document" msgid "Price" msgstr "Preis" #: documenttemplate.cpp:117 #, kde-format msgctxt "Printed on the document" msgid "Sum" msgstr "Summe" #: documenttemplate.cpp:118 #, kde-format msgctxt "printed on the document" msgid "Net" msgstr "Netto" #: documenttemplate.cpp:119 #, kde-format msgctxt "Printed on the document" msgid "VAT" msgstr "MwSt." #: documenttemplate.cpp:121 #, kde-format msgctxt "Printed on the document" msgid "Phone" msgstr "Telefon" #: documenttemplate.cpp:122 #, kde-format msgctxt "Printed on the document" msgid "FAX" msgstr "Fax" #: documenttemplate.cpp:123 #, kde-format msgctxt "Printed on the document" msgid "Mobile" msgstr "Mobil" #: documenttemplate.cpp:124 #, kde-format msgctxt "Printed on the document" msgid "Email" msgstr "Email" #: documenttemplate.cpp:125 #, kde-format msgctxt "Printed on the document" msgid "Website" msgstr "Webseite" #: documenttemplate.cpp:127 #, kde-format msgctxt "Printed on the document" msgid "Page" msgstr "Seite" #: documenttemplate.cpp:128 #, kde-format msgctxt "the 'of' in page X of Y" msgid "of" msgstr "von" #: documenttemplate.cpp:129 #, kde-format msgctxt "Document number on document" msgid "Document No." msgstr "Dokument Nr." #: documenttemplate.cpp:130 #, kde-format msgctxt "Date on document" msgid "Date" msgstr "Datum" #: documenttemplate.cpp:131 #, kde-format msgctxt "Project label" msgid "Project" msgstr "Projekt" #: documenttemplate.cpp:132 #, kde-format msgctxt "Customer ID on document" msgid "Customer Id" msgstr "Kundennr." #: documenttemplate.cpp:267 #, kde-format msgid "" "Please note: This offer contains %1 alternative or demand positions, printed" " in italic font. These do not add to the overall sum." msgstr "" "Bitte beachten: Dieses Angebot enthält %1 Alternativ- oder " "Bedarfspositionen, die kursiv gedruckt sind. Diese addieren sich nicht zur " "Endsumme." #: documenttemplate.cpp:278 #, kde-format msgid "tax free items (%1 pcs.)" msgstr "Steuerfreie Posten (%1 Stck.)" #: documenttemplate.cpp:284 #, kde-format msgid "items with reduced tax of %1% (%2 pcs.)" msgstr "Posten mit reduzierter Steuer %1% (%2 Stck.)" #: documenttemplate.cpp:293 #, kde-format msgid "No label: items with full tax of %1% (%2 pcs.)" msgstr "Nicht gekennzeichnet: Posten mit vollem Steuersatz von %1% (%2 Stck.)" #: documenttemplate.cpp:323 kraftview_ro.cpp:240 #, kde-format msgid "reduced VAT" msgstr "Reduzierte Umsatzsteuer" #: documenttemplate.cpp:331 kraftview_ro.cpp:212 kraftview_ro.cpp:249 #: rc.cpp:21 rc.cpp:931 #, kde-format msgid "VAT" msgstr "Umsatzsteuer" #: documenttemplate.cpp:363 #, kde-format msgid "Template to convert is not existing!" msgstr "Vorlage existiert nicht!" #: documenttemplate.cpp:366 #, kde-format msgid "Can not read template file!" msgstr "Vorlangendatei kann nicht gelesen werden!" #: calcpart.cpp:98 #, kde-format msgid "Base" msgstr "Basis" #: filterheader.cpp:40 addressselectorwidget.cpp:294 #, kde-format msgid "&Search:" msgstr "&Suchen: " #: fixcalcdialog.cpp:36 #, kde-format msgid "Calculation Fix Item" msgstr "Zeitkostenanteil" #: flostempldialog.cpp:71 #, kde-format msgid "Create or Edit Template Items" msgstr "Posten der Vorlage erstellen oder bearbeiten" #: flostempldialog.cpp:213 flostempldialog.cpp:604 #, kde-format msgid "No" msgstr "Nein" #: flostempldialog.cpp:214 flostempldialog.cpp:604 #, kde-format msgid "Yes" msgstr "Ja" #: flostempldialog.cpp:251 #, kde-format msgid "Calculated Price: " msgstr "Kalkulierter Preis: " #: flostempldialog.cpp:255 #, kde-format msgid "Manual Price: " msgstr "Manueller Preis: " #: flostempldialog.cpp:260 #, kde-format msgid "(+%1%)" msgstr "(+ %1 %)" #: flostempldialog.cpp:264 #, kde-format msgid "%1%" msgstr "%1 %" #: flostempldialog.cpp:266 #, kde-format msgid ": " msgstr ": " #: flostempldialog.cpp:388 #, kde-format msgid "Template Error" msgstr "Vorlagenfehler" #: flostempldialog.cpp:388 #, kde-format msgid "Saving of this template failed, sorry" msgstr "Das Speichern der Vorlage ist fehlgeschlagen." #: flostempldialog.cpp:416 #, kde-format msgid "The template has been modified." msgstr "Vorlage geändert" #: flostempldialog.cpp:417 #, kde-format msgid "Do you want to discard your changes?" msgstr "" "Die Vorlage ist bearbeitet worden. Möchten Sie wirklich alle Änderungen " "verwerfen?" #: flostempldialog.cpp:446 #, kde-format msgid "" "The catalog chapter was changed for this template.\n" "Do you really want to move the template to the new chapter?" msgstr "" "Die Kategorie des Katalogs für diese Vorlage ist geändert worden.\n" "Möchten Sie diese Vorlage wirklich in die neue Kategorie verschieben?" #: flostempldialog.cpp:448 #, kde-format msgid "Chapter Change" msgstr "Kapitelveränderung" #: flostempldialog.h:104 #, kde-format msgid "Calculated material" msgstr "Kalkuliertes Material" #: katalog.cpp:137 #, kde-format msgid "not found" msgstr "nicht gefunden" #: main.cpp:53 #, kde-format msgid "Open document with arch doc number " msgstr "Dokument mit Archiv-Nummer %1 öffnen" #: main.cpp:54 #, kde-format msgid "Open Kraft in read only mode - document changes prohibited" msgstr "" "Kraft im Nur-Lesen Modus öffnen - Dokument Änderungen sind nicht möglich" #: importfilter.cpp:50 #, kde-format msgid "Unable to find filter called %1" msgstr "Der Filter mit dem Namen „%1“ kann nicht gefunden werden." #: importfilter.cpp:58 #, kde-format msgid "Could not open the definition file!" msgstr "Definitionsdatei kann nicht geöffnet werden!" #: importfilter.cpp:143 #, kde-format msgid "Unknown tags: " msgstr "Unbekannte Markierungen:" #: importfilter.cpp:184 #, kde-format msgid "Could not recode input file!" msgstr "Umcodieren der Eingabedatei fehlgeschlagen!" #: importfilter.cpp:192 #, kde-format msgid "Unable to open temp file " msgstr "Die temporäre Datei kann nicht geöffnet werden " #: importfilter.cpp:198 #, kde-format msgid "Could not open the import source file!" msgstr "Die zu importierende Datei kann nicht geöffnet werden." #: importitemdialog.cpp:47 #, kde-format msgid "Import Items From File" msgstr "Posten aus Datei importieren" #: importitemdialog.cpp:116 #, kde-format msgid "the Header of the Document" msgstr "Der Kopfbereich des Dokuments." #: importitemdialog.cpp:125 templtopositiondialogbase.cpp:58 #, kde-format msgid "..." msgstr "..." #: inserttempldialog.cpp:105 #, kde-format msgid "Create a new Item" msgstr "Neuen Posten anlegen" #: inserttempldialog.cpp:107 #, kde-format msgid "Create a new Item from Template" msgstr "Posten aus Vorlage erstellen" #: kataloglistview.cpp:350 #, kde-format msgid "A catalog chapter can not be deleted as long it has children." msgstr "" "Ein Kapitel im Katalog kann nicht gelöscht werden, wenn es noch Unterkapitel" " gibt." #: kataloglistview.cpp:351 #, kde-format msgid "Chapter can not be deleted" msgstr "Das Kapitel kann nicht gelöscht werden" #: katalogview.cpp:157 #, kde-format msgid "Edit Sub chapter" msgstr "Unterkapitel bearbeiten" #: katalogview.cpp:159 #, kde-format msgid "Edit a catalog sub chapter" msgstr "Katalog-Unterkapitel bearbeiten" #: katalogview.cpp:163 #, kde-format msgid "Add a sub chapter" msgstr "Unterkapitel hinzufügen" #: katalogview.cpp:165 #, kde-format msgid "Add a sub chapter below the selected one" msgstr "Ein Unterkapitel unter dem ausgewählten hinzufügen" #: katalogview.cpp:169 katalogview.cpp:171 #, kde-format msgid "Remove a sub chapter" msgstr "Unterkapitel entfernen" #: katalogview.cpp:175 rc.cpp:3 rc.cpp:913 #, kde-format msgid "Edit Template" msgstr "Vorlage bearbeiten" #: katalogview.cpp:177 #, kde-format msgid "Opens the editor window for templates to edit the selected one" msgstr "Öffnet das Fenster zum Ändern der ausgewählten Vorlage." #: katalogview.cpp:182 #, kde-format msgid "New template" msgstr "Neue Vorlage" #: katalogview.cpp:184 #, kde-format msgid "Opens the editor window for templates to enter a new template" msgstr "Öffnet das Fenster zum Anlegen einer neuen Vorlage." #: katalogview.cpp:189 #, kde-format msgid "Delete template" msgstr "Vorlage löschen" #: katalogview.cpp:191 #, kde-format msgid "Deletes the template" msgstr "Löscht die Vorlage" #: katalogview.cpp:196 #, kde-format msgid "Export catalog" msgstr "Katalog exportieren" #: katalogview.cpp:198 #, kde-format msgid "Export the whole catalog as XML encoded file" msgstr "Den gesamten Katalog als XML-Datei exportieren." #: katalogview.cpp:203 #, kde-format msgid "Import catalog" msgstr "Katalog importieren" #: katalogview.cpp:205 #, kde-format msgid "Import a catalog from a XML file" msgstr "Katalog aus einer XML Datei einlesen " #: katalogview.cpp:209 rc.cpp:487 rc.cpp:1358 #, kde-format msgid "&Catalog" msgstr "&Katalog" #: katalogview.cpp:227 #, kde-format msgid "Opening file..." msgstr "Datei wird geöffnet ..." #: katalogview.cpp:229 katalogview.cpp:295 katalogview.cpp:309 #: katalogview.cpp:318 katalogview.cpp:327 portal.cpp:659 portal.cpp:683 #: portal.cpp:1002 #, kde-format msgid "Ready." msgstr "Fertig." #: katalogview.cpp:234 portal.cpp:946 #, kde-format msgid "Exiting..." msgstr "Beenden ..." #: katalogview.cpp:291 #, kde-format msgid "Exporting file..." msgstr "Datei wird exportiert ..." #: katalogview.cpp:300 #, kde-format msgid "Importfile... (not yet implemented)" msgstr "Datei importieren (noch nicht implementiert)" #: katalogview.cpp:305 #, kde-format msgid "Creating a new sub chapter..." msgstr "Neues Unterkapitel wird erstellt ..." #: katalogview.cpp:314 #, kde-format msgid "Editing a sub chapter..." msgstr "Unterkapitel wird bearbeitet ..." #: katalogview.cpp:323 #, kde-format msgid "Removing a sub chapter..." msgstr "Unterkapitel wird entfernt ..." #: katalogview.cpp:355 #, kde-format msgid "Created at:%1" msgstr "Erstellt am:%1" #: katalogview.cpp:358 #, kde-format msgid "  Last used:%1" msgstr "  Zuletzt genutzt: %1" #: katalogview.cpp:365 #, kde-format msgid "Modified at:%1" msgstr "Geändert am:%1" #: katalogview.cpp:368 #, kde-format msgid "  Use Count:%1" msgstr "  %1 mal verwendet." #: kraftdoc.cpp:159 #, kde-format msgctxt "First argument is the doctype, like Invoice, followed by the ID" msgid "%1 (Id %2)" msgstr "%1 (Id %2)" #: kraftdoc.cpp:307 #, kde-format msgctxt "Document part header" msgid "Header" msgstr "Kopfbereich" #: kraftdoc.cpp:309 #, kde-format msgctxt "Document part footer" msgid "Footer" msgstr "Fußbereich" #: kraftdoc.cpp:311 #, kde-format msgctxt "Document part containing the items" msgid "Items" msgstr "Posten" #: kraftdoc.cpp:313 #, kde-format msgid "Unknown document part" msgstr "Unbekannter Dokumententeil" #: positionviewwidget.cpp:92 #, kde-format msgid "Item Actions" msgstr "Postenaktionen" #: positionviewwidget.cpp:95 #, kde-format msgid "Item Kind" msgstr "Postenart" #: positionviewwidget.cpp:96 positionviewwidget.cpp:671 #, kde-format msgid "Normal" msgstr "Normal" #: positionviewwidget.cpp:98 positionviewwidget.cpp:679 #, kde-format msgid "Alternative" msgstr "Alternative" #: positionviewwidget.cpp:100 #, kde-format msgid "On Demand" msgstr "Bei Bedarf" #: positionviewwidget.cpp:105 rc.cpp:265 rc.cpp:874 #, kde-format msgid "Tax" msgstr "Steuer" #: positionviewwidget.cpp:108 #, kde-format msgid "Taxfree Item" msgstr "Steuerfreier Posten" #: positionviewwidget.cpp:114 #, kde-format msgid "Reduced Tax" msgstr "Reduzierter Steuersatz" #: positionviewwidget.cpp:120 #, kde-format msgid "Full Tax" msgstr "Allgemeiner Steuersatz" #: positionviewwidget.cpp:129 #, kde-format msgid "Move Up" msgstr "Nach oben" #: positionviewwidget.cpp:131 #, kde-format msgid "Move Down" msgstr "Nach unten" #: positionviewwidget.cpp:133 #, kde-format msgid "Lock Item" msgstr "Posten sperren" #: positionviewwidget.cpp:135 #, kde-format msgid "Unlock Item" msgstr "Posten entsperren" #: positionviewwidget.cpp:137 #, kde-format msgid "Delete Item" msgstr "Posten löschen" #: positionviewwidget.cpp:221 #, kde-format msgid "All items" msgstr "Alle Posten" #: positionviewwidget.cpp:231 #, kde-format msgid "%1-tagged items" msgstr "Mit „%1“ markierte Posten" #: positionviewwidget.cpp:263 #, kde-format msgid "Tag: %1" msgstr "Markierung: %1" #: positionviewwidget.cpp:265 #, kde-format msgid "Tags:
    " msgstr "Markierungen:
      " #: positionviewwidget.cpp:276 #, kde-format msgid "No tags assigned yet." msgstr "Es sind noch keine Stichwörter zugewiesen." #: positionviewwidget.cpp:405 #, kde-format msgid "Active" msgstr "Aktiv" #: positionviewwidget.cpp:407 #, kde-format msgid "New" msgstr "Neu" #: positionviewwidget.cpp:409 #, kde-format msgid "Deleted" msgstr "Gelöscht" #: positionviewwidget.cpp:411 #, kde-format msgid "Locked" msgstr "Gesperrt" #: positionviewwidget.cpp:618 #, kde-format msgid "" "This is an alternative item.

      Use the position toolbox to change " "the item type." msgstr "" "Dies ist ein Alternativposten.

      Verwenden Sie die Posten-Werkzeuge, " "um die Art des Postens zu ändern." #: positionviewwidget.cpp:634 #, kde-format msgid "" "This item is either completely optional or its amount varies depending on " "the needs.

      Use the item toolbox to change the item type." msgstr "" "Dieser Posten ist entweder vollständig optional oder sein Betrag variiert " "abhängig von den Erfordernissen.

      Verwenden Sie die Posten-" "Werkzeuge, um die Art des Postens zu ändern." #: positionviewwidget.cpp:675 #, kde-format msgid "Demand" msgstr "Bedarf" #: kraftdocfooteredit.cpp:52 #, kde-format msgid "Document Footer" msgstr "Dokumentfuß" #: kraftdocheaderedit.cpp:56 #, kde-format msgid "Document Header" msgstr "Dokumentkopf" #: kraftdocheaderedit.cpp:64 #, kde-format msgid "Manually set in address field." msgstr "Manuell im Adressfeld gesetzt." #: kraftdocpositionsedit.cpp:94 #, kde-format msgid "Add Item..." msgstr "Posten hinzufügen ..." #: kraftdocpositionsedit.cpp:96 #, kde-format msgid "Add a normal item to the document manually." msgstr "Zum manuellen Hinzufügen eines Postens." #: kraftdocpositionsedit.cpp:100 #, kde-format msgid "Add Discount Item" msgstr "Rabattposten hinzufügen" #: kraftdocpositionsedit.cpp:103 #, kde-format msgid "" "Adds an item to the document that allows discounts on other items in the " "document" msgstr "" "Fügt einen Posten zum Dokument hinzu, mit dem Rabatte für andere Posten " "innerhalb des Dokuments gewährt werden können." #: kraftdocpositionsedit.cpp:106 #, kde-format msgid "Import Items..." msgstr "Posten importieren ..." #: kraftdocpositionsedit.cpp:109 #, kde-format msgid "Opens a dialog where multiple items can be imported from a text file." msgstr "" "Öffnet einen Dialog zum Importieren mehrerer Posten aus einer Textdatei." #: kraftdocpositionsedit.cpp:118 #, kde-format msgid "Document Items" msgstr "Dokumentposten" #: kraftview.cpp:87 kraftview_ro.cpp:75 #, kde-format msgid "Document" msgstr "Dokument" #: kraftview.cpp:261 #, kde-format msgid "Successor of %1" msgstr "Nachfolger von %1" #: kraftview.cpp:398 kraftview.cpp:843 #, kde-format msgid "" "The address label is not empty and different from the selected one.
      Do " "you really want to replace it with the text shown below?
      %1
      " msgstr "" "Die Adress-Beschriftung ist nicht leer und unterscheidet sich von der " "ausgewählten Adresse
      Möchten Sie sie wirklich mit diesem angezeigten " "Text ersetzen?
      %1
      " #: kraftview.cpp:445 #, kde-format msgid "" "

      The Document Items List is still empty, but Items can be added " "now.

      To add items to the document either
      • Press the 'Add item' " "button above.
      • Open the template catalog by clicking on the 'show " "Template' button on the right and pick one of the available " "templates.
      " msgstr "" "

      Die Postenliste ist noch leer, aber jetzt können Posten hinzugefügt werden.

      \n" "Um Posten hinzuzufügen\n" "
        \n" "
      • Drücken Sie den 'Posten hinzufügen' Knopf
      • Öffnen Sie den Vorlagenkatalog durch einen Klick auf den 'Vorlagen anzeigen' Knopf rechts und übertragen Sie eine der vorhandenen Vorlagen.
      • \n" "
      \n" " " #: kraftview.cpp:630 prefsdialog.cpp:319 #, kde-format msgid "Display no tax at all" msgstr "Keine Steuer auswerfen" #: kraftview.cpp:631 prefsdialog.cpp:320 #, kde-format msgid "Calculate reduced tax for all items" msgstr "Reduzierte Steuer für alle Posten" #: kraftview.cpp:632 prefsdialog.cpp:321 #, kde-format msgid "Calculate full tax for all items" msgstr "Allgemeine Steuer für alle Posten" #: kraftview.cpp:633 #, kde-format msgid "Calculate individual tax for each item" msgstr "Einzelne Steuern für jeden Posten berechnen" #: kraftview.cpp:690 #, kde-format msgid "Tax Settings Overwrite" msgstr "Steuer-Einstellungen überschreiben" #: kraftview.cpp:691 #, kde-format msgid "Really overwrite all individual tax settings of the items?" msgstr "" "Möchten Sie wirklich alle individuellen Steuer-Einstellungen für diesen " "Eintrag überschreiben?" #: kraftview.cpp:842 #, kde-format msgid "Address Overwrite" msgstr "Adresse überschreiben" #: kraftview.cpp:1099 #, kde-format msgid "Discount" msgstr "Rabatt" #: kraftview.cpp:1419 #, kde-format msgid "The document has been modified." msgstr "Das Dokuments wurde verändert." #: kraftview.cpp:1420 #, kde-format msgid "Do you want to save your changes?" msgstr "Sollen die Änderungen gesichert werden?" #: kraftview_ro.cpp:213 #, kde-format msgid "Reduced TAX" msgstr "Reduzierte Steuer" #: newdocassistant.cpp:52 newdocassistant.cpp:96 #, kde-format msgid "New Document Settings" msgstr "Einstellungen für neues Dokument" #: newdocassistant.cpp:55 #, kde-format msgid "" "Please select a customer as addressee for the document. If there is no entry" " for the customer in the addressbook yet, it can be opened by clicking on " "the button below." msgstr "" "Bitte wählen Sie einen Kunden als Adressaten für das Dokument aus. Wenn es " "noch keinen Eintrag für den Kunden im Adressbuch gibt, kann es hier geöffnet" " und ein neuer Kunde angelegt werden." #: newdocassistant.cpp:100 #, kde-format msgid "" "Select a document type and a date. A comment on the whiteboard helps to " "classify the document." msgstr "" "Wählen Sie einen Dokumenttyp und das Datum aus. Eine Notiz hilft, das " "Dokument besser zu klassifizieren." #: newdocassistant.cpp:107 #, kde-format msgid "Customer: Not yet selected!" msgstr "Kunde: Es ist noch kein Kunde ausgewählt." #: newdocassistant.cpp:116 #, kde-format msgid "Document &Type:" msgstr "&Dokumenttyp:" #: newdocassistant.cpp:120 #, kde-format msgid "Document Date: " msgstr "Dokumentdatum:" #: newdocassistant.cpp:123 #, kde-format msgid "Whiteboard Content:" msgstr "Notizen:" #: newdocassistant.cpp:127 #, kde-format msgid "Copy document items from predecessor document" msgstr "Kopiere Posten vom Vorgängerdokument" #: newdocassistant.cpp:175 #, kde-format msgid "Create a new Kraft Document" msgstr "Erzeuge ein neues Kraft-Dokument" #: newdocassistant.cpp:265 #, kde-format msgid "Followup Document for %1" msgstr "Folgedokument für %1" #: materialkataloglistview.cpp:39 rc.cpp:108 rc.cpp:643 rc.cpp:1018 #: rc.cpp:1675 #, kde-format msgid "Material" msgstr "Material" #: materialkataloglistview.cpp:40 #, kde-format msgid "Pack" msgstr "Gebinde" #: materialkataloglistview.cpp:41 rc.cpp:123 rc.cpp:1033 #, kde-format msgid "Unit" msgstr "Einheit" #: materialkataloglistview.cpp:42 #, kde-format msgid "Purchase" msgstr "Einkauf" #: materialkataloglistview.cpp:43 #, kde-format msgid "Sale" msgstr "Verkauf" #: materialkataloglistview.cpp:44 #, kde-format msgid "Last Modified" msgstr "Zuletzt geändert" #: materialkataloglistview.cpp:50 #, kde-format msgid "Material Catalog" msgstr "Material-Katalog" #: materialkatalogview.cpp:106 #, kde-format msgid "" msgstr "" #: materialkatalogview.cpp:136 templkatalogview.cpp:139 #, kde-format msgid "Do you really want to delete the template from the catalog?" msgstr "Möchten Sie die ausgewählte Vorlage wirklich aus dem Katalog löschen?" #: materialselectdialog.cpp:39 #, kde-format msgid "Add Material to Calculation" msgstr "Material zur Kalkulation hinzufügen" #: materialselectdialog.cpp:44 #, kde-format msgid "

      Add Material to Calculation

      " msgstr "

      Material zur Kalkulation hinzufügen

      " #: rc.cpp:6 rc.cpp:916 templkataloglistview.cpp:46 texteditdialog.cpp:76 #, kde-format msgid "Template" msgstr "Vorlage" #: rc.cpp:9 rc.cpp:919 #, kde-format msgid "Text:" msgstr "Text:" #: rc.cpp:12 rc.cpp:922 #, kde-format msgid "&Store in Chapter" msgstr "In Kategorie s&peichern" #: rc.cpp:15 rc.cpp:925 #, kde-format msgid "&Unit" msgstr "&Einheit" #: rc.cpp:18 rc.cpp:928 #, kde-format msgid "&Count Time for Overalltime" msgstr "Zeiten für &Gesamtzeit zusammenzählen" #: rc.cpp:24 rc.cpp:934 #, kde-format msgid "full" msgstr "Allgemein" #: rc.cpp:27 rc.cpp:937 #, kde-format msgid "half" msgstr "Reduziert" #: rc.cpp:30 rc.cpp:940 #, kde-format msgid "Time Calculation" msgstr "Arbeitszeit" #: rc.cpp:33 rc.cpp:72 rc.cpp:111 rc.cpp:943 rc.cpp:982 rc.cpp:1021 #, kde-format msgid "text" msgstr "text" #: rc.cpp:36 rc.cpp:946 #, kde-format msgid "Time measureable effort for this template:" msgstr "In Geldeinheiten umgerechneter Zeitaufwand für diese Vorlage:" #: rc.cpp:39 rc.cpp:81 rc.cpp:117 rc.cpp:949 rc.cpp:991 rc.cpp:1027 #, kde-format msgid "Label" msgstr "Bezeichnung" #: rc.cpp:42 rc.cpp:952 #, kde-format msgid "Duration" msgstr "Dauer" #: rc.cpp:45 rc.cpp:955 #, kde-format msgid "Hourly Rate" msgstr "Stundensatz" #: rc.cpp:48 rc.cpp:958 #, kde-format msgid "Glob. Rate" msgstr "Glob. Tarif" #: rc.cpp:51 rc.cpp:961 #, kde-format msgid "Adds a new time calculation part to the template" msgstr "Fügt eine neue Komponente zur Zeitberechnung zur Vorlage hinzu" #: rc.cpp:54 rc.cpp:93 rc.cpp:132 rc.cpp:964 rc.cpp:1003 rc.cpp:1042 #, kde-format msgid "new..." msgstr "Neu ..." #: rc.cpp:57 rc.cpp:967 #, kde-format msgid "Edits the current time calculation part" msgstr "Ändert die aktuelle Komponente zur Zeitberechnung" #: rc.cpp:60 rc.cpp:99 rc.cpp:138 rc.cpp:970 rc.cpp:1009 rc.cpp:1048 #, kde-format msgid "edit..." msgstr "Bearbeiten ..." #: rc.cpp:63 rc.cpp:973 #, kde-format msgid "Deletes the current time calculation part" msgstr "Löscht die aktuelle Komponente zur Zeitberechnung" #: rc.cpp:66 rc.cpp:105 rc.cpp:144 rc.cpp:976 rc.cpp:1015 rc.cpp:1054 #, kde-format msgid "delete" msgstr "Entfernen" #: rc.cpp:69 rc.cpp:979 #, kde-format msgid "Fix Costs" msgstr "Fixkosten" #: rc.cpp:75 rc.cpp:985 #, kde-format msgid "Fix costs for this template per one unit:" msgstr "Fixkosten für diese Vorlage für eine Einheit:" #: rc.cpp:84 rc.cpp:994 #, kde-format msgid "Single Price" msgstr "Einzelpreis" #: rc.cpp:87 rc.cpp:997 #, kde-format msgid "Overall Price" msgstr "Gesamtpreis" #: rc.cpp:90 rc.cpp:1000 #, kde-format msgid "adds a new fix calculation part" msgstr "Fügt eine neue Festpreis-Kalkulationskomponente hinzu" #: rc.cpp:96 rc.cpp:1006 #, kde-format msgid "edits the current fix calculation part" msgstr "Festpreis-Kalkulationskomponente bearbeiten" #: rc.cpp:102 rc.cpp:1012 #, kde-format msgid "deletes the current fix calculation part" msgstr "Festpreis-Kalkulationskomponente entfernen" #: rc.cpp:114 rc.cpp:1024 #, kde-format msgid "Needed materials for one unit of this template:" msgstr "Benötigte Materialien für eine Einheit dieser Vorlage:" #: rc.cpp:126 rc.cpp:1036 prefswages.cpp:53 templkataloglistview.cpp:47 #, kde-format msgid "Price" msgstr "Preis" #: rc.cpp:129 rc.cpp:1039 #, kde-format msgid "adds a new material calculation part" msgstr "Fügt eine neue Material-Kalkulationskomponente hinzu" #: rc.cpp:135 rc.cpp:1045 #, kde-format msgid "edits the current material part" msgstr "Material-Kalkulationskomponente bearbeiten" #: rc.cpp:141 rc.cpp:1051 #, kde-format msgid "deletes the current material calculation part" msgstr "Material-Kalkulationskomponente entfernen" #: rc.cpp:147 rc.cpp:1057 #, kde-format msgid "Overall Price per Unit" msgstr "Gesamtpreis pro Einheit" #: rc.cpp:150 rc.cpp:1060 #, kde-format msgid "&Manual Price" msgstr "&Manueller Preis" #: rc.cpp:153 rc.cpp:1063 #, kde-format msgid "Calculated Price" msgstr "Kalkulierter Preis" #: rc.cpp:156 rc.cpp:1066 #, kde-format msgid "Fix Costs Part:" msgstr "Fixkostenanteil:" #: rc.cpp:159 rc.cpp:1069 #, kde-format msgid "Material Part:" msgstr "Material-Anteil:" #: rc.cpp:162 rc.cpp:1072 #, kde-format msgid "Profit:" msgstr "Gewinn:" #: rc.cpp:166 rc.cpp:1076 #, no-c-format, kde-format msgid " %" msgstr " %" #: rc.cpp:169 rc.cpp:1079 #, kde-format msgid "Time Calculation Part:" msgstr "Arbeitszeit:" #: rc.cpp:172 rc.cpp:1082 #, kde-format msgid "Calculated Price:" msgstr "Kalkulierter Preis:" #: rc.cpp:175 rc.cpp:1085 #, kde-format msgid "88.888,88 €" msgstr "88.888,88 €" #: rc.cpp:178 rc.cpp:1088 #, kde-format msgid "Database creation and initial schema setup:" msgstr "Einrichtung der Datenbank und Setup des Anfangsschemas:" #: rc.cpp:181 rc.cpp:190 rc.cpp:844 rc.cpp:1091 rc.cpp:1100 rc.cpp:1706 #, kde-format msgid "0 / 129" msgstr "0 / 129" #: rc.cpp:184 rc.cpp:193 rc.cpp:847 rc.cpp:1094 rc.cpp:1103 rc.cpp:1709 #, kde-format msgid "X" msgstr "X" #: rc.cpp:187 rc.cpp:1097 #, kde-format msgid "Filling the database with initial values:" msgstr "Füllen der Datenbank mit Anfangswerten:" #: rc.cpp:196 rc.cpp:850 rc.cpp:1106 rc.cpp:1712 #, kde-format msgid "Status: " msgstr "Status: " #: rc.cpp:199 rc.cpp:1109 #, kde-format msgid "Database setup status..." msgstr "Status der Datenbank-Einrichtung ..." #: rc.cpp:202 rc.cpp:1112 #, kde-format msgid "Bruns Data File:" msgstr "Bruns-Datendatei:" #: rc.cpp:205 rc.cpp:1115 #, kde-format msgid "Bruns Key File:" msgstr "Bruns-Schlüsseldatei:" #: rc.cpp:208 rc.cpp:1118 #, kde-format msgid "Qt Database Driver:" msgstr "Qt-Datenbank-Treiber:" #: rc.cpp:211 rc.cpp:1121 #, kde-format msgid "Database Server:" msgstr "Datenbank-Server:" #: rc.cpp:214 rc.cpp:1124 #, kde-format msgid "Server Port:" msgstr "Server Port:" #: rc.cpp:217 rc.cpp:1127 #, kde-format msgid "Database Name" msgstr "Datenbankname" #: rc.cpp:220 rc.cpp:700 rc.cpp:1130 rc.cpp:1555 #, kde-format msgid "Database User:" msgstr "Datenbank-Benutzer:" #: rc.cpp:223 rc.cpp:1133 #, kde-format msgid "Database Password:" msgstr "Datenbank-Passwort:" #: rc.cpp:226 rc.cpp:1136 #, kde-format msgid "The default database name when creating new databases" msgstr "Standard-Datenbankname bei neu erstellter Datenbank" #: rc.cpp:229 rc.cpp:1139 #, kde-format msgid "File Name" msgstr "Dateiname" #: rc.cpp:232 rc.cpp:1142 #, kde-format msgid "The path where database file are stored. Leave empty!" msgstr "Pfad zu den Datenbankdateien. Leer lassen!" #: rc.cpp:235 rc.cpp:1145 #, kde-format msgid "Database Update:" msgstr "Datenbank-Aktualisierung:" #: rc.cpp:238 rc.cpp:1148 #, kde-format msgid "Overall Progress:" msgstr "Gesamtfortschritt:" #: rc.cpp:241 rc.cpp:1151 #, kde-format msgid "Detail Progress:" msgstr "Fortschrittsanzeige:" #: rc.cpp:244 rc.cpp:1154 #, kde-format msgid "Status:" msgstr "Status:" #: rc.cpp:247 rc.cpp:1289 #, kde-format msgid "" "

      Kraft uses a database backend to store values. By " "default it uses a file based database with easy setup targeted to single " "user mode.


      " msgstr "" "

      Kraft nutzt ein Datenbank-Backend, um Werte zu " "speichern. Standardmäßig wird eine dateibasierte Datenbank mit einfacher " "Einrichtung verwendet, die auf Einbenutzerbetrieb ausgelegt ist. " "


      Bitte wählen Sie das Datenbank-Backend, das " "Sie verwenden möchten:" #: rc.cpp:250 rc.cpp:1292 #, kde-format msgid "SQLite 3 - file based database (default)" msgstr "SQLite 3 – Datei-basierte Datenbank (Voreinstellung)" #: rc.cpp:253 rc.cpp:1295 #, kde-format msgid "MySQL Serverbased Database for advanced Setups" msgstr "MySQL – Server-basierte Datenbank für fortgeschrittene Einrichtungen" #: rc.cpp:256 rc.cpp:865 #, kde-format msgid "Footer Texts" msgstr "Fußzeilentexte" #: rc.cpp:259 rc.cpp:868 #, kde-format msgid "&Summary Text on Last Page:" msgstr "&Zusammenfassung auf letzter Seite:" #: rc.cpp:262 rc.cpp:871 #, kde-format msgid "&Greeting:" msgstr "&Anrede:" #: rc.cpp:268 rc.cpp:877 #, kde-format msgid "Document &Tax:" msgstr "&Steuersatz:" #: rc.cpp:271 rc.cpp:880 #, kde-format msgid "TextLabel" msgstr "Textlabel" #: rc.cpp:274 rc.cpp:883 #, kde-format msgid "&Project:" msgstr "&Projekt:" #: rc.cpp:277 rc.cpp:886 #, kde-format msgid "&Whiteboard:" msgstr "&Notizen:" #: rc.cpp:280 rc.cpp:889 #, kde-format msgid "" "Enter a label that describes the project. This may appear on the customer " "document." msgstr "" "Geben Sie eine Beschriftung an, die das Projekt beschreibt. Diese kann auf " "dem Kundendokument erscheinen." #: rc.cpp:283 rc.cpp:892 #, kde-format msgid "Postal &Address:" msgstr "&Anschrift:" #: rc.cpp:286 rc.cpp:895 #, kde-format msgid "not selected" msgstr "nicht ausgewählt" #: rc.cpp:289 rc.cpp:898 #, kde-format msgid "Select an addressee from the address books." msgstr "Wählt eine Adresse aus den Adressbüchern." #: rc.cpp:292 rc.cpp:901 #, kde-format msgid "select..." msgstr "Auswählen ..." #: rc.cpp:295 rc.cpp:904 #, kde-format msgid "&Salutatory Address:" msgstr "&Grußformel:" #: rc.cpp:298 rc.cpp:907 #, kde-format msgid "Customer:" msgstr "Kunde:" #: rc.cpp:301 rc.cpp:910 #, kde-format msgid "&Entry Text on First Page:" msgstr "&Einleitender Text auf der ersten Seite:" #: rc.cpp:304 rc.cpp:733 rc.cpp:1157 rc.cpp:1588 #, kde-format msgid "Click to add a new document type to the list." msgstr "Klicken Sie, um der Liste einen neuen Dokumenttyp hinzuzufügen." #: rc.cpp:307 rc.cpp:1160 prefsdialog.cpp:184 prefsunits.cpp:79 #: prefswages.cpp:90 #, kde-format msgid "Add" msgstr "Hinzufügen" #: rc.cpp:310 rc.cpp:1163 #, kde-format msgid "click to edit the selected document type name" msgstr "" "Klicken Sie hier, um den Namen des ausgewählten Dokumenttyps zu bearbeiten." #: rc.cpp:313 rc.cpp:1166 prefsunits.cpp:83 prefswages.cpp:94 #, kde-format msgid "Edit" msgstr "Bearbeiten" #: rc.cpp:316 rc.cpp:739 rc.cpp:1169 rc.cpp:1594 #, kde-format msgid "click to remove the current document type" msgstr "Klicken Sie hier, um den aktuellen Dokumenttyp zu entfernen." #: rc.cpp:319 rc.cpp:1172 portalview.cpp:220 prefsdialog.cpp:188 #: prefsunits.cpp:88 prefswages.cpp:99 #, kde-format msgid "Remove" msgstr "Entfernen" #: rc.cpp:322 rc.cpp:1175 #, kde-format msgid "Unique Document Number" msgstr "Eindeutige Dokumentennummer" #: rc.cpp:325 rc.cpp:1178 #, kde-format msgid "Number &Cycle:" msgstr "Nummern&bestimmung:" #: rc.cpp:328 rc.cpp:337 rc.cpp:340 rc.cpp:724 rc.cpp:1181 rc.cpp:1190 #: rc.cpp:1193 rc.cpp:1579 #, kde-format msgid "example" msgstr "Beispiel" #: rc.cpp:331 rc.cpp:1184 #, kde-format msgid "ident Template:" msgstr "Nummernvorlage:" #: rc.cpp:334 rc.cpp:718 rc.cpp:1187 rc.cpp:1573 #, kde-format msgid "Example Id:" msgstr "Beispielnummer:" #: rc.cpp:343 rc.cpp:1196 #, kde-format msgid "Counter:" msgstr "Anfangswert:" #: rc.cpp:346 rc.cpp:1199 #, kde-format msgid "&Edit Number Cycles..." msgstr "Nummernkreise &bearbeiten ..." #: rc.cpp:349 rc.cpp:1202 #, kde-format msgid "Template for PDF Creation" msgstr "Vorlage für PDF-Erstellung" #: rc.cpp:352 rc.cpp:1205 #, kde-format msgid "&Template File" msgstr "&Vorlagendatei" #: rc.cpp:355 rc.cpp:1208 #, kde-format msgid "W&atermark:" msgstr "W&asserzeichen:" #: rc.cpp:358 rc.cpp:1211 #, kde-format msgid "no watermark" msgstr "Kein Wasserzeichen" #: rc.cpp:361 rc.cpp:1214 #, kde-format msgid "on first page" msgstr "Auf erster Seite" #: rc.cpp:364 rc.cpp:1217 #, kde-format msgid "on all pages" msgstr "Auf allen Seiten" #: rc.cpp:367 rc.cpp:1220 #, kde-format msgid "&Watermark File" msgstr "&Wasserzeichendatei" #: rc.cpp:370 rc.cpp:1223 #, kde-format msgid "Calculation Parts Fix" msgstr "Fixkostenanteil der Kalkulation" #: rc.cpp:373 rc.cpp:1226 #, kde-format msgid "

      Fix Cost Parts

      " msgstr "

      Fixkostenanteil

      " #: rc.cpp:376 rc.cpp:1229 #, kde-format msgid "Add a fix cost for one unit of the template:" msgstr "Fixkosten für eine Einheit der Vorlage hinzufügen" #: rc.cpp:379 rc.cpp:808 rc.cpp:1232 rc.cpp:1529 #, kde-format msgid "&Label:" msgstr "&Bezeichnung" #: rc.cpp:382 rc.cpp:1235 #, kde-format msgid "describing text" msgstr "Beschreibungstext" #: rc.cpp:385 rc.cpp:1238 #, kde-format msgid "amortisation" msgstr "Amortisation" #: rc.cpp:388 rc.cpp:680 rc.cpp:1241 rc.cpp:1511 #, kde-format msgid "&Amount:" msgstr "&Menge:" #: rc.cpp:391 rc.cpp:1244 #, kde-format msgid "amount multiplier" msgstr "Betrags-Multiplikator" #: rc.cpp:394 rc.cpp:1247 #, kde-format msgid "at &Price:" msgstr "zum &Preis von:" #: rc.cpp:397 rc.cpp:683 rc.cpp:1250 rc.cpp:1514 #, kde-format msgid "Price for one piece" msgstr "Stückpreis" #: rc.cpp:400 rc.cpp:1253 #, kde-format msgid "€" msgstr "€" #: rc.cpp:403 rc.cpp:1256 #, kde-format msgid "Form" msgstr "Formular" #: rc.cpp:406 rc.cpp:1259 tagtemplatesdialog.cpp:58 #, kde-format msgid "Name:" msgstr "Name:" #: rc.cpp:409 rc.cpp:1262 #, kde-format msgid "Organization:" msgstr "Organisation:" #: rc.cpp:412 rc.cpp:1265 #, kde-format msgid "Street:" msgstr "Straße" #: rc.cpp:415 rc.cpp:1268 #, kde-format msgid "Post Code:" msgstr "Postleitzahl:" #: rc.cpp:418 rc.cpp:1271 #, kde-format msgid "City:" msgstr "Ort:" #: rc.cpp:421 rc.cpp:1274 #, kde-format msgid "Phone:" msgstr "Telefon (Arbeit)" #: rc.cpp:424 rc.cpp:1277 #, kde-format msgid "Fax:" msgstr "Fax:" #: rc.cpp:427 rc.cpp:1280 #, kde-format msgid "Mobile Phone:" msgstr "Mobil:" #: rc.cpp:430 rc.cpp:1283 #, kde-format msgid "EMail:" msgstr "EMail:" #: rc.cpp:433 rc.cpp:1286 #, kde-format msgid "Website:" msgstr "Webseite:" #: rc.cpp:436 rc.cpp:1298 #, kde-format msgid "Import Document Items" msgstr "Dokumentposten importieren" #: rc.cpp:439 rc.cpp:1301 #, kde-format msgid "Import information" msgstr "Informationen importieren" #: rc.cpp:442 rc.cpp:1304 #, kde-format msgid "Select a &File to import from:" msgstr "Bitte wählen Sie die zu importierende &Datei:" #: rc.cpp:445 rc.cpp:1307 #, kde-format msgid "FixMe!" msgstr "FixMe!" #: rc.cpp:448 rc.cpp:1310 #, kde-format msgid "Import &Schema:" msgstr "&Schema importieren:" #: rc.cpp:451 rc.cpp:1313 #, kde-format msgid "this is interesting information about the selected schema" msgstr "Dies sind nützliche Informationen über das ausgewählte Schema." #: rc.cpp:454 rc.cpp:1316 #, kde-format msgid "Insert all Items &after" msgstr "Alle Posten einfügen &nach" #: rc.cpp:457 rc.cpp:481 rc.cpp:1319 rc.cpp:1352 #, kde-format msgid "Tags" msgstr "Stichwörter" #: rc.cpp:460 rc.cpp:1331 templtopositiondialogbase.cpp:35 #, kde-format msgid "Create Item from Template" msgstr "Posten aus Vorlage erstellen" #: rc.cpp:463 rc.cpp:1334 #, kde-format msgid "New Item Text" msgstr "Neuer Posten" #: rc.cpp:466 rc.cpp:1337 #, kde-format msgid "&insert" msgstr "Einfügen" #: rc.cpp:469 rc.cpp:1340 #, kde-format msgid "à" msgstr "à" #: rc.cpp:472 rc.cpp:1343 #, kde-format msgid "&after item" msgstr "&nach" #: rc.cpp:475 rc.cpp:1346 #, kde-format msgid "Keep this item as template for future documents" msgstr "Diesen Posten als Vorlage für zukünftige Dokumente speichern" #: rc.cpp:478 rc.cpp:1349 #, kde-format msgid "save in &chapter" msgstr "in &Kapitel" #: rc.cpp:484 rc.cpp:1355 portal.cpp:215 #, kde-format msgid "&File" msgstr "&Datei" #: rc.cpp:490 rc.cpp:1361 #, kde-format msgid "Do Database Initialisation" msgstr "Datenbank initialisieren" #: rc.cpp:493 rc.cpp:1364 #, kde-format msgid "Do XML archiving of documents they're printed?" msgstr "XML-Dokumente archivieren, sobald sie gedruckt sind?" #: rc.cpp:496 rc.cpp:1367 #, kde-format msgid "" "Where Kraft stores the XML archive documents. If empty, KDEHOME/share/apps " "is used." msgstr "" "Speicherort für die XML-Archiv-Dokumente. Falls leer, wird " "$KDEHOME/share/apps verwendet." #: rc.cpp:499 rc.cpp:1370 #, kde-format msgid "The local xml document storage path" msgstr "Der lokale Speicherpfad für XML-Dokumente" #: rc.cpp:502 rc.cpp:1373 #, kde-format msgid "Default mail user agent. Set xdg for xdg-email" msgstr "Standard Email-Versende-Tool. Setze xdg für xdg-email" #: rc.cpp:505 rc.cpp:508 rc.cpp:1376 rc.cpp:1379 #, kde-format msgid "The default geometry of the document view dialog" msgstr "Standardgröße der Dokumentansicht" #: rc.cpp:511 rc.cpp:1382 #, kde-format msgid "The current state of the portal" msgstr "Der momentane Status des Portals" #: rc.cpp:514 rc.cpp:1385 #, kde-format msgid "The current geometry of the portal" msgstr "Die aktuelle Geometry des Portals" #: rc.cpp:517 rc.cpp:1388 #, kde-format msgid "The current geometry of the new doc assistant" msgstr "Aktuelle Geometry des Dokument-Neu Assistenten" #: rc.cpp:520 rc.cpp:1391 #, kde-format msgid "The splitter position of the document view dialog" msgstr "Teilungsstelle des Dokumentansicht-Dialogs" #: rc.cpp:523 rc.cpp:1394 #, kde-format msgid "The default size of the material catalog view" msgstr "Standardgröße der Material-Katalogs-Ansicht" #: rc.cpp:526 rc.cpp:1397 #, kde-format msgid "The splitter setting for the doc assistant" msgstr "Splitter-Einstellung für den Dokument-Assistenten" #: rc.cpp:529 rc.cpp:1400 #, kde-format msgid "The window size of the template to document dialog for plants" msgstr "Die Fenstergröße des Dialogs „Vorlage zu Dokument für Pflanzen“." #: rc.cpp:532 rc.cpp:1403 #, kde-format msgid "The window size of the template to document dialog" msgstr "Die Fenstergröße des Dialogs „Vorlage zu Dokument“" #: rc.cpp:535 rc.cpp:1406 #, kde-format msgid "The digest list column arrangement for the Latest-list" msgstr "" "Die Anordnung der Übersicht der Listenspalte für die Liste der letzten " "Einträge" #: rc.cpp:538 rc.cpp:1409 #, kde-format msgid "The digest list column arrangement for the all-list" msgstr "" "Die Anordnung der Übersicht der Listenspalte für die Liste aller Einträge" #: rc.cpp:541 rc.cpp:1412 #, kde-format msgid "The digest list column arrangement for timeline" msgstr "Die Anordnung der Übersicht der Listenspalte für die Zeitleiste" #: rc.cpp:544 rc.cpp:1415 #, kde-format msgid "The sizes of the slider in the address picker Widget" msgstr "Die Größe der Schieberegler im Adress-Auswahlfenster" #: rc.cpp:547 rc.cpp:1418 #, kde-format msgid "The state of the treeview inside the address selector widget" msgstr "Der Status der Baumansicht im Address-Auswahlwidget" #: rc.cpp:550 rc.cpp:1421 #, kde-format msgid "Size of the address select dialog" msgstr "Größe des Adressauswahldialoges" #: rc.cpp:553 rc.cpp:1424 #, kde-format msgid "State of the window of the material catalog" msgstr "Status des Materialkatalogfensters" #: rc.cpp:556 rc.cpp:1427 #, kde-format msgid "Geometry of the material catalog" msgstr "Geometrie des Materialkatalogfensters" #: rc.cpp:559 rc.cpp:1430 #, kde-format msgid "State of the header of the material catalog" msgstr "Standardgröße der Material-Katalogs-Ansicht" #: rc.cpp:562 rc.cpp:1433 #, kde-format msgid "State of the window of the template catalog" msgstr "Status des Katalog-Fensters" #: rc.cpp:565 rc.cpp:1436 #, kde-format msgid "Geometry the template catalog" msgstr "Geometrie des Katalog-Fensters" #: rc.cpp:568 rc.cpp:1439 #, kde-format msgid "State of the header of the template catalog" msgstr "Status der Kopfzeile des Vorlagenkatalogs" #: rc.cpp:571 rc.cpp:1442 #, kde-format msgid "" "Default percentage the sale price for a material should be higher than its " "purchase price" msgstr "" "Der prozentuale Aufschlag für den VK-Preis eines Materials sollte höher als " "sein EK-Preis sein." #: rc.cpp:574 rc.cpp:1445 #, kde-format msgid "The name of the last selected chapter in the catalog" msgstr "Der Name der zuletzt ausgewählten Kategorie im Katalog." #: rc.cpp:577 rc.cpp:1448 #, kde-format msgid "The complete filename of the trml2pdf binary" msgstr "Vollständiger Pfad zu trml2pdf" #: rc.cpp:580 rc.cpp:1451 #, kde-format msgid "The path to the output directory for document pdfs" msgstr "Pfad zum Ausgabe-Ordner für die Dokument-PDFs" #: rc.cpp:583 rc.cpp:1454 #, kde-format msgid "The last created doc type." msgstr "Typ des zuletzt erstellten Dokuments" #: rc.cpp:586 rc.cpp:1457 #, kde-format msgid "The date format for print." msgstr "Datumsformat für den Ausdruck." #: rc.cpp:589 rc.cpp:1460 #, kde-format msgid "The greeting below on the document footer." msgstr "Der Abschlussgruß im Dokumentfuß." #: rc.cpp:592 rc.cpp:1463 #, kde-format msgid "The salut message on the document header." msgstr "Die Anrede im Kopfbereich des Dokuments." #: rc.cpp:595 rc.cpp:1466 #, kde-format msgid "" "The name of the catalog chapter where to store new templates in by default" msgstr "" "Der Name des Katalogkapitels, in dem neue Vorlagen standardmäßig gespeichert" " werden" #: rc.cpp:598 rc.cpp:1469 #, kde-format msgid "The name of the last used import schema for items." msgstr "Der Name des letzten Eingabeschemas für Posten." #: rc.cpp:601 rc.cpp:1472 #, kde-format msgid "The name of the last used input file for items." msgstr "Der Name der letzten Eingabedatei für Posten." #: rc.cpp:604 rc.cpp:1475 #, kde-format msgid "" "User name as reference to the KAddressbook to identify 'my' address " "(DEPRECATED)." msgstr "" "Benutzername als Verknüpfung zu KAddressbook, um „Meine Adresse“ festzulegen" " (veraltet)." #: rc.cpp:607 rc.cpp:1478 #, kde-format msgid "" "UID of the user as reference to the KAddressbook to identify 'my' address." msgstr "" "Kennung des Benutzers als Verknüpfung zu KAddressbook, um „Meine Adresse“ " "festzulegen." #: rc.cpp:610 rc.cpp:1481 #, kde-format msgid "The doc id template" msgstr "Dokumentennummer-Vorlage" #: rc.cpp:613 rc.cpp:1484 #, kde-format msgid "Localization on document level" msgstr "Lokalisierung auf Dokumentebene" #: rc.cpp:616 rc.cpp:1487 #, kde-format msgid "The tax default for new documents." msgstr "Der standardmäßige Steuersatz für neue Dokumente." #: rc.cpp:619 rc.cpp:1490 #, kde-format msgid "The label for alternative positions" msgstr "Bezeichnung für Alternativposten" #: rc.cpp:622 rc.cpp:625 rc.cpp:1493 rc.cpp:1496 #, kde-format msgid "The label for demand positions" msgstr "Bezeichnung für Bedarfsposten" #: rc.cpp:628 rc.cpp:1322 portal.cpp:224 #, kde-format msgid "&Document" msgstr "&Dokument" #: rc.cpp:631 rc.cpp:1325 #, kde-format msgid "Settings" msgstr "Einstellungen" #: rc.cpp:637 rc.cpp:1669 #, kde-format msgid "Edit Material" msgstr "Material bearbeiten" #: rc.cpp:640 rc.cpp:1672 #, kde-format msgid "Store in C&hapter" msgstr "In &Kategorie speichern" #: rc.cpp:646 rc.cpp:1678 #, kde-format msgid "Pac&kaged:" msgstr "Ge&binde:" #: rc.cpp:649 rc.cpp:1681 #, kde-format msgid "per P&ackage" msgstr "pro Ge&binde" #: rc.cpp:652 rc.cpp:1684 #, kde-format msgid "Prices" msgstr "Preise" #: rc.cpp:655 rc.cpp:1687 #, kde-format msgid "= Price of &Sale:" msgstr "= &Verkaufspreis:" #: rc.cpp:658 rc.cpp:1690 #, kde-format msgid "pl&us" msgstr "&zzgl." #: rc.cpp:662 rc.cpp:755 rc.cpp:1610 rc.cpp:1694 #, no-c-format, kde-format msgid "%" msgstr " %" #: rc.cpp:665 rc.cpp:1697 #, kde-format msgid "&Purchase Price:" msgstr "&Einkaufspreis:" #: rc.cpp:668 rc.cpp:1499 #, kde-format msgid "Calculation Item Material" msgstr "Materialkostenanteil" #: rc.cpp:671 rc.cpp:1502 #, kde-format msgid "

      Calculation Part 'Material'

      " msgstr "

      Kalkulationsanteil „Material“

      " #: rc.cpp:674 rc.cpp:1505 #, kde-format msgid "Add Material to the template calculation." msgstr "Material zur Kalkulation der Vorlage hinzufügen" #: rc.cpp:677 rc.cpp:1508 #, kde-format msgid "material" msgstr "Material" #: rc.cpp:686 rc.cpp:1517 #, kde-format msgid "unit" msgstr "Einheit" #: rc.cpp:689 rc.cpp:1544 #, kde-format msgid "" "Please enter the MySQL Database server settings. \n" "\n" "For detailed setup instructions for the MySQL to use with Kraft please check the Kraft website." msgstr "" "Bitte bestätigen Sie die Einstellungen der MySQL-Datenbank. \n" "\n" "Für ausführliche Setup Anweisungen, um MySQL mit Kraft zu nutzen besuchen Sie bitte die Kraft Homepage. " #: rc.cpp:694 rc.cpp:1549 #, kde-format msgid "Database Host:" msgstr "Datenbank-Rechner:" #: rc.cpp:697 rc.cpp:1552 #, kde-format msgid "Database Name:" msgstr "Datenbankname:" #: rc.cpp:703 rc.cpp:1558 #, kde-format msgid "Password:" msgstr "Passwort:" #: rc.cpp:706 rc.cpp:1561 #, kde-format msgid "

      Edit Number Cycles

      " msgstr "

      Nummernbestimmung bearbeiten

      " #: rc.cpp:709 rc.cpp:1564 #, kde-format msgid "Number Cycle Details" msgstr "Nummernbestimmung" #: rc.cpp:712 rc.cpp:1567 #, kde-format msgid "&Number Cycle:" msgstr "&Nummernbestimmung" #: rc.cpp:715 rc.cpp:1570 #, kde-format msgid "&Counter:" msgstr "&Anfangswert:" #: rc.cpp:721 rc.cpp:1576 #, kde-format msgid "ident &Template:" msgstr "Identifizierungs&vorlage:" #: rc.cpp:727 rc.cpp:1582 #, kde-format msgid "&Select a number cycle and edit the details on the right:" msgstr "" "&Wählen Sie eine Nummernbestimmung aus der Liste und bearbeiten Sie diese " "auf der rechten Seite:" #: rc.cpp:730 rc.cpp:1585 #, kde-format msgid "New Item" msgstr "Neuer Posten" #: rc.cpp:736 rc.cpp:1591 #, kde-format msgid "add" msgstr "Hinzufügen" #: rc.cpp:742 rc.cpp:1597 #, kde-format msgid "remove" msgstr "Entfernen" #: rc.cpp:745 rc.cpp:1600 #, kde-format msgid "1." msgstr "1." #: rc.cpp:748 rc.cpp:751 rc.cpp:1603 rc.cpp:1606 #, kde-format msgid "D" msgstr "D" #: rc.cpp:758 rc.cpp:1613 #, kde-format msgid "of the sum of" msgstr "der Summe von" #: rc.cpp:761 rc.cpp:1616 #, kde-format msgid "" "Please enter the SQLite Database Settings.\n" "\n" "Pick a filename to name the SQLite database file or leave the default setting." msgstr "" "Bitte bestätigen Sie die SQLite-Datenbank-Einstellungen.\n" "\n" "Wählen Sie einen Dateinamen, um die SQLite-Datenbank-Datei zu benennen, oder übernehmen Sie die Voreinstellung. " #: rc.cpp:766 rc.cpp:1621 #, kde-format msgid "store the database file at default place." msgstr "Speichern der Datenbank-Datei am voreingestellten Ort." #: rc.cpp:769 rc.cpp:1624 #, kde-format msgid "select a file name:" msgstr "Dateinamen auswählen:" #: rc.cpp:772 rc.cpp:1627 #, kde-format msgid "

      Add a Tax Rate

      " msgstr "

      Steuersatz hinzufügen

      " #: rc.cpp:775 rc.cpp:1630 #, kde-format msgid "Start-Date:" msgstr "Anfangsdatum:" #: rc.cpp:778 rc.cpp:1633 #, kde-format msgid "&Reduced Tax Rate:" msgstr "&Reduzierter Steuersatz:" #: rc.cpp:781 rc.cpp:1636 #, kde-format msgid "&Full Tax Rate:" msgstr "&Allgemeiner Steuersatz:" #: rc.cpp:784 rc.cpp:1639 #, kde-format msgid "Edit Document Text Template" msgstr "Dokumenten-Textvorlage bearbeiten" #: rc.cpp:787 rc.cpp:1642 #, kde-format msgid "&Name:" msgstr "&Name:" #: rc.cpp:790 rc.cpp:1645 #, kde-format msgid "displayed as" msgstr "angezeigt als" #: rc.cpp:793 rc.cpp:1648 #, kde-format msgid "in doc type" msgstr "im Dokumenttyp" #: rc.cpp:796 rc.cpp:1651 #, kde-format msgid "&Text:" msgstr "&Text:" #: rc.cpp:799 rc.cpp:1520 #, kde-format msgid "Calculation Item Time" msgstr "Zeitkostenanteil" #: rc.cpp:802 rc.cpp:1523 #, kde-format msgid "

      Calculation Part 'Time'

      " msgstr "

      Arbeitszeitverrechnung

      " #: rc.cpp:805 rc.cpp:1526 #, kde-format msgid "" "Calculate time efforts here for one unit of the template.
      Note that the" " costs may depend on a global hourly rate." msgstr "" "Hier den zeitlichen Aufwand für eine Einheit der Vorlage berechnen. " "
      Beachten Sie dabei, dass die Kosten auf einem globalen Stundensatz " "beruhen können." #: rc.cpp:811 rc.cpp:1532 #, kde-format msgid "Work" msgstr "Arbeitsaufwand" #: rc.cpp:814 rc.cpp:1535 #, kde-format msgid "&Time Effort:" msgstr "&Zeitaufwand:" #: rc.cpp:817 rc.cpp:1538 #, kde-format msgid "&Hourly Rate:" msgstr "&Stundensatz:" #: rc.cpp:820 rc.cpp:1541 #, kde-format msgid "Apply the &global hourly rate" msgstr "Globalen Stundensatz anwenden" #: rc.cpp:823 rc.cpp:1654 #, kde-format msgid "

      Add a unit

      " msgstr "

      Einheit hinzufügen

      " #: rc.cpp:826 rc.cpp:1657 #, kde-format msgid "Unit short" msgstr "Einheit (kurz)" #: rc.cpp:829 rc.cpp:1660 #, kde-format msgid "Unit long" msgstr "Einheit (ausführlich)" #: rc.cpp:832 rc.cpp:1663 #, kde-format msgid "Unit plural short" msgstr "Einheiten-Mehrzahl (kurz)" #: rc.cpp:835 rc.cpp:1666 #, kde-format msgid "Unit plural long" msgstr "Einheiten-Mehrzahl (ausführlich)" #: rc.cpp:838 rc.cpp:1700 #, kde-format msgid "" "This step checks if the database schema version is sufficient for this version of Kraft. \n" "\n" "In case it is not, the schema is updated automatically.\n" msgstr "" "Dieser Schritt überprüft, ob die Datenbankschema-Version für diese Version von Kraft ausreicht.\n" "\n" "Wenn sie nicht ausreicht, wird das Schema automatisch aktualisiert.\n" #: rc.cpp:853 rc.cpp:1715 #, kde-format msgid "Upgrade not yet started" msgstr "Die Aktualisierung wurde noch nicht eingeleitet." #: rc.cpp:856 rc.cpp:1718 #, kde-format msgid "

      Add a Wage group

      " msgstr "

      Lohngruppe hinzufügen

      " #: rc.cpp:859 rc.cpp:1721 #, kde-format msgid "Group name" msgstr "Gruppenname" #: rc.cpp:862 rc.cpp:1724 #, kde-format msgid "Wage" msgstr "Arbeitslohn" #: models/docbasemodel.cpp:33 #, kde-format msgid "Doc. Number" msgstr "Dokumentennummer" #: models/docbasemodel.cpp:34 #, kde-format msgid "Doc. Type" msgstr "Dokumenttyp" #: models/docbasemodel.cpp:36 #, kde-format msgid "Client Id" msgstr "Kundenkennung" #: models/docbasemodel.cpp:37 #, kde-format msgid "Last modified" msgstr "Zuletzt geändert" #: models/docbasemodel.cpp:38 #, kde-format msgid "Creation date" msgstr "Erstellungsdatum" #: models/docbasemodel.cpp:40 #, kde-format msgid "Client Address" msgstr "Kundenadresse" #: models/docbasemodel.cpp:41 #, kde-format msgid "Client" msgstr "Kunde" #: models/docbasemodel.cpp:109 #, kde-format msgid "Looking up address" msgstr "Adresse wird gesucht" #: models/docbasemodel.cpp:111 #, kde-format msgid "Lookup started" msgstr "Suche gestartet" #: itemtagdialog.cpp:82 #, kde-format msgid "Edit Item Tags" msgstr "Posten-Markierungen bearbeiten" #: itemtagdialog.cpp:89 #, kde-format msgid "Item Tags" msgstr "Posten-Markierungen" #: itemtagdialog.cpp:90 #, kde-format msgid "Select all tags for the item should be tagged with." msgstr "Wählen Sie alle Markierungen für den Posten aus." #: itemtagdialog.cpp:105 tagtemplatesdialog.cpp:150 #, kde-format msgid "Tag" msgstr "Markierung" #: itemtagdialog.cpp:106 tagtemplatesdialog.cpp:151 #, kde-format msgid "Color" msgstr "Farbe" #: itemtagdialog.cpp:107 tagtemplatesdialog.cpp:152 #, kde-format msgid "Description" msgstr "Beschreibung" #: numbercycledialog.cpp:52 #, kde-format msgid "Edit Number Cycles" msgstr "Nummernbestimmung ändern" #: numbercycledialog.cpp:67 #, kde-format msgid "" "The template may contain the following tags:
      • %y or %yyyy - the year " "of the documents date.
      • %yy - the year of the document (two " "digits).
      • %w - the week number of the documents date.
      • %ww - " "the week number of the documents date with leading zero.
      • %d - the " "day number of the documents date.
      • %dd - the day number of the " "documents date with leading zero.
      • %m or %M - the month number of the" " documents date.
      • %MM - the month number with leading " "zero.
      • %c - the customer id from kaddressbook
      • %i - the unique" " counter
      • %type - the localised doc type (offer, invoice " "etc.)
      • %uid - the contact id of the client.
      %i needs to be " "part of the template." msgstr "" "Die Vorlage kann folgende Platzhalter enthalten (bezogen auf das Datum der " "Dokumentenerstellung):
      • %y oder %yyyy – Jahr
      • %yy – Jahr " "(zweistellig).
      • %w – Woche
      • %ww – Woche (immer " "zweistellig)
      • %d – Tag innerhalb des Monats
      • %dd – Tag " "innerhalb des Monats (immer zweistellig)
      • %m oder %M – " "Monat
      • %MM – Monat (immer zweistellig)
      • %i – Fortlaufende " "Zahl
      • %type – Lokalisierte Dokumentenart (Angebot, Rechnung " "usw.)
      • %uid - Die Kontakt-Kennung des Kunden.
      %1 muss in der" " Vorlage enthalten sein." #: numbercycledialog.cpp:138 #, kde-format msgid "Doc-Type" msgstr "Dokumenttyp" #: numbercycledialog.cpp:215 #, kde-format msgid "Add Number Cycle" msgstr "Nummernbestimmung hinzufügen" #: numbercycledialog.cpp:216 #, kde-format msgid "Enter the name of a new number cycle." msgstr "Geben Sie den Namen für die neue Nummernbestimmung an." #: numbercycledialog.cpp:274 #, kde-format msgid "The numbercycle %1 is still assigned to a document type." msgstr "Der Nummernkreis %1 ist noch einem Dokumenttyp zugeordnet." #: numbercycledialog.cpp:275 #, kde-format msgid "" "The number cycle can not be deleted as long as it is assigned to a document " "type." msgstr "" "Der Nummernkreis ist noch einer oder mehreren Dokumentenarten zugeordnet und" " kann erst nach Aufhebung aller Zuordnungen gelöscht werden." #: numbercycledialog.cpp:334 #, kde-format msgid "Dangerous Counter Change" msgstr "Gefährlicher neuer Anfangswert" #: numbercycledialog.cpp:335 #, kde-format msgid "The new counter is lower than the old one. " msgstr "Der neue Zähler ist kleiner als der alte." #: numbercycledialog.cpp:336 #, kde-format msgid "" "That has potential to create duplicate document numbers. Do you really want " "to decrease it?" msgstr "" "Der neue Anfangswert ist niedriger als der vorherige. Dadurch besteht die " "Möglichkeit, dass Dokumentennummern doppelt vergeben werden. Möchten Sie den" " neuen Anfangswert wirklich übernehmen?" #: portalview.cpp:65 #, kde-format msgid "About Kraft" msgstr "Über Kraft" #: portalview.cpp:83 #, kde-format msgid "Documents" msgstr "Dokumente" #: portalview.cpp:89 #, kde-format msgid "Timeline" msgstr "Zeitverlauf" #: portalview.cpp:96 #, kde-format msgid "Catalogs" msgstr "Kataloge" #: portalview.cpp:134 #, kde-format msgid "Kraft Document Overview" msgstr "Kraft-Dokument-Übersicht" #: portalview.cpp:141 portalview.cpp:160 #, kde-format msgid "Available Catalogs" msgstr "Verfügbare Kataloge" #: portalview.cpp:143 #, kde-format msgid "No catalogs available." msgstr "Keine Kataloge installiert" #: portalview.cpp:192 #, kde-format msgid "Open" msgstr "Öffnen" #: portalview.cpp:203 #, kde-format msgid "No templates yet." msgstr "Noch keine Vorlage angelegt." #: portalview.cpp:208 #, kde-format msgid "%1 templates in %2 chapters
      last modified at %3" msgstr "%1 Vorlagen in %2 Kapiteln
      Zuletzt angepasst am %3" #: portalview.cpp:215 #, kde-format msgid "XML Export" msgstr "XML-Export" #: portalview.cpp:270 #, kde-format msgid "Kraft Website" msgstr "Kraft Webseite" #: portalview.cpp:273 #, kde-format msgctxt "The string is followed by a link to the GPL2 text" msgid "Kraft is free software licensed under the" msgstr "Kraft ist freie Software lizensiert unter" #: portalview.cpp:274 #, kde-format msgctxt "The string is followed by the link to github" msgid "Kraft is maintained on " msgstr "Kraft wird entwickelt auf " #: portalview.cpp:275 #, kde-format msgid "Authors" msgstr "Autoren" #: portalview.cpp:276 #, kde-format msgid "Developer and Maintainer" msgstr "Entwickler und Maintainer" #: portalview.cpp:277 #, kde-format msgid "Developer" msgstr "Entwickler" #: portalview.cpp:278 #, kde-format msgctxt "The person who provided the logo graphics" msgid "Logo design" msgstr "Logo Entwurf" #: portalview.cpp:279 #, kde-format msgctxt "The person who provided the user manual" msgid "User Manual" msgstr "Bentzerhandbuch" #: portalview.cpp:281 #, kde-format msgid "" "Kraft helps you to handle documents like quotes and invoices in your small " "business." msgstr "" "Kraft ist freie Software zur schnellen, flexiblen und professionellen " "Angebots- und Rechnungsbearbeitung." #: portalview.cpp:282 #, kde-format msgid "Welcome to Kraft" msgstr "Willkommen zu Kraft" #: portalview.cpp:283 #, kde-format msgid "Kraft Version" msgstr "Kraft-Version" #: portalview.cpp:285 #, kde-format msgid "Codename" msgstr "Codename" #: portalview.cpp:288 #, kde-format msgid "Country Setting" msgstr "Ländereinstellung" #: portalview.cpp:291 #, kde-format msgid "Language Setting" msgstr "Spracheinstellung" #: portalview.cpp:295 #, kde-format msgid "Kraft Initialisation Problem" msgstr "Kraft Initialisierungsproblem" #: portalview.cpp:296 #, kde-format msgid "" "There is a initialisation error on your system. Kraft will not work that " "way." msgstr "" "Bei der Initialisierung ist ein Fehler aufgetreten. Kraft wird so nicht " "funktionieren." #: portalview.cpp:303 #, kde-format msgid "Database Information" msgstr "Datenbank-Informationen" #: portalview.cpp:304 #, kde-format msgid "Kraft database name" msgstr "Kraft-Datenbankname" #: portalview.cpp:309 #, kde-format msgid "Required Version" msgstr "Nötige Version" #: portalview.cpp:312 #, kde-format msgid "Database schema version" msgstr "Datenbankschema-Version" #: portalview.cpp:314 #, kde-format msgid "Qt database driver" msgstr "Qt-Datenbank-Treiber" #: portalview.cpp:318 #, kde-format msgid "established" msgstr "Verbunden" #: portalview.cpp:318 #, kde-format msgid "NOT AVAILABLE!" msgstr "NICHT VERFÜGBAR" #: portalview.cpp:319 #, kde-format msgid "Database connection" msgstr "Datenbankverbindung" #: portalview.cpp:328 #, kde-format msgid "Database Version" msgstr "Datenbankversion" #: portalview.cpp:336 #, kde-format msgid "Addressbook Backend" msgstr "Adressbuch-Backend" #: portalview.cpp:337 #, kde-format msgid "Backend type" msgstr "Backendtyp" #: portalview.cpp:339 #, kde-format msgid "running" msgstr "läuft" #: portalview.cpp:339 #, kde-format msgid "not running" msgstr "nicht funktionsfähig" #: portalview.cpp:343 #, kde-format msgid "External Tools" msgstr "Externe Werkzeuge" #: portalview.cpp:344 #, kde-format msgid "RML to PDF conversion tool" msgstr "Umwandlungsprogramm (RML zu PDF):" #: portalview.cpp:347 #, kde-format msgid "not found!" msgstr "nicht gefunden." #: portalview.cpp:350 #, kde-format msgid "iconv tool for text import" msgstr "„iconv“-Werkzeug zum Textimport" #: portalview.cpp:353 #, kde-format msgid "weasyprint for PDF generation" msgstr "weasyprint zur PDF Generierung" #: portalview.cpp:357 #, kde-format msgid "not available" msgstr "nicht gefunden" #: portalview.cpp:362 #, kde-format msgid "Some Icons are made by" msgstr "Einige Icons sind erstellt von" #: portalview.cpp:363 #, kde-format msgid "Acknowledgements" msgstr "Danksagung" #: prefsdialog.cpp:66 #, kde-format msgid "Configure Kraft" msgstr "Kraft einrichten" #: prefsdialog.cpp:100 #, kde-format msgid "Document Defaults" msgstr "Dokumentenstandards" #: prefsdialog.cpp:101 #, kde-format msgid "Taxes" msgstr "Steuersätze" #: prefsdialog.cpp:102 #, kde-format msgid "Document Types" msgstr "Dokumentenart" #: prefsdialog.cpp:104 #, kde-format msgid "Wages" msgstr "Löhne" #: prefsdialog.cpp:106 #, kde-format msgid "Units" msgstr "Maßeinheiten" #: prefsdialog.cpp:107 #, kde-format msgid "Own Identity" msgstr "Eigene Identität" #: prefsdialog.cpp:154 #, kde-format msgid "Tax rates beginning at date:" msgstr "Steuersätze nach Datum:" #: prefsdialog.cpp:162 prefsunits.cpp:53 prefswages.cpp:51 #, kde-format msgid "ID" msgstr "ID" #: prefsdialog.cpp:163 #, kde-format msgid "Full Tax [%]" msgstr "Allgemeiner Steuersatz" #: prefsdialog.cpp:164 #, kde-format msgid "Reduced Tax [%]" msgstr "Reduzierter Steuersatz" #: prefsdialog.cpp:165 #, kde-format msgid "Start Date" msgstr "Anfangsdatum:" #: prefsdialog.cpp:205 #, kde-format msgid "" "Select the identity of the sending entity of documents. That's your " "companies address." msgstr "" "Wählen Sie die Identität für den Versand von Dokumenten. Das ist Ihre " "Firmenadresse." #: prefsdialog.cpp:221 #, kde-format msgid "Select Identity..." msgstr "Identität auswählen ..." #: prefsdialog.cpp:227 #, kde-format msgid "From AddressBook" msgstr "aus dem Adressbuch" #: prefsdialog.cpp:232 setupassistant.cpp:340 #, kde-format msgid "Manual Entry" msgstr "Manueller Eintrag" #: prefsdialog.cpp:244 #, kde-format msgid "Manual Address" msgstr "Manuelle Adresse" #: prefsdialog.cpp:299 #, kde-format msgid "&Default document type on creation:" msgstr "&Standard-Dokumenttyp bei Erstellung:" #: prefsdialog.cpp:304 #, kde-format msgid "New documents default to the selected type." msgstr "Neue Dokumente sind standardmäßig von diesem Typ." #: prefsdialog.cpp:313 #, kde-format msgid "Default &Tax for Documents:" msgstr "Standardmäßiger &Steuersatz:" #: prefsdialog.cpp:318 #, kde-format msgid "The default tax setting for all documents." msgstr "" "Die standardmäßigen Einstellungen des Steuersatzes für alle Dokumente." #: prefsdialog.cpp:330 #, kde-format msgid "Document Date Format:" msgstr "Datumsformat im Dokument:" #: prefsdialog.cpp:336 #, kde-format msgid "The default date format for documents." msgstr "Das voreingestellte Datumsformat für Dokumente." #: prefsdialog.cpp:338 #, kde-format msgid "ISO-Format: %1" msgstr "ISO-Format: %1" #: prefsdialog.cpp:340 #, kde-format msgid "Short-Date: %1" msgstr "Datum in Kurzform: %1" #: prefsdialog.cpp:342 #, kde-format msgid "Long-Date: %1" msgstr "Datum in Langform: %1" #: prefsdialog.cpp:344 #, kde-format msgid "RFC 2822-Format: %1" msgstr "RFC 2822-Format: %1" #: prefsdialog.cpp:346 #, kde-format msgid "\"German Format\": %1" msgstr "\"Deutsches Format\": %1" #: prefsdialog.cpp:347 #, kde-format msgid "Custom Setting in Settingsfile" msgstr "Benutzerdefiniert aus dem Settingsfile" #: prefsdialog.cpp:382 #, kde-format msgid "" "The old default doc type for new documents was just deleted.Please check the" " setting in the Document Defaults in the Kraft preferences Dialog." msgstr "" "Der alte Standard-Dokumenttyp für neue Dokumente wurde gelöscht. Bitte " "überprüfen Sie die Einstellung der Dokumentenstandards im Dialog " "Einstellungen von Kraft." #: prefsdialog.cpp:385 #, kde-format msgid "Document Default Change" msgstr "Änderung des Standard-Dokumenttyps" #: prefsdialog.cpp:593 #, kde-format msgid "The identity can not be found." msgstr "Die Identität ist in keinem Adressbuch enthalten." #: prefsdialog.cpp:595 #, kde-format msgid "" "

      Kraft Addressbook Integration down.

      The address book backend" " is not up and running.

      Please check your addressbook integration " "setup.

      " msgstr "" "

      Die Kraft Adressbuch-Integration funktioniert nicht

      Das " "Adressbuch-Backend läuft nicht.

      Bitte Adressbuch-Setup überprüfen

      " #: prefsdialog.cpp:601 #, kde-format msgid "The identity is not listed in an address book." msgstr "Die Identität ist in keinem Adressbuch enthalten." #: prefsdialog.cpp:603 #, kde-format msgid "" "

      Kraft does not know your identity.

      Please pick one from the " "address books by clicking on the Button below.

      Not having an identity " "selected can make your documents look incomplete.

      " msgstr "" "

      Ihre Identität ist nicht eingestellt.

      Bitte wählen Sie eine " "Identität aus dem Adressbuch, indem Sie auf den Knopf unten " "klicken..

      Haben Sie keine Identität ausgewählt, dann werden Ihre " "Dokumente unvollständig aussehen.

      " #: prefsdialog.cpp:620 #, kde-format msgid "Your identity can be found in the address books." msgstr "Ihre Identität wurde in den Adressbüchern gefunden." #: prefsdialog.cpp:631 #, kde-format msgid "Work Phone" msgstr "Telefon (Arbeit)" #: prefsdialog.cpp:632 #, kde-format msgid "Fax" msgstr "Fax" #: prefsdialog.cpp:633 #, kde-format msgid "Cell Phone" msgstr "Mobiltelefon" #: prefsunits.cpp:54 #, kde-format msgid "Short" msgstr "Kurz" #: prefsunits.cpp:55 #, kde-format msgid "Long" msgstr "Lang" #: prefsunits.cpp:56 #, kde-format msgid "Short plural" msgstr "Mehrzahl kurz" #: prefsunits.cpp:57 #, kde-format msgid "Long plural" msgstr "Mehrzahl lang" #: prefsunits.cpp:152 #, kde-format msgid "Edit a unit" msgstr "Eine Maßeinheit bearbeiten" #: prefsunits.cpp:195 #, kde-format msgid "

      Edit unit

      " msgstr "

      Maßeinheit bearbeiten

      " #: prefswages.cpp:52 #, kde-format msgid "Code" msgstr "Code" #: prefswages.cpp:54 #, kde-format msgid "Sortkey" msgstr "Sortierschlüssel" #: prefswages.cpp:80 #, kde-format msgid "Up" msgstr "Nach oben" #: prefswages.cpp:85 #, kde-format msgid "Down" msgstr "Nach unten" #: prefswages.cpp:201 #, kde-format msgid "Edit a wage group" msgstr "Lohngruppe bearbeiten" #: prefswages.cpp:241 #, kde-format msgid "

      Edit wage group

      " msgstr "

      Lohngruppe bearbeiten

      " #: reportgenerator.cpp:113 #, kde-format msgid "Document generation process is still running." msgstr "Die Dokument-Generierung läuft noch." #: reportgenerator.cpp:167 #, kde-format msgid "Template file is not accessible." msgstr "Vorlage ist nicht lesbar." #: reportgenerator.cpp:192 #, kde-format msgid "The template conversion failed." msgstr "Vorlagenkonvertierung ist fehlgeschlagen." #: reportgenerator.cpp:200 #, kde-format msgid "Saving to temporar file failed." msgstr "Speichern in eine temporäre Datei ist fehlgeschlagen." #: reportgenerator.cpp:292 #, kde-format msgid "No converter error." msgstr "Kein Konverter gefunden." #: reportgenerator.cpp:295 #, kde-format msgid "The ReportLab based converter script can not be executed." msgstr "Das Reportlab-Script kann nicht ausgeführt werden." #: reportgenerator.cpp:298 #, kde-format msgid "An unknown error happened." msgstr "Ein unerwarteter Fehler ist aufgetreten." #: reportgenerator.cpp:301 #, kde-format msgid "The ReportLab python module is not installed." msgstr "Das Reportlab Python-Modul ist nicht installiert." #: reportgenerator.cpp:304 #, kde-format msgid "The source file can not be read." msgstr "Die Quelldatei kann nicht gelesen werden." #: reportgenerator.cpp:307 #, kde-format msgid "The target can not be opened to write." msgstr "Das Ziel kann nicht zum Schreiben geöffnet werden." #: reportgenerator.cpp:310 #, kde-format msgid "The target file does not exist." msgstr "Die Zieldatei existiert nicht." #: reportgenerator.cpp:313 #, kde-format msgid "The WeasyPrint tool is not installed." msgstr "Das Tool Weasyprint ist nicht installiert." #: reportgenerator.cpp:316 #, kde-format msgid "The PDF merger utility failed." msgstr "Das Programm zum Verbinden von PDFs ist fehlgeschlagen." #: reportgenerator.cpp:335 #, kde-format msgid "There is not template defined for %1." msgstr "Für %1 ist keine Vorlage angegeben." #: reportgenerator.cpp:340 #, kde-format msgid "The template file %1 for document type %2 does not exist." msgstr "Die Vorlage %1 für Dokument Typ %2 existiert nicht." #: reportgenerator.cpp:344 #, kde-format msgid "The template file %1 for document type %2 can not be read." msgstr "Die Vorlage %1 für Dokument Typ %2 kann nicht gelesen werden." #: setupassistant.cpp:36 #, kde-format msgid "Welcome to the Kraft Setup Assistant" msgstr "Willkommen beim Einrichtungsassistenten von Kraft" #: setupassistant.cpp:55 #, kde-format msgid "Select the Database Backend" msgstr "Bitte wählen Sie das Datenbank-Modul" #: setupassistant.cpp:92 #, kde-format msgid "Sqlite File Name" msgstr "Dateiname der Sqlite-Datenbank" #: setupassistant.cpp:146 #, kde-format msgid "MySql Detail Information" msgstr "MySQL Datenbank-Informationen" #: setupassistant.cpp:188 #, kde-format msgid "Create Database" msgstr "Datenbank neu erzeugen" #: setupassistant.cpp:210 setupassistant.cpp:223 #, kde-format msgid "0/%1" msgstr "0 / %1" #: setupassistant.cpp:242 setupassistant.cpp:252 setupassistant.cpp:290 #, kde-format msgid "%1/%2" msgstr "%1 / %2" #: setupassistant.cpp:265 #, kde-format msgid "Upgrade the Database" msgstr "Datenbankschema aktualisieren" #: setupassistant.cpp:313 #, kde-format msgid "Your Company Address" msgstr "Adresse der Firma" #: setupassistant.cpp:318 #, kde-format msgid "" "Select your companies address either from the address book or enter it " "manually. It is set as a consigner on the documents." msgstr "" "Wählen Sie die Adresse Ihrer Firma aus dem Adressbuch oder tragen Sie sie " "manuell ein. Sie wird als Absender der Dokumente verwendet." #: setupassistant.cpp:325 #, kde-format msgid "Select from Addressbook" msgstr "Aus dem Adressbuch" #: setupassistant.cpp:416 #, kde-format msgid "Final Status" msgstr "Endergebnis" #: setupassistant.cpp:510 #, kde-format msgid "" "

      Can't connect to your database. Are you sure your credentials are correct" " and the database exists?

      " msgstr "" "

      Die Verbindung zur Datenbank kann nicht hergestellt werden. Bitte stellen" " Sie sicher, dass die Einstellungen richtig sind und die Datenbank vorhanden" " ist.

      " #: setupassistant.cpp:521 #, kde-format msgid "

      Can't open your database file, check the permissions and such." msgstr "" "

      Die Datenbank-Datei kann nicht geöffnet werden. Bitte überprüfen Sie die " "Berechtigungen.

      " #: setupassistant.cpp:530 #, kde-format msgid "" "

      The database is already existing, no action needs to be taken " "here.

      Please hit next to proceed.

      " msgstr "" "

      Die Datenbank ist bereits angelegt, sodass hier keine weiteren Aktionen " "durchzuführen sind.

      Bitte drücken Sie auf Weiter, um " "fortzufahren.

      " #: setupassistant.cpp:579 #, kde-format msgid "

      The database setup was successfully completed.

      " msgstr "

      Die Datenbankeinrichtung ist erfolgreich abgeschlossen.

      " #: setupassistant.cpp:580 #, kde-format msgid "

      You can start to work with Kraft now. Please do not forget to

      " msgstr "" "

      Sie können nun Ihre Arbeit mit Kraft beginnen. Bitte vergessen Sie " "nicht:

      " #: setupassistant.cpp:582 #, kde-format msgid "
    • adjust various settings in the Kraft Preferences dialog.
    • " msgstr "" "
    • Verschiedene Einstellungen im Einstellungen-Fenster von Kraft " "anzupassen.
    • " #: setupassistant.cpp:583 #, kde-format msgid "
    • Check the Catalog chapter list.
    • " msgstr "
    • Überprüfen Sie die Kapitelliste des Katalogs.
    • " #: setupassistant.cpp:584 #, kde-format msgid "
    • Make your business and have fun.
    • " msgstr "
    • Führen Sie Ihre Geschäfte und haben Sie Spaß.
    • " #: setupassistant.cpp:586 #, kde-format msgid "" "

      If you press Finish now, the new database configuration is stored " "in Krafts configuration.

      " msgstr "" "

      Falls Sie nun auf Fertigstellen klicken, wird die neue Datenbank-" "Einrichtung in der Kraft-Konfiguration gespeichert.

      " #: setupassistant.cpp:602 #, kde-format msgid "" "The Database can not be connected. Please check the database credentials." msgstr "" "Die Verbindung zur Datenbank kann nicht hergestellt werden. Bitte stellen " "Sie sicher, dass die Einstellungen richtig sind." #: setupassistant.cpp:608 #, kde-format msgid "The database core tables do not exist. Please check initial setup." msgstr "" "Die grundlegenden Tabellen der Datenbank sind nicht vorhanden. Bitte " "überprüfen Sie die einleitende Einrichtung." #: setupassistant.cpp:615 #, kde-format msgid "Database is up to date. No upgrade is required." msgstr "" "Die Datenbank ist auf dem aktuellen Stand, sodass keine Aktualisierung " "notwendig ist." #: setupassistant.cpp:620 #, kde-format msgid "Parse Update Commands..." msgstr "Update-Anweisungen analysieren ..." #: setupassistant.cpp:663 #, kde-format msgid "The Upgrade failed!" msgstr "Die Aktualisierung ist fehlgeschlagen." #: setupassistant.cpp:665 #, kde-format msgid "The Upgrade succeeded, the current schema version is %1!" msgstr "" "Die Aktualisierung war erfolgreich. Die aktuelle Version des Schemas ist %1." #: setupassistant.cpp:678 #, kde-format msgid "" "The Database can not be connected. Please check the database credentials!" msgstr "" "Die Verbindung zur Datenbank kann nicht hergestellt werden. Bitte stellen " "Sie sicher, dass die Einstellungen richtig sind." #: setupassistant.cpp:684 #, kde-format msgid "Parse Create Commands..." msgstr "Create-Anweisungen analysieren ..." #: setupassistant.cpp:692 #, kde-format msgid "Parse database fillup commands..." msgstr "Datenbank-Füllbefehle analysieren ..." #: setupassistant.cpp:705 #, kde-format msgid "Processing database creation commands..." msgstr "Datenbank-Erzeugungsbefehle bearbeiten ..." #: setupassistant.cpp:722 #, kde-format msgid "Process database fillup commands..." msgstr "Datenbank-Füllbefehle bearbeiten ..." #: setupassistant.cpp:732 #, kde-format msgid "Successfully finished commands." msgstr "Die Befehle wurden erfolgreich ausgeführt." #: setupassistant.cpp:734 #, kde-format msgid "Failed to perform all commands." msgstr "Es kann kein Befehl erfolgreich ausgeführt werden." #: setupassistant.cpp:778 #, kde-format msgid "" "This assistant guides you through the basic settings of your Kraft " "installation." msgstr "" "Dieser Assistent führt Sie durch die Grundeinstellungen Ihrer Kraft-" "Installation" #: setupassistant.cpp:788 #, kde-format msgid "There was no database configuration found." msgstr "Es kann keine Datenbank-Einrichtung gefunden werden." #: setupassistant.cpp:790 #, kde-format msgid "A valid current database configuration file was found." msgstr "Eine gültige aktuelle Datenbank-Einrichtung ist gefunden worden." #: setupassistant.cpp:812 #, kde-format msgid "The database schema version is too low. It will be updated." msgstr "" "Die Version des Datenbank-Schemas ist zu niedrig. Es wird aktualisiert." #: setupassistant.cpp:815 #, kde-format msgid "The current database schema version is too high. Leaving untouched! " msgstr "" "Die Version des Datenbankschema ist zu hoch. Es wird nichts verändert." #: setupassistant.cpp:822 #, kde-format msgid "" "

      The database can be opened, but does not contain valid content.

      A " "new database can be created automatically from scratch.

      " msgstr "" "

      Die Datenbank kann geöffnet werden, enthält aber keine gültigen " "Inhalte.

      Eine neue Datenbank kann automatisch von Grund auf erzeugt " "werden.

      " #: setupassistant.cpp:829 #, kde-format msgid "

      Kraft failed to connect to the configured database.

      " msgstr "

      Kraft konnte nicht mit der konfigurierten Datenbank verbinden

      " #: setupassistant.cpp:831 #, kde-format msgid "" "

      Please check the database server setup and restart Kraft to connect." msgstr "" "

      Bitte überprüfen Sie die Einstellungen des Datenbank-Servers und starten " "Sie Kraft neu, um eine Verbindung herzustellen." #: setupassistant.cpp:833 #, kde-format msgid "

      Please check the database file." msgstr "

      Bitte überprüfen Sie die Datenbank-Datei." #: setupassistant.cpp:835 #, kde-format msgid "or create a new database by hitting next.

      " msgstr "" "Oder erstellen Sie eine neue Datenbank, indem Sie Weiter drücken.

      " #: setupassistant.cpp:843 #, kde-format msgid "

      Please hit next and follow the instructions.

      " msgstr "

      Drücken Sie Weiter und folgen Sie den Angaben.

      " #: tagtemplatesdialog.cpp:47 #, kde-format msgid "Edit Tag Template" msgstr "Stichwort-Vorlage bearbeiten" #: tagtemplatesdialog.cpp:54 #, kde-format msgid "Edit a Tag Template" msgstr "Marker-Vorlage bearbeiten" #: tagtemplatesdialog.cpp:55 #, kde-format msgid "Adjust settings for name, color and description." msgstr "Legen Sie einen Namen, eine Farbe und eine Beschreibung fest." #: tagtemplatesdialog.cpp:63 #, kde-format msgid "Description:" msgstr "Beschreibung:" #: tagtemplatesdialog.cpp:69 #, kde-format msgid "Associated Color:" msgstr "Zugehörige Farbe:" #: tagtemplatesdialog.cpp:135 tagtemplatesdialog.cpp:140 portal.cpp:165 #, kde-format msgid "Edit Tag Templates" msgstr "Stichwort-Vorlagen bearbeiten" #: tagtemplatesdialog.cpp:141 #, kde-format msgid "Add, edit and remove tag templates for use in the documents." msgstr "" "Stichwort-Vorlagen hinzufügen, ändern oder entfernen, die in diesen " "Dokumenten verwendet werden." #: tagtemplatesdialog.cpp:165 #, kde-format msgid "Add..." msgstr "Hinzufügen ..." #: tagtemplatesdialog.cpp:167 #, kde-format msgid "Edit..." msgstr "Bearbeiten ..." #: tagtemplatesdialog.cpp:170 #, kde-format msgid "Delete..." msgstr "Löschen ..." #: tagtemplatesdialog.cpp:223 #, kde-format msgid "Do you really want to delete the template?" msgstr "Möchten Sie die ausgewählte Vorlage wirklich löschen?" #: taxeditdialog.cpp:36 #, kde-format msgid "Edit Tax Rates" msgstr "Steuersätze bearbeiten" #: templkataloglistview.cpp:48 #, kde-format msgid "Calc. Type" msgstr "Kalkulationsart" #: templkataloglistview.cpp:55 #, kde-format msgid "Template Catalog" msgstr "Vorlagenkatalog" #: templkatalogview.cpp:101 #, kde-format msgid "" msgstr "" #: templtopositiondialogbase.cpp:51 #, kde-format msgid "the Header of the Document as first item" msgstr "als ersten Posten" #: texteditdialog.cpp:42 #, kde-format msgid "Edit Text Templates" msgstr "Textvorlagen bearbeiten" #: texteditdialog.cpp:62 #, kde-format msgid "Edit %1 Template" msgstr "Vorlage „%1“ bearbeiten" #: textselection.cpp:42 #, kde-format msgid "Template Collection" msgstr "Vorlagensammlung" #: textselection.cpp:100 #, kde-format msgid "Template Actions" msgstr "Vorlagenaktionen" #: textselection.cpp:129 #, kde-format msgid "This is the standard text used in new documents." msgstr "Dies ist der Standardtext, der in neuen Dokumenten verwendet wird." #: textselection.cpp:138 #, kde-format msgid "%1 Templates for %2" msgstr "%1 Vorlagen für %2" #: textselection.cpp:146 #, kde-format msgid "" "There is no %1 template text available for document type %2.
      Click the " "add-button below to create one." msgstr "" "Es ist kein Vorlagentext %1 für den Dokumenttyp %2 vorhanden.
      Klicken " "Sie auf den Knopf „Hinzufügen“ um einen Vorlagentext zu erstellen." #: textselection.cpp:201 #, kde-format msgid "&Use in Document" msgstr "Im Dokument &verwenden" #: texttemplate.cpp:103 #, kde-format msgid "Failed to open template source" msgstr "Die Vorlagendatei kann nicht geöffnet werden." #: portal.cpp:102 #, kde-format msgid "&Quit" msgstr "&Beenden" #: portal.cpp:107 #, kde-format msgid "&Cut" msgstr "&Ausschneiden" #: portal.cpp:112 #, kde-format msgid "C&opy" msgstr "K&opieren" #: portal.cpp:117 #, kde-format msgid "&Paste" msgstr "&Einfügen" #: portal.cpp:122 #, kde-format msgid "&Settings" msgstr "&Einstellungen" #: portal.cpp:127 #, kde-format msgid "&Create Document" msgstr "Dokument &anlegen" #: portal.cpp:131 #, kde-format msgid "&Copy Document" msgstr "Dokument &kopieren" #: portal.cpp:135 #, kde-format msgid "Create &Followup Document" msgstr "&Nachfolgedokument erzeugen" #: portal.cpp:140 #, kde-format msgid "Print Document" msgstr "Dokument drucken" #: portal.cpp:145 #, kde-format msgid "Show Document" msgstr "Dokument anzeigen" #: portal.cpp:150 #, kde-format msgid "Edit Document" msgstr "Dokument bearbeiten" #: portal.cpp:155 #, kde-format msgid "Open Archived Document" msgstr "Archiviertes Dokument öffnen" #: portal.cpp:160 #, kde-format msgid "Mail Document" msgstr "Dokument mailen" #: portal.cpp:170 #, kde-format msgid "Redo Initial Setup..." msgstr "Wiederhole initiales Setup..." #: portal.cpp:175 #, kde-format msgid "Kraft Handbook..." msgstr "Kraft Handbuch..." #: portal.cpp:179 #, kde-format msgid "About Qt..." msgstr "Über Qt..." #: portal.cpp:183 #, kde-format msgid "About Kraft..." msgstr "Über Kraft...." #: portal.cpp:187 #, kde-format msgid "Quits the application" msgstr "Anwendung beenden" #: portal.cpp:189 #, kde-format msgid "Cuts the selected section and puts it to the clipboard" msgstr "" "Schneidet den ausgewählten Abschnitt aus und kopiert ihn in die " "Zwischenablage." #: portal.cpp:190 #, kde-format msgid "Copies the selected section to the clipboard" msgstr "Kopiert den ausgewählten Abschnitt in die Zwischenablage." #: portal.cpp:191 #, kde-format msgid "Pastes the clipboard contents to current position" msgstr "Fügt den Inhalt der Zwischenablage an der aktuellen Position ein." #: portal.cpp:193 #, kde-format msgid "Creates a new Document" msgstr "Erstellt ein neues Dokument." #: portal.cpp:194 #, kde-format msgid "Print and archive this Document" msgstr "Das Dokument drucken und archivieren" #: portal.cpp:195 #, kde-format msgid "Creates a new document which is a copy of the selected document" msgstr "Erstellt ein neues Dokument, das eine Kopie des ausgewählten ist." #: portal.cpp:196 #, kde-format msgid "Create a followup document for the current document" msgstr "Ein Folgedokument für das aktuell ausgewählte erstellen" #: portal.cpp:197 #, kde-format msgid "Opens the document for editing" msgstr "Öffnet das Dokument zum Bearbeiten." #: portal.cpp:198 #, kde-format msgid "Opens a read only view on the document." msgstr "Öffnet das Dokument schreibgeschützt." #: portal.cpp:199 #, kde-format msgid "Send document per mail" msgstr "Das Dokument per E-Mail versenden" #: portal.cpp:200 #, kde-format msgid "" "Edit the available tag templates which can be assigned to document items." msgstr "" "Markierungs-Vorlagen hinzufügen, ändern oder entfernen, die den Posten der " "Dokumente zugeordnet werden können." #: portal.cpp:201 #, kde-format msgid "Configure the Database Kraft is working on." msgstr "Die Datenbank einrichten, mit der Kraft arbeitet." #: portal.cpp:202 #, kde-format msgid "Open a viewer on an archived document" msgstr "Das archivierte Dokument zur Ansicht öffnen" #: portal.cpp:219 #, kde-format msgid "&Edit" msgstr "&Bearbeiten" #: portal.cpp:238 #, kde-format msgid "Kraft" msgstr "Kraft" #: portal.cpp:241 #, kde-format msgid "&Preferences" msgstr "&Einstellungen" #: portal.cpp:245 #, kde-format msgid "Toolbars" msgstr "Werkzeugleisten" #: portal.cpp:251 #, kde-format msgid "&Help" msgstr "&Hilfe" #: portal.cpp:336 #, kde-format msgid "Database not running" msgstr "Datenbank nicht erreichbar" #: portal.cpp:337 #, kde-format msgid "" "Kraft was started in readonly mode, but the configured database can not be connected.\n" "\n" "Kraft will abort." msgstr "" "Kraft wurde im Nur-Lesen Modus gestartet, aber die konfigurierte Datenbank kann nicht erreicht werden.\n" "\n" "Kraft wird beendet." #: portal.cpp:353 #, kde-format msgid "" "Kraft can not connect to the specified MySQL server. Please check the Kraft " "database settings, check if the server is running and verify if a database " "with the name %1 exits!" msgstr "" "Kraft kann keine Verbindung zum angegebenen MySQL-Server herstellen. Bitte " "überprüfen Sie die Datenbank-Einstellungen von Kraft, ob der Datenbank-" "Server läuft und ob eine Datenbank namens %1 vorhanden ist." #: portal.cpp:357 #, kde-format msgid "" "The database with the name %1 does not exist on the database server. Please " "make sure the database exists and is accessible by the user running Kraft." msgstr "" "Die Datenbank namens %1 ist auf dem Datenbank-Server nicht verfügbar. Bitte " "stellen Sie sicher, dass die Datenbank vorhanden ist und vom Benutzer, unter" " welchem Kraft läuft, darauf zugegriffen werden kann." #: portal.cpp:361 #, kde-format msgid "" "The Qt database driver could not be loaded. That probably means, that they " "are not installed. Please make sure the Qt database packages are installed " "and try again." msgstr "" "Der Qt-Datenbank-Treiber lässt sich nicht laden. Das bedeutet eventuell, " "dass er nicht installiert ist. Bitte stellen Sie sicher, dass die Qt-" "Datenbank-Pakete installiert sind und versuchen es dann erneut." #: portal.cpp:365 #, kde-format msgid "There is a database problem: %1" msgstr "Es ist ein Problem mit der Datenbank aufgetreten: %1" #: portal.cpp:382 #, kde-format msgid "Database Problem." msgstr "Datenbank-Problem" #: portal.cpp:389 #, kde-format msgid "Check commandline actions" msgstr "Befehlszeilenaktionen prüfen" #: portal.cpp:481 #, kde-format msgid "Welcome to Kraft, %1" msgstr "Willkommen zu Kraft, %1" #: portal.cpp:510 #, kde-format msgid "Creating new document..." msgstr "Neues Dokument erstellen ..." #: portal.cpp:536 #, kde-format msgctxt "" "Dialog title of the followup doc dialog, followed by the id of the source " "doc" msgid "Create follow up document for %1" msgstr "Folgedokument für %1 anlegen" #: portal.cpp:581 #, kde-format msgctxt "Title of the new doc dialog, %1 is the source doc id" msgid "Create new Document as Copy of %1" msgstr "Neues Dokument als Kopie von %1 erzeugen" #: portal.cpp:616 #, kde-format msgid "Opening document to view..." msgstr "Dokument zur Ansicht öffnen ..." #: portal.cpp:646 #, kde-format msgid "Generating PDF..." msgstr "PDF wird erstellt ..." #: portal.cpp:668 #, kde-format msgid "Generating PDF for EMail" msgstr "Generiere PDF für EMail" #: portal.cpp:688 #, kde-format msgid "Doc Generation Error" msgstr "Dokument Generierungs-Fehler" #: portal.cpp:745 #, kde-format msgid "Printing archived document..." msgstr "Archiviertes Dokument drucken ..." #: portal.cpp:797 #, kde-format msgid "Opening document %1" msgstr "Dokument %1 öffnen" #: portal.cpp:973 #, kde-format msgid "Cutting selection..." msgstr "Auswahl ausschneiden ..." #: portal.cpp:980 #, kde-format msgid "Copying selection to clipboard..." msgstr "Auswahl in die Zwischenablage kopieren ..." #: portal.cpp:987 #, kde-format msgid "Inserting clipboard contents..." msgstr "Inhalt der Zwischenablage einfügen ..." #: portal.cpp:1000 #, kde-format msgid "" "Ready. Kraft is running in read only mode. Document editing is prohibited." msgstr "" "Bereit. Kraft läuft im Nur-Lesen Modus. Dokumente können nicht editiert " "werden." #: addressselectorwidget.cpp:229 #, kde-format msgid "Name" msgstr "Name" #: addressselectorwidget.cpp:231 #, kde-format msgid "Address" msgstr "Kundenadresse" #: addressselectorwidget.cpp:331 #, kde-format msgid "Edit Contact..." msgstr "Kontakt bearbeiten ..." #: addressselectorwidget.cpp:332 #, kde-format msgid "Edit the currently selected contact" msgstr "Bearbeitet den aktuell ausgewählten Kontakt" #: addressselectorwidget.cpp:335 #, kde-format msgid "New Contact..." msgstr "Neuer Kontakt ..." #: addressselectorwidget.cpp:336 #, kde-format msgid "Create a new Contact" msgstr "Neuen Kontakt erstellen" #: timecalcpart.cpp:82 #, kde-format msgid "Minutes" msgstr "Minuten" #: timecalcpart.cpp:84 #, kde-format msgid "Hours" msgstr "Stunden" #: timecalcpart.cpp:86 #, kde-format msgid "Seconds" msgstr "Sekunden" #: texttemplateinterface.cpp:53 #, kde-format msgid "No file name given for template" msgstr "Für die Vorlage ist kein Name angegeben worden." #: texttemplateinterface.cpp:61 #, kde-format msgid "Could not find template file %1" msgstr "Die Vorlagendatei „%1“ kann nicht gefunden werden." kraft-0.97/po/kraft.pot000066400000000000000000002125621410616450300150410ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-06-14 21:37+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: addeditchapterdialog.cpp:35 #, kde-format msgid "Add/Edit Catalog Chapter" msgstr "" #: addeditchapterdialog.cpp:41 #, kde-format msgid "Create a new Catalog Chapter" msgstr "" #: addeditchapterdialog.cpp:45 #, kde-format msgid "Chapter Name:" msgstr "" #: addeditchapterdialog.cpp:49 #, kde-format msgid "Chapter Description:" msgstr "" #: addeditchapterdialog.cpp:76 #, kde-format msgid "Create new Catalog Chapter below chapter %1" msgstr "" #: addeditchapterdialog.cpp:83 #, kde-format msgid "Edit name and description of chapter %1" msgstr "" #: alldocsview.cpp:64 #, kde-format msgid "All documents" msgstr "" #: alldocsview.cpp:65 #, kde-format msgid "Documents of last week" msgstr "" #: alldocsview.cpp:66 #, kde-format msgid "Documents of last month" msgstr "" #: alldocsview.cpp:72 #, kde-format msgid "&Show: " msgstr "" #: alldocsview.cpp:79 #, kde-format msgid "&Search: " msgstr "" #: alldocsview.cpp:134 rc.cpp:634 rc.cpp:1328 portal.cpp:289 portal.cpp:290 #, kde-format msgid "Document Actions" msgstr "" #: archdoc.cpp:70 #, kde-format msgid "%1 for %2 (Id %3)" msgstr "" #: catalogselection.cpp:50 #, kde-format msgid "Selected &Catalog: " msgstr "" #: catalogselection.cpp:159 #, kde-format msgid "Append to Document" msgstr "" #: catalogtemplate.cpp:52 #, kde-format msgid "Manual Price" msgstr "" #: catalogtemplate.cpp:54 #, kde-format msgid "Calculated" msgstr "" #: catalogtemplate.cpp:56 #, kde-format msgid "AutoCalc" msgstr "" #: catalogtemplate.cpp:57 #, kde-format msgid "Err: Unknown type %d" msgstr "" #: docassistant.cpp:52 #, kde-format msgid "Show &Templates" msgstr "" #: docassistant.cpp:57 #, kde-format msgid "Show mask to create or select templates to be used in the document" msgstr "" #: docassistant.cpp:121 #, kde-format msgid "Add a template to the document" msgstr "" #: docassistant.cpp:127 #, kde-format msgid "Create a new template" msgstr "" #: docassistant.cpp:134 #, kde-format msgid "Edit the current template" msgstr "" #: docassistant.cpp:141 #, kde-format msgid "Delete the current template" msgstr "" #: docassistant.cpp:305 #, kde-format msgid "" "Do you really want to delete the template permanently?\n" "It can not be recovered." msgstr "" #: docdigestdetailview.cpp:224 rc.cpp:78 rc.cpp:120 rc.cpp:988 rc.cpp:1030 #, kde-format msgid "Amount" msgstr "" #: docdigestdetailview.cpp:225 #, kde-format msgid "Type" msgstr "" #: docdigestdetailview.cpp:226 #, kde-format msgid "Sum" msgstr "" #: docdigestdetailview.cpp:255 #, kde-format msgid "Results in %1 %2" msgstr "" #: docdigestdetailview.cpp:256 docdigestdetailview.cpp:289 #, kde-format msgid "Year" msgstr "" #: docdigestdetailview.cpp:258 #, kde-format msgid "Month" msgstr "" #: docdigestdetailview.cpp:291 #, kde-format msgid "Results in Year %1" msgstr "" #: docdigestdetailview.cpp:310 docdigestdetailview.cpp:319 #, kde-format msgid "Customer" msgstr "" #: docdigestdetailview.cpp:314 #, kde-format msgid "not set" msgstr "" #: docdigestdetailview.cpp:330 #, kde-format msgid "The address is not listed in an address book." msgstr "" #: docdigestdetailview.cpp:332 #, kde-format msgid "" "The client has the address book id %1 but can not found in our address books." msgstr "" #: docdigestdetailview.cpp:335 #, kde-format msgid "The client can be found in our address books." msgstr "" #: docdigestdetailview.cpp:346 prefsdialog.cpp:636 #, kde-format msgid "preferred address" msgstr "" #: docdigestdetailview.cpp:350 prefsdialog.cpp:640 #, kde-format msgid "home address" msgstr "" #: docdigestdetailview.cpp:354 prefsdialog.cpp:644 #, kde-format msgid "work address" msgstr "" #: docdigestdetailview.cpp:358 prefsdialog.cpp:648 #, kde-format msgid "postal address" msgstr "" #: docdigestdetailview.cpp:362 prefsdialog.cpp:652 #, kde-format msgid "international address" msgstr "" #: docdigestdetailview.cpp:366 prefsdialog.cpp:656 #, kde-format msgid "domestic address" msgstr "" #: docdigestdetailview.cpp:370 prefsdialog.cpp:660 #, kde-format msgid "unknown" msgstr "" #: docdigestdetailview.cpp:407 models/docbasemodel.cpp:32 #, kde-format msgid "Date" msgstr "" #: docdigestdetailview.cpp:412 models/docbasemodel.cpp:35 #, kde-format msgid "Whiteboard" msgstr "" #: docdigestdetailview.cpp:417 models/docbasemodel.cpp:39 #, kde-format msgid "Project" msgstr "" #: docdigestdetailview.cpp:427 #, kde-format msgid "This document was never printed." msgstr "" #: docdigestdetailview.cpp:434 #, kde-format msgid "Last printed" msgstr "" #: docdigestdetailview.cpp:435 #, kde-format msgid "Opens last created PDF document" msgstr "" #: docdigestdetailview.cpp:436 #, kde-format msgid "open" msgstr "" #: docdigestdetailview.cpp:440 #, kde-format msgid "One older print" msgstr "" #: docdigestdetailview.cpp:442 #, kde-format msgid "%1 older prints" msgstr "" #: docdigestdetailview.cpp:446 #, kde-format msgid "Archived documents can not be found. Check PDF Output dir." msgstr "" #: docpostcard.cpp:34 #, kde-format msgid "Document Overview" msgstr "" #: docpostcard.cpp:122 #, kde-format msgid "Netto:" msgstr "" #: docpostcard.cpp:133 docpostcard.cpp:140 #, kde-format msgid "+ %1% Tax:" msgstr "" #: docpostcard.cpp:146 #, kde-format msgid "Sum Tax:" msgstr "" #: docpostcard.cpp:151 #, kde-format msgid "Total:" msgstr "" #: docpostcard.cpp:243 #, kde-format msgid "%1 Items" msgstr "" #: docpostcard.cpp:245 #, kde-format msgid "%1 Items, netto %2" msgstr "" #: doctext.cpp:61 #, kde-format msgid "Standard" msgstr "" #: doctext.cpp:76 #, kde-format msgid "Header Text" msgstr "" #: doctext.cpp:77 #, kde-format msgid "Footer Text" msgstr "" #: doctext.cpp:78 #, kde-format msgid "Items" msgstr "" #: doctext.cpp:80 defaultprovider.cpp:55 positionviewwidget.cpp:413 #, kde-format msgid "Unknown" msgstr "" #: doctypeedit.cpp:48 #, kde-format msgid "" msgstr "" #: doctypeedit.cpp:49 #, kde-format msgid "
      " msgstr "" #: doctypeedit.cpp:132 doctypeedit.cpp:158 #, kde-format msgid "Add Document Type" msgstr "" #: doctypeedit.cpp:133 #, kde-format msgid "Enter the name of a new document type" msgstr "" #: doctypeedit.cpp:159 #, kde-format msgid "Edit the name of a document type" msgstr "" #: documentman.cpp:93 #, kde-format msgctxt "" "Text to be inserted into a doc, if the sum of another doc needs to be " "substracted ie. in a final invoice if there was a partial invoice before%1 " "is substited by the doc type, %2 by the id of the predecessor doc." msgid "Substract sum from %1 %2" msgstr "" #: documenttemplate.cpp:112 #, kde-format msgctxt "Sequence number printed on the document" msgid "No." msgstr "" #: documenttemplate.cpp:113 #, kde-format msgctxt "Document item printed on the document" msgid "Item" msgstr "" #: documenttemplate.cpp:114 #, kde-format msgctxt "Abbrev. of Quantity printed on the document" msgid "Qty." msgstr "" #: documenttemplate.cpp:115 #, kde-format msgctxt "Unit printed on the document" msgid "Unit" msgstr "" #: documenttemplate.cpp:116 #, kde-format msgctxt "Price of an item printed on the document" msgid "Price" msgstr "" #: documenttemplate.cpp:117 #, kde-format msgctxt "Printed on the document" msgid "Sum" msgstr "" #: documenttemplate.cpp:118 #, kde-format msgctxt "printed on the document" msgid "Net" msgstr "" #: documenttemplate.cpp:119 #, kde-format msgctxt "Printed on the document" msgid "VAT" msgstr "" #: documenttemplate.cpp:121 #, kde-format msgctxt "Printed on the document" msgid "Phone" msgstr "" #: documenttemplate.cpp:122 #, kde-format msgctxt "Printed on the document" msgid "FAX" msgstr "" #: documenttemplate.cpp:123 #, kde-format msgctxt "Printed on the document" msgid "Mobile" msgstr "" #: documenttemplate.cpp:124 #, kde-format msgctxt "Printed on the document" msgid "Email" msgstr "" #: documenttemplate.cpp:125 #, kde-format msgctxt "Printed on the document" msgid "Website" msgstr "" #: documenttemplate.cpp:127 #, kde-format msgctxt "Printed on the document" msgid "Page" msgstr "" #: documenttemplate.cpp:128 #, kde-format msgctxt "the 'of' in page X of Y" msgid "of" msgstr "" #: documenttemplate.cpp:129 #, kde-format msgctxt "Document number on document" msgid "Document No." msgstr "" #: documenttemplate.cpp:130 #, kde-format msgctxt "Date on document" msgid "Date" msgstr "" #: documenttemplate.cpp:131 #, kde-format msgctxt "Project label" msgid "Project" msgstr "" #: documenttemplate.cpp:132 #, kde-format msgctxt "Customer ID on document" msgid "Customer Id" msgstr "" #: documenttemplate.cpp:267 #, kde-format msgid "" "Please note: This offer contains %1 alternative or demand positions, printed " "in italic font. These do not add to the overall sum." msgstr "" #: documenttemplate.cpp:278 #, kde-format msgid "tax free items (%1 pcs.)" msgstr "" #: documenttemplate.cpp:284 #, kde-format msgid "items with reduced tax of %1% (%2 pcs.)" msgstr "" #: documenttemplate.cpp:293 #, kde-format msgid "No label: items with full tax of %1% (%2 pcs.)" msgstr "" #: documenttemplate.cpp:323 kraftview_ro.cpp:240 #, kde-format msgid "reduced VAT" msgstr "" #: documenttemplate.cpp:331 kraftview_ro.cpp:212 kraftview_ro.cpp:249 rc.cpp:21 #: rc.cpp:931 #, kde-format msgid "VAT" msgstr "" #: documenttemplate.cpp:363 #, kde-format msgid "Template to convert is not existing!" msgstr "" #: documenttemplate.cpp:366 #, kde-format msgid "Can not read template file!" msgstr "" #: calcpart.cpp:98 #, kde-format msgid "Base" msgstr "" #: filterheader.cpp:40 addressselectorwidget.cpp:294 #, kde-format msgid "&Search:" msgstr "" #: fixcalcdialog.cpp:36 #, kde-format msgid "Calculation Fix Item" msgstr "" #: flostempldialog.cpp:71 #, kde-format msgid "Create or Edit Template Items" msgstr "" #: flostempldialog.cpp:213 flostempldialog.cpp:604 #, kde-format msgid "No" msgstr "" #: flostempldialog.cpp:214 flostempldialog.cpp:604 #, kde-format msgid "Yes" msgstr "" #: flostempldialog.cpp:251 #, kde-format msgid "Calculated Price: " msgstr "" #: flostempldialog.cpp:255 #, kde-format msgid "Manual Price: " msgstr "" #: flostempldialog.cpp:260 #, kde-format msgid "(+%1%)" msgstr "" #: flostempldialog.cpp:264 #, kde-format msgid "%1%" msgstr "" #: flostempldialog.cpp:266 #, kde-format msgid ": " msgstr "" #: flostempldialog.cpp:388 #, kde-format msgid "Template Error" msgstr "" #: flostempldialog.cpp:388 #, kde-format msgid "Saving of this template failed, sorry" msgstr "" #: flostempldialog.cpp:416 #, kde-format msgid "The template has been modified." msgstr "" #: flostempldialog.cpp:417 #, kde-format msgid "Do you want to discard your changes?" msgstr "" #: flostempldialog.cpp:446 #, kde-format msgid "" "The catalog chapter was changed for this template.\n" "Do you really want to move the template to the new chapter?" msgstr "" #: flostempldialog.cpp:448 #, kde-format msgid "Chapter Change" msgstr "" #: flostempldialog.h:104 #, kde-format msgid "Calculated material" msgstr "" #: katalog.cpp:137 #, kde-format msgid "not found" msgstr "" #: main.cpp:53 #, kde-format msgid "Open document with arch doc number " msgstr "" #: main.cpp:54 #, kde-format msgid "Open Kraft in read only mode - document changes prohibited" msgstr "" #: importfilter.cpp:50 #, kde-format msgid "Unable to find filter called %1" msgstr "" #: importfilter.cpp:58 #, kde-format msgid "Could not open the definition file!" msgstr "" #: importfilter.cpp:143 #, kde-format msgid "Unknown tags: " msgstr "" #: importfilter.cpp:184 #, kde-format msgid "Could not recode input file!" msgstr "" #: importfilter.cpp:192 #, kde-format msgid "Unable to open temp file " msgstr "" #: importfilter.cpp:198 #, kde-format msgid "Could not open the import source file!" msgstr "" #: importitemdialog.cpp:47 #, kde-format msgid "Import Items From File" msgstr "" #: importitemdialog.cpp:116 #, kde-format msgid "the Header of the Document" msgstr "" #: importitemdialog.cpp:125 templtopositiondialogbase.cpp:58 #, kde-format msgid "..." msgstr "" #: inserttempldialog.cpp:105 #, kde-format msgid "Create a new Item" msgstr "" #: inserttempldialog.cpp:107 #, kde-format msgid "Create a new Item from Template" msgstr "" #: kataloglistview.cpp:350 #, kde-format msgid "A catalog chapter can not be deleted as long it has children." msgstr "" #: kataloglistview.cpp:351 #, kde-format msgid "Chapter can not be deleted" msgstr "" #: katalogview.cpp:157 #, kde-format msgid "Edit Sub chapter" msgstr "" #: katalogview.cpp:159 #, kde-format msgid "Edit a catalog sub chapter" msgstr "" #: katalogview.cpp:163 #, kde-format msgid "Add a sub chapter" msgstr "" #: katalogview.cpp:165 #, kde-format msgid "Add a sub chapter below the selected one" msgstr "" #: katalogview.cpp:169 katalogview.cpp:171 #, kde-format msgid "Remove a sub chapter" msgstr "" #: katalogview.cpp:175 rc.cpp:3 rc.cpp:913 #, kde-format msgid "Edit Template" msgstr "" #: katalogview.cpp:177 #, kde-format msgid "Opens the editor window for templates to edit the selected one" msgstr "" #: katalogview.cpp:182 #, kde-format msgid "New template" msgstr "" #: katalogview.cpp:184 #, kde-format msgid "Opens the editor window for templates to enter a new template" msgstr "" #: katalogview.cpp:189 #, kde-format msgid "Delete template" msgstr "" #: katalogview.cpp:191 #, kde-format msgid "Deletes the template" msgstr "" #: katalogview.cpp:196 #, kde-format msgid "Export catalog" msgstr "" #: katalogview.cpp:198 #, kde-format msgid "Export the whole catalog as XML encoded file" msgstr "" #: katalogview.cpp:203 #, kde-format msgid "Import catalog" msgstr "" #: katalogview.cpp:205 #, kde-format msgid "Import a catalog from a XML file" msgstr "" #: katalogview.cpp:209 rc.cpp:487 rc.cpp:1358 #, kde-format msgid "&Catalog" msgstr "" #: katalogview.cpp:227 #, kde-format msgid "Opening file..." msgstr "" #: katalogview.cpp:229 katalogview.cpp:295 katalogview.cpp:309 #: katalogview.cpp:318 katalogview.cpp:327 portal.cpp:659 portal.cpp:683 #: portal.cpp:1002 #, kde-format msgid "Ready." msgstr "" #: katalogview.cpp:234 portal.cpp:946 #, kde-format msgid "Exiting..." msgstr "" #: katalogview.cpp:291 #, kde-format msgid "Exporting file..." msgstr "" #: katalogview.cpp:300 #, kde-format msgid "Importfile... (not yet implemented)" msgstr "" #: katalogview.cpp:305 #, kde-format msgid "Creating a new sub chapter..." msgstr "" #: katalogview.cpp:314 #, kde-format msgid "Editing a sub chapter..." msgstr "" #: katalogview.cpp:323 #, kde-format msgid "Removing a sub chapter..." msgstr "" #: katalogview.cpp:355 #, kde-format msgid "Created at:%1" msgstr "" #: katalogview.cpp:358 #, kde-format msgid "  Last used:%1" msgstr "" #: katalogview.cpp:365 #, kde-format msgid "Modified at:%1" msgstr "" #: katalogview.cpp:368 #, kde-format msgid "  Use Count:%1" msgstr "" #: kraftdoc.cpp:159 #, kde-format msgctxt "First argument is the doctype, like Invoice, followed by the ID" msgid "%1 (Id %2)" msgstr "" #: kraftdoc.cpp:307 #, kde-format msgctxt "Document part header" msgid "Header" msgstr "" #: kraftdoc.cpp:309 #, kde-format msgctxt "Document part footer" msgid "Footer" msgstr "" #: kraftdoc.cpp:311 #, kde-format msgctxt "Document part containing the items" msgid "Items" msgstr "" #: kraftdoc.cpp:313 #, kde-format msgid "Unknown document part" msgstr "" #: positionviewwidget.cpp:92 #, kde-format msgid "Item Actions" msgstr "" #: positionviewwidget.cpp:95 #, kde-format msgid "Item Kind" msgstr "" #: positionviewwidget.cpp:96 positionviewwidget.cpp:671 #, kde-format msgid "Normal" msgstr "" #: positionviewwidget.cpp:98 positionviewwidget.cpp:679 #, kde-format msgid "Alternative" msgstr "" #: positionviewwidget.cpp:100 #, kde-format msgid "On Demand" msgstr "" #: positionviewwidget.cpp:105 rc.cpp:265 rc.cpp:874 #, kde-format msgid "Tax" msgstr "" #: positionviewwidget.cpp:108 #, kde-format msgid "Taxfree Item" msgstr "" #: positionviewwidget.cpp:114 #, kde-format msgid "Reduced Tax" msgstr "" #: positionviewwidget.cpp:120 #, kde-format msgid "Full Tax" msgstr "" #: positionviewwidget.cpp:129 #, kde-format msgid "Move Up" msgstr "" #: positionviewwidget.cpp:131 #, kde-format msgid "Move Down" msgstr "" #: positionviewwidget.cpp:133 #, kde-format msgid "Lock Item" msgstr "" #: positionviewwidget.cpp:135 #, kde-format msgid "Unlock Item" msgstr "" #: positionviewwidget.cpp:137 #, kde-format msgid "Delete Item" msgstr "" #: positionviewwidget.cpp:221 #, kde-format msgid "All items" msgstr "" #: positionviewwidget.cpp:231 #, kde-format msgid "%1-tagged items" msgstr "" #: positionviewwidget.cpp:263 #, kde-format msgid "Tag: %1" msgstr "" #: positionviewwidget.cpp:265 #, kde-format msgid "Tags:
        " msgstr "" #: positionviewwidget.cpp:276 #, kde-format msgid "No tags assigned yet." msgstr "" #: positionviewwidget.cpp:405 #, kde-format msgid "Active" msgstr "" #: positionviewwidget.cpp:407 #, kde-format msgid "New" msgstr "" #: positionviewwidget.cpp:409 #, kde-format msgid "Deleted" msgstr "" #: positionviewwidget.cpp:411 #, kde-format msgid "Locked" msgstr "" #: positionviewwidget.cpp:618 #, kde-format msgid "" "This is an alternative item.

        Use the position toolbox to change " "the item type." msgstr "" #: positionviewwidget.cpp:634 #, kde-format msgid "" "This item is either completely optional or its amount varies depending on " "the needs.

        Use the item toolbox to change the item type." msgstr "" #: positionviewwidget.cpp:675 #, kde-format msgid "Demand" msgstr "" #: kraftdocfooteredit.cpp:52 #, kde-format msgid "Document Footer" msgstr "" #: kraftdocheaderedit.cpp:56 #, kde-format msgid "Document Header" msgstr "" #: kraftdocheaderedit.cpp:64 #, kde-format msgid "Manually set in address field." msgstr "" #: kraftdocpositionsedit.cpp:94 #, kde-format msgid "Add Item..." msgstr "" #: kraftdocpositionsedit.cpp:96 #, kde-format msgid "Add a normal item to the document manually." msgstr "" #: kraftdocpositionsedit.cpp:100 #, kde-format msgid "Add Discount Item" msgstr "" #: kraftdocpositionsedit.cpp:103 #, kde-format msgid "" "Adds an item to the document that allows discounts on other items in the " "document" msgstr "" #: kraftdocpositionsedit.cpp:106 #, kde-format msgid "Import Items..." msgstr "" #: kraftdocpositionsedit.cpp:109 #, kde-format msgid "Opens a dialog where multiple items can be imported from a text file." msgstr "" #: kraftdocpositionsedit.cpp:118 #, kde-format msgid "Document Items" msgstr "" #: kraftview.cpp:87 kraftview_ro.cpp:75 #, kde-format msgid "Document" msgstr "" #: kraftview.cpp:261 #, kde-format msgid "Successor of %1" msgstr "" #: kraftview.cpp:398 kraftview.cpp:843 #, kde-format msgid "" "The address label is not empty and different from the selected one.
        Do " "you really want to replace it with the text shown below?
        %1
        " msgstr "" #: kraftview.cpp:445 #, kde-format msgid "" "

        The Document Items List is still empty, but Items can be added now.To add items to the document either
        • Press the 'Add item' button " "above.
        • Open the template catalog by clicking on the 'show Template' " "button on the right and pick one of the available templates.
        " msgstr "" #: kraftview.cpp:630 prefsdialog.cpp:319 #, kde-format msgid "Display no tax at all" msgstr "" #: kraftview.cpp:631 prefsdialog.cpp:320 #, kde-format msgid "Calculate reduced tax for all items" msgstr "" #: kraftview.cpp:632 prefsdialog.cpp:321 #, kde-format msgid "Calculate full tax for all items" msgstr "" #: kraftview.cpp:633 #, kde-format msgid "Calculate individual tax for each item" msgstr "" #: kraftview.cpp:690 #, kde-format msgid "Tax Settings Overwrite" msgstr "" #: kraftview.cpp:691 #, kde-format msgid "Really overwrite all individual tax settings of the items?" msgstr "" #: kraftview.cpp:842 #, kde-format msgid "Address Overwrite" msgstr "" #: kraftview.cpp:1099 #, kde-format msgid "Discount" msgstr "" #: kraftview.cpp:1419 #, kde-format msgid "The document has been modified." msgstr "" #: kraftview.cpp:1420 #, kde-format msgid "Do you want to save your changes?" msgstr "" #: kraftview_ro.cpp:213 #, kde-format msgid "Reduced TAX" msgstr "" #: newdocassistant.cpp:52 newdocassistant.cpp:96 #, kde-format msgid "New Document Settings" msgstr "" #: newdocassistant.cpp:55 #, kde-format msgid "" "Please select a customer as addressee for the document. If there is no entry " "for the customer in the addressbook yet, it can be opened by clicking on the " "button below." msgstr "" #: newdocassistant.cpp:100 #, kde-format msgid "" "Select a document type and a date. A comment on the whiteboard helps to " "classify the document." msgstr "" #: newdocassistant.cpp:107 #, kde-format msgid "Customer: Not yet selected!" msgstr "" #: newdocassistant.cpp:116 #, kde-format msgid "Document &Type:" msgstr "" #: newdocassistant.cpp:120 #, kde-format msgid "Document Date: " msgstr "" #: newdocassistant.cpp:123 #, kde-format msgid "Whiteboard Content:" msgstr "" #: newdocassistant.cpp:127 #, kde-format msgid "Copy document items from predecessor document" msgstr "" #: newdocassistant.cpp:175 #, kde-format msgid "Create a new Kraft Document" msgstr "" #: newdocassistant.cpp:265 #, kde-format msgid "Followup Document for %1" msgstr "" #: materialkataloglistview.cpp:39 rc.cpp:108 rc.cpp:643 rc.cpp:1018 rc.cpp:1675 #, kde-format msgid "Material" msgstr "" #: materialkataloglistview.cpp:40 #, kde-format msgid "Pack" msgstr "" #: materialkataloglistview.cpp:41 rc.cpp:123 rc.cpp:1033 #, kde-format msgid "Unit" msgstr "" #: materialkataloglistview.cpp:42 #, kde-format msgid "Purchase" msgstr "" #: materialkataloglistview.cpp:43 #, kde-format msgid "Sale" msgstr "" #: materialkataloglistview.cpp:44 #, kde-format msgid "Last Modified" msgstr "" #: materialkataloglistview.cpp:50 #, kde-format msgid "Material Catalog" msgstr "" #: materialkatalogview.cpp:106 #, kde-format msgid "" msgstr "" #: materialkatalogview.cpp:136 templkatalogview.cpp:139 #, kde-format msgid "Do you really want to delete the template from the catalog?" msgstr "" #: materialselectdialog.cpp:39 #, kde-format msgid "Add Material to Calculation" msgstr "" #: materialselectdialog.cpp:44 #, kde-format msgid "

        Add Material to Calculation

        " msgstr "" #: rc.cpp:6 rc.cpp:916 templkataloglistview.cpp:46 texteditdialog.cpp:76 #, kde-format msgid "Template" msgstr "" #: rc.cpp:9 rc.cpp:919 #, kde-format msgid "Text:" msgstr "" #: rc.cpp:12 rc.cpp:922 #, kde-format msgid "&Store in Chapter" msgstr "" #: rc.cpp:15 rc.cpp:925 #, kde-format msgid "&Unit" msgstr "" #: rc.cpp:18 rc.cpp:928 #, kde-format msgid "&Count Time for Overalltime" msgstr "" #: rc.cpp:24 rc.cpp:934 #, kde-format msgid "full" msgstr "" #: rc.cpp:27 rc.cpp:937 #, kde-format msgid "half" msgstr "" #: rc.cpp:30 rc.cpp:940 #, kde-format msgid "Time Calculation" msgstr "" #: rc.cpp:33 rc.cpp:72 rc.cpp:111 rc.cpp:943 rc.cpp:982 rc.cpp:1021 #, kde-format msgid "text" msgstr "" #: rc.cpp:36 rc.cpp:946 #, kde-format msgid "Time measureable effort for this template:" msgstr "" #: rc.cpp:39 rc.cpp:81 rc.cpp:117 rc.cpp:949 rc.cpp:991 rc.cpp:1027 #, kde-format msgid "Label" msgstr "" #: rc.cpp:42 rc.cpp:952 #, kde-format msgid "Duration" msgstr "" #: rc.cpp:45 rc.cpp:955 #, kde-format msgid "Hourly Rate" msgstr "" #: rc.cpp:48 rc.cpp:958 #, kde-format msgid "Glob. Rate" msgstr "" #: rc.cpp:51 rc.cpp:961 #, kde-format msgid "Adds a new time calculation part to the template" msgstr "" #: rc.cpp:54 rc.cpp:93 rc.cpp:132 rc.cpp:964 rc.cpp:1003 rc.cpp:1042 #, kde-format msgid "new..." msgstr "" #: rc.cpp:57 rc.cpp:967 #, kde-format msgid "Edits the current time calculation part" msgstr "" #: rc.cpp:60 rc.cpp:99 rc.cpp:138 rc.cpp:970 rc.cpp:1009 rc.cpp:1048 #, kde-format msgid "edit..." msgstr "" #: rc.cpp:63 rc.cpp:973 #, kde-format msgid "Deletes the current time calculation part" msgstr "" #: rc.cpp:66 rc.cpp:105 rc.cpp:144 rc.cpp:976 rc.cpp:1015 rc.cpp:1054 #, kde-format msgid "delete" msgstr "" #: rc.cpp:69 rc.cpp:979 #, kde-format msgid "Fix Costs" msgstr "" #: rc.cpp:75 rc.cpp:985 #, kde-format msgid "Fix costs for this template per one unit:" msgstr "" #: rc.cpp:84 rc.cpp:994 #, kde-format msgid "Single Price" msgstr "" #: rc.cpp:87 rc.cpp:997 #, kde-format msgid "Overall Price" msgstr "" #: rc.cpp:90 rc.cpp:1000 #, kde-format msgid "adds a new fix calculation part" msgstr "" #: rc.cpp:96 rc.cpp:1006 #, kde-format msgid "edits the current fix calculation part" msgstr "" #: rc.cpp:102 rc.cpp:1012 #, kde-format msgid "deletes the current fix calculation part" msgstr "" #: rc.cpp:114 rc.cpp:1024 #, kde-format msgid "Needed materials for one unit of this template:" msgstr "" #: rc.cpp:126 rc.cpp:1036 prefswages.cpp:53 templkataloglistview.cpp:47 #, kde-format msgid "Price" msgstr "" #: rc.cpp:129 rc.cpp:1039 #, kde-format msgid "adds a new material calculation part" msgstr "" #: rc.cpp:135 rc.cpp:1045 #, kde-format msgid "edits the current material part" msgstr "" #: rc.cpp:141 rc.cpp:1051 #, kde-format msgid "deletes the current material calculation part" msgstr "" #: rc.cpp:147 rc.cpp:1057 #, kde-format msgid "Overall Price per Unit" msgstr "" #: rc.cpp:150 rc.cpp:1060 #, kde-format msgid "&Manual Price" msgstr "" #: rc.cpp:153 rc.cpp:1063 #, kde-format msgid "Calculated Price" msgstr "" #: rc.cpp:156 rc.cpp:1066 #, kde-format msgid "Fix Costs Part:" msgstr "" #: rc.cpp:159 rc.cpp:1069 #, kde-format msgid "Material Part:" msgstr "" #: rc.cpp:162 rc.cpp:1072 #, kde-format msgid "Profit:" msgstr "" #: rc.cpp:166 rc.cpp:1076 #, no-c-format, kde-format msgid " %" msgstr "" #: rc.cpp:169 rc.cpp:1079 #, kde-format msgid "Time Calculation Part:" msgstr "" #: rc.cpp:172 rc.cpp:1082 #, kde-format msgid "Calculated Price:" msgstr "" #: rc.cpp:175 rc.cpp:1085 #, kde-format msgid "88.888,88 €" msgstr "" #: rc.cpp:178 rc.cpp:1088 #, kde-format msgid "Database creation and initial schema setup:" msgstr "" #: rc.cpp:181 rc.cpp:190 rc.cpp:844 rc.cpp:1091 rc.cpp:1100 rc.cpp:1706 #, kde-format msgid "0 / 129" msgstr "" #: rc.cpp:184 rc.cpp:193 rc.cpp:847 rc.cpp:1094 rc.cpp:1103 rc.cpp:1709 #, kde-format msgid "X" msgstr "" #: rc.cpp:187 rc.cpp:1097 #, kde-format msgid "Filling the database with initial values:" msgstr "" #: rc.cpp:196 rc.cpp:850 rc.cpp:1106 rc.cpp:1712 #, kde-format msgid "Status: " msgstr "" #: rc.cpp:199 rc.cpp:1109 #, kde-format msgid "Database setup status..." msgstr "" #: rc.cpp:202 rc.cpp:1112 #, kde-format msgid "Bruns Data File:" msgstr "" #: rc.cpp:205 rc.cpp:1115 #, kde-format msgid "Bruns Key File:" msgstr "" #: rc.cpp:208 rc.cpp:1118 #, kde-format msgid "Qt Database Driver:" msgstr "" #: rc.cpp:211 rc.cpp:1121 #, kde-format msgid "Database Server:" msgstr "" #: rc.cpp:214 rc.cpp:1124 #, kde-format msgid "Server Port:" msgstr "" #: rc.cpp:217 rc.cpp:1127 #, kde-format msgid "Database Name" msgstr "" #: rc.cpp:220 rc.cpp:700 rc.cpp:1130 rc.cpp:1555 #, kde-format msgid "Database User:" msgstr "" #: rc.cpp:223 rc.cpp:1133 #, kde-format msgid "Database Password:" msgstr "" #: rc.cpp:226 rc.cpp:1136 #, kde-format msgid "The default database name when creating new databases" msgstr "" #: rc.cpp:229 rc.cpp:1139 #, kde-format msgid "File Name" msgstr "" #: rc.cpp:232 rc.cpp:1142 #, kde-format msgid "The path where database file are stored. Leave empty!" msgstr "" #: rc.cpp:235 rc.cpp:1145 #, kde-format msgid "Database Update:" msgstr "" #: rc.cpp:238 rc.cpp:1148 #, kde-format msgid "Overall Progress:" msgstr "" #: rc.cpp:241 rc.cpp:1151 #, kde-format msgid "Detail Progress:" msgstr "" #: rc.cpp:244 rc.cpp:1154 #, kde-format msgid "Status:" msgstr "" #: rc.cpp:247 rc.cpp:1289 #, kde-format msgid "" "

        Kraft uses a database backend to store values. By " "default it uses a file based database with easy setup targeted to single " "user mode.


        " msgstr "" #: rc.cpp:250 rc.cpp:1292 #, kde-format msgid "SQLite 3 - file based database (default)" msgstr "" #: rc.cpp:253 rc.cpp:1295 #, kde-format msgid "MySQL Serverbased Database for advanced Setups" msgstr "" #: rc.cpp:256 rc.cpp:865 #, kde-format msgid "Footer Texts" msgstr "" #: rc.cpp:259 rc.cpp:868 #, kde-format msgid "&Summary Text on Last Page:" msgstr "" #: rc.cpp:262 rc.cpp:871 #, kde-format msgid "&Greeting:" msgstr "" #: rc.cpp:268 rc.cpp:877 #, kde-format msgid "Document &Tax:" msgstr "" #: rc.cpp:271 rc.cpp:880 #, kde-format msgid "TextLabel" msgstr "" #: rc.cpp:274 rc.cpp:883 #, kde-format msgid "&Project:" msgstr "" #: rc.cpp:277 rc.cpp:886 #, kde-format msgid "&Whiteboard:" msgstr "" #: rc.cpp:280 rc.cpp:889 #, kde-format msgid "" "Enter a label that describes the project. This may appear on the customer " "document." msgstr "" #: rc.cpp:283 rc.cpp:892 #, kde-format msgid "Postal &Address:" msgstr "" #: rc.cpp:286 rc.cpp:895 #, kde-format msgid "not selected" msgstr "" #: rc.cpp:289 rc.cpp:898 #, kde-format msgid "Select an addressee from the address books." msgstr "" #: rc.cpp:292 rc.cpp:901 #, kde-format msgid "select..." msgstr "" #: rc.cpp:295 rc.cpp:904 #, kde-format msgid "&Salutatory Address:" msgstr "" #: rc.cpp:298 rc.cpp:907 #, kde-format msgid "Customer:" msgstr "" #: rc.cpp:301 rc.cpp:910 #, kde-format msgid "&Entry Text on First Page:" msgstr "" #: rc.cpp:304 rc.cpp:733 rc.cpp:1157 rc.cpp:1588 #, kde-format msgid "Click to add a new document type to the list." msgstr "" #: rc.cpp:307 rc.cpp:1160 prefsdialog.cpp:184 prefsunits.cpp:79 #: prefswages.cpp:90 #, kde-format msgid "Add" msgstr "" #: rc.cpp:310 rc.cpp:1163 #, kde-format msgid "click to edit the selected document type name" msgstr "" #: rc.cpp:313 rc.cpp:1166 prefsunits.cpp:83 prefswages.cpp:94 #, kde-format msgid "Edit" msgstr "" #: rc.cpp:316 rc.cpp:739 rc.cpp:1169 rc.cpp:1594 #, kde-format msgid "click to remove the current document type" msgstr "" #: rc.cpp:319 rc.cpp:1172 portalview.cpp:220 prefsdialog.cpp:188 #: prefsunits.cpp:88 prefswages.cpp:99 #, kde-format msgid "Remove" msgstr "" #: rc.cpp:322 rc.cpp:1175 #, kde-format msgid "Unique Document Number" msgstr "" #: rc.cpp:325 rc.cpp:1178 #, kde-format msgid "Number &Cycle:" msgstr "" #: rc.cpp:328 rc.cpp:337 rc.cpp:340 rc.cpp:724 rc.cpp:1181 rc.cpp:1190 #: rc.cpp:1193 rc.cpp:1579 #, kde-format msgid "example" msgstr "" #: rc.cpp:331 rc.cpp:1184 #, kde-format msgid "ident Template:" msgstr "" #: rc.cpp:334 rc.cpp:718 rc.cpp:1187 rc.cpp:1573 #, kde-format msgid "Example Id:" msgstr "" #: rc.cpp:343 rc.cpp:1196 #, kde-format msgid "Counter:" msgstr "" #: rc.cpp:346 rc.cpp:1199 #, kde-format msgid "&Edit Number Cycles..." msgstr "" #: rc.cpp:349 rc.cpp:1202 #, kde-format msgid "Template for PDF Creation" msgstr "" #: rc.cpp:352 rc.cpp:1205 #, kde-format msgid "&Template File" msgstr "" #: rc.cpp:355 rc.cpp:1208 #, kde-format msgid "W&atermark:" msgstr "" #: rc.cpp:358 rc.cpp:1211 #, kde-format msgid "no watermark" msgstr "" #: rc.cpp:361 rc.cpp:1214 #, kde-format msgid "on first page" msgstr "" #: rc.cpp:364 rc.cpp:1217 #, kde-format msgid "on all pages" msgstr "" #: rc.cpp:367 rc.cpp:1220 #, kde-format msgid "&Watermark File" msgstr "" #: rc.cpp:370 rc.cpp:1223 #, kde-format msgid "Calculation Parts Fix" msgstr "" #: rc.cpp:373 rc.cpp:1226 #, kde-format msgid "

        Fix Cost Parts

        " msgstr "" #: rc.cpp:376 rc.cpp:1229 #, kde-format msgid "Add a fix cost for one unit of the template:" msgstr "" #: rc.cpp:379 rc.cpp:808 rc.cpp:1232 rc.cpp:1529 #, kde-format msgid "&Label:" msgstr "" #: rc.cpp:382 rc.cpp:1235 #, kde-format msgid "describing text" msgstr "" #: rc.cpp:385 rc.cpp:1238 #, kde-format msgid "amortisation" msgstr "" #: rc.cpp:388 rc.cpp:680 rc.cpp:1241 rc.cpp:1511 #, kde-format msgid "&Amount:" msgstr "" #: rc.cpp:391 rc.cpp:1244 #, kde-format msgid "amount multiplier" msgstr "" #: rc.cpp:394 rc.cpp:1247 #, kde-format msgid "at &Price:" msgstr "" #: rc.cpp:397 rc.cpp:683 rc.cpp:1250 rc.cpp:1514 #, kde-format msgid "Price for one piece" msgstr "" #: rc.cpp:400 rc.cpp:1253 #, kde-format msgid "€" msgstr "" #: rc.cpp:403 rc.cpp:1256 #, kde-format msgid "Form" msgstr "" #: rc.cpp:406 rc.cpp:1259 tagtemplatesdialog.cpp:58 #, kde-format msgid "Name:" msgstr "" #: rc.cpp:409 rc.cpp:1262 #, kde-format msgid "Organization:" msgstr "" #: rc.cpp:412 rc.cpp:1265 #, kde-format msgid "Street:" msgstr "" #: rc.cpp:415 rc.cpp:1268 #, kde-format msgid "Post Code:" msgstr "" #: rc.cpp:418 rc.cpp:1271 #, kde-format msgid "City:" msgstr "" #: rc.cpp:421 rc.cpp:1274 #, kde-format msgid "Phone:" msgstr "" #: rc.cpp:424 rc.cpp:1277 #, kde-format msgid "Fax:" msgstr "" #: rc.cpp:427 rc.cpp:1280 #, kde-format msgid "Mobile Phone:" msgstr "" #: rc.cpp:430 rc.cpp:1283 #, kde-format msgid "EMail:" msgstr "" #: rc.cpp:433 rc.cpp:1286 #, kde-format msgid "Website:" msgstr "" #: rc.cpp:436 rc.cpp:1298 #, kde-format msgid "Import Document Items" msgstr "" #: rc.cpp:439 rc.cpp:1301 #, kde-format msgid "Import information" msgstr "" #: rc.cpp:442 rc.cpp:1304 #, kde-format msgid "Select a &File to import from:" msgstr "" #: rc.cpp:445 rc.cpp:1307 #, kde-format msgid "FixMe!" msgstr "" #: rc.cpp:448 rc.cpp:1310 #, kde-format msgid "Import &Schema:" msgstr "" #: rc.cpp:451 rc.cpp:1313 #, kde-format msgid "this is interesting information about the selected schema" msgstr "" #: rc.cpp:454 rc.cpp:1316 #, kde-format msgid "Insert all Items &after" msgstr "" #: rc.cpp:457 rc.cpp:481 rc.cpp:1319 rc.cpp:1352 #, kde-format msgid "Tags" msgstr "" #: rc.cpp:460 rc.cpp:1331 templtopositiondialogbase.cpp:35 #, kde-format msgid "Create Item from Template" msgstr "" #: rc.cpp:463 rc.cpp:1334 #, kde-format msgid "New Item Text" msgstr "" #: rc.cpp:466 rc.cpp:1337 #, kde-format msgid "&insert" msgstr "" #: rc.cpp:469 rc.cpp:1340 #, kde-format msgid "à" msgstr "" #: rc.cpp:472 rc.cpp:1343 #, kde-format msgid "&after item" msgstr "" #: rc.cpp:475 rc.cpp:1346 #, kde-format msgid "Keep this item as template for future documents" msgstr "" #: rc.cpp:478 rc.cpp:1349 #, kde-format msgid "save in &chapter" msgstr "" #: rc.cpp:484 rc.cpp:1355 portal.cpp:215 #, kde-format msgid "&File" msgstr "" #: rc.cpp:490 rc.cpp:1361 #, kde-format msgid "Do Database Initialisation" msgstr "" #: rc.cpp:493 rc.cpp:1364 #, kde-format msgid "Do XML archiving of documents they're printed?" msgstr "" #: rc.cpp:496 rc.cpp:1367 #, kde-format msgid "" "Where Kraft stores the XML archive documents. If empty, KDEHOME/share/apps " "is used." msgstr "" #: rc.cpp:499 rc.cpp:1370 #, kde-format msgid "The local xml document storage path" msgstr "" #: rc.cpp:502 rc.cpp:1373 #, kde-format msgid "Default mail user agent. Set xdg for xdg-email" msgstr "" #: rc.cpp:505 rc.cpp:508 rc.cpp:1376 rc.cpp:1379 #, kde-format msgid "The default geometry of the document view dialog" msgstr "" #: rc.cpp:511 rc.cpp:1382 #, kde-format msgid "The current state of the portal" msgstr "" #: rc.cpp:514 rc.cpp:1385 #, kde-format msgid "The current geometry of the portal" msgstr "" #: rc.cpp:517 rc.cpp:1388 #, kde-format msgid "The current geometry of the new doc assistant" msgstr "" #: rc.cpp:520 rc.cpp:1391 #, kde-format msgid "The splitter position of the document view dialog" msgstr "" #: rc.cpp:523 rc.cpp:1394 #, kde-format msgid "The default size of the material catalog view" msgstr "" #: rc.cpp:526 rc.cpp:1397 #, kde-format msgid "The splitter setting for the doc assistant" msgstr "" #: rc.cpp:529 rc.cpp:1400 #, kde-format msgid "The window size of the template to document dialog for plants" msgstr "" #: rc.cpp:532 rc.cpp:1403 #, kde-format msgid "The window size of the template to document dialog" msgstr "" #: rc.cpp:535 rc.cpp:1406 #, kde-format msgid "The digest list column arrangement for the Latest-list" msgstr "" #: rc.cpp:538 rc.cpp:1409 #, kde-format msgid "The digest list column arrangement for the all-list" msgstr "" #: rc.cpp:541 rc.cpp:1412 #, kde-format msgid "The digest list column arrangement for timeline" msgstr "" #: rc.cpp:544 rc.cpp:1415 #, kde-format msgid "The sizes of the slider in the address picker Widget" msgstr "" #: rc.cpp:547 rc.cpp:1418 #, kde-format msgid "The state of the treeview inside the address selector widget" msgstr "" #: rc.cpp:550 rc.cpp:1421 #, kde-format msgid "Size of the address select dialog" msgstr "" #: rc.cpp:553 rc.cpp:1424 #, kde-format msgid "State of the window of the material catalog" msgstr "" #: rc.cpp:556 rc.cpp:1427 #, kde-format msgid "Geometry of the material catalog" msgstr "" #: rc.cpp:559 rc.cpp:1430 #, kde-format msgid "State of the header of the material catalog" msgstr "" #: rc.cpp:562 rc.cpp:1433 #, kde-format msgid "State of the window of the template catalog" msgstr "" #: rc.cpp:565 rc.cpp:1436 #, kde-format msgid "Geometry the template catalog" msgstr "" #: rc.cpp:568 rc.cpp:1439 #, kde-format msgid "State of the header of the template catalog" msgstr "" #: rc.cpp:571 rc.cpp:1442 #, kde-format msgid "" "Default percentage the sale price for a material should be higher than its " "purchase price" msgstr "" #: rc.cpp:574 rc.cpp:1445 #, kde-format msgid "The name of the last selected chapter in the catalog" msgstr "" #: rc.cpp:577 rc.cpp:1448 #, kde-format msgid "The complete filename of the trml2pdf binary" msgstr "" #: rc.cpp:580 rc.cpp:1451 #, kde-format msgid "The path to the output directory for document pdfs" msgstr "" #: rc.cpp:583 rc.cpp:1454 #, kde-format msgid "The last created doc type." msgstr "" #: rc.cpp:586 rc.cpp:1457 #, kde-format msgid "The date format for print." msgstr "" #: rc.cpp:589 rc.cpp:1460 #, kde-format msgid "The greeting below on the document footer." msgstr "" #: rc.cpp:592 rc.cpp:1463 #, kde-format msgid "The salut message on the document header." msgstr "" #: rc.cpp:595 rc.cpp:1466 #, kde-format msgid "" "The name of the catalog chapter where to store new templates in by default" msgstr "" #: rc.cpp:598 rc.cpp:1469 #, kde-format msgid "The name of the last used import schema for items." msgstr "" #: rc.cpp:601 rc.cpp:1472 #, kde-format msgid "The name of the last used input file for items." msgstr "" #: rc.cpp:604 rc.cpp:1475 #, kde-format msgid "" "User name as reference to the KAddressbook to identify 'my' address " "(DEPRECATED)." msgstr "" #: rc.cpp:607 rc.cpp:1478 #, kde-format msgid "" "UID of the user as reference to the KAddressbook to identify 'my' address." msgstr "" #: rc.cpp:610 rc.cpp:1481 #, kde-format msgid "The doc id template" msgstr "" #: rc.cpp:613 rc.cpp:1484 #, kde-format msgid "Localization on document level" msgstr "" #: rc.cpp:616 rc.cpp:1487 #, kde-format msgid "The tax default for new documents." msgstr "" #: rc.cpp:619 rc.cpp:1490 #, kde-format msgid "The label for alternative positions" msgstr "" #: rc.cpp:622 rc.cpp:625 rc.cpp:1493 rc.cpp:1496 #, kde-format msgid "The label for demand positions" msgstr "" #: rc.cpp:628 rc.cpp:1322 portal.cpp:224 #, kde-format msgid "&Document" msgstr "" #: rc.cpp:631 rc.cpp:1325 #, kde-format msgid "Settings" msgstr "" #: rc.cpp:637 rc.cpp:1669 #, kde-format msgid "Edit Material" msgstr "" #: rc.cpp:640 rc.cpp:1672 #, kde-format msgid "Store in C&hapter" msgstr "" #: rc.cpp:646 rc.cpp:1678 #, kde-format msgid "Pac&kaged:" msgstr "" #: rc.cpp:649 rc.cpp:1681 #, kde-format msgid "per P&ackage" msgstr "" #: rc.cpp:652 rc.cpp:1684 #, kde-format msgid "Prices" msgstr "" #: rc.cpp:655 rc.cpp:1687 #, kde-format msgid "= Price of &Sale:" msgstr "" #: rc.cpp:658 rc.cpp:1690 #, kde-format msgid "pl&us" msgstr "" #: rc.cpp:662 rc.cpp:755 rc.cpp:1610 rc.cpp:1694 #, no-c-format, kde-format msgid "%" msgstr "" #: rc.cpp:665 rc.cpp:1697 #, kde-format msgid "&Purchase Price:" msgstr "" #: rc.cpp:668 rc.cpp:1499 #, kde-format msgid "Calculation Item Material" msgstr "" #: rc.cpp:671 rc.cpp:1502 #, kde-format msgid "

        Calculation Part 'Material'

        " msgstr "" #: rc.cpp:674 rc.cpp:1505 #, kde-format msgid "Add Material to the template calculation." msgstr "" #: rc.cpp:677 rc.cpp:1508 #, kde-format msgid "material" msgstr "" #: rc.cpp:686 rc.cpp:1517 #, kde-format msgid "unit" msgstr "" #: rc.cpp:689 rc.cpp:1544 #, kde-format msgid "" "Please enter the MySQL Database server settings. \n" "\n" "For detailed setup instructions for the MySQL to use with Kraft please check " "the Kraft website." msgstr "" #: rc.cpp:694 rc.cpp:1549 #, kde-format msgid "Database Host:" msgstr "" #: rc.cpp:697 rc.cpp:1552 #, kde-format msgid "Database Name:" msgstr "" #: rc.cpp:703 rc.cpp:1558 #, kde-format msgid "Password:" msgstr "" #: rc.cpp:706 rc.cpp:1561 #, kde-format msgid "

        Edit Number Cycles

        " msgstr "" #: rc.cpp:709 rc.cpp:1564 #, kde-format msgid "Number Cycle Details" msgstr "" #: rc.cpp:712 rc.cpp:1567 #, kde-format msgid "&Number Cycle:" msgstr "" #: rc.cpp:715 rc.cpp:1570 #, kde-format msgid "&Counter:" msgstr "" #: rc.cpp:721 rc.cpp:1576 #, kde-format msgid "ident &Template:" msgstr "" #: rc.cpp:727 rc.cpp:1582 #, kde-format msgid "&Select a number cycle and edit the details on the right:" msgstr "" #: rc.cpp:730 rc.cpp:1585 #, kde-format msgid "New Item" msgstr "" #: rc.cpp:736 rc.cpp:1591 #, kde-format msgid "add" msgstr "" #: rc.cpp:742 rc.cpp:1597 #, kde-format msgid "remove" msgstr "" #: rc.cpp:745 rc.cpp:1600 #, kde-format msgid "1." msgstr "" #: rc.cpp:748 rc.cpp:751 rc.cpp:1603 rc.cpp:1606 #, kde-format msgid "D" msgstr "" #: rc.cpp:758 rc.cpp:1613 #, kde-format msgid "of the sum of" msgstr "" #: rc.cpp:761 rc.cpp:1616 #, kde-format msgid "" "Please enter the SQLite Database Settings.\n" "\n" "Pick a filename to name the SQLite database file or leave the default " "setting." msgstr "" #: rc.cpp:766 rc.cpp:1621 #, kde-format msgid "store the database file at default place." msgstr "" #: rc.cpp:769 rc.cpp:1624 #, kde-format msgid "select a file name:" msgstr "" #: rc.cpp:772 rc.cpp:1627 #, kde-format msgid "

        Add a Tax Rate

        " msgstr "" #: rc.cpp:775 rc.cpp:1630 #, kde-format msgid "Start-Date:" msgstr "" #: rc.cpp:778 rc.cpp:1633 #, kde-format msgid "&Reduced Tax Rate:" msgstr "" #: rc.cpp:781 rc.cpp:1636 #, kde-format msgid "&Full Tax Rate:" msgstr "" #: rc.cpp:784 rc.cpp:1639 #, kde-format msgid "Edit Document Text Template" msgstr "" #: rc.cpp:787 rc.cpp:1642 #, kde-format msgid "&Name:" msgstr "" #: rc.cpp:790 rc.cpp:1645 #, kde-format msgid "displayed as" msgstr "" #: rc.cpp:793 rc.cpp:1648 #, kde-format msgid "in doc type" msgstr "" #: rc.cpp:796 rc.cpp:1651 #, kde-format msgid "&Text:" msgstr "" #: rc.cpp:799 rc.cpp:1520 #, kde-format msgid "Calculation Item Time" msgstr "" #: rc.cpp:802 rc.cpp:1523 #, kde-format msgid "

        Calculation Part 'Time'

        " msgstr "" #: rc.cpp:805 rc.cpp:1526 #, kde-format msgid "" "Calculate time efforts here for one unit of the template.
        Note that the " "costs may depend on a global hourly rate." msgstr "" #: rc.cpp:811 rc.cpp:1532 #, kde-format msgid "Work" msgstr "" #: rc.cpp:814 rc.cpp:1535 #, kde-format msgid "&Time Effort:" msgstr "" #: rc.cpp:817 rc.cpp:1538 #, kde-format msgid "&Hourly Rate:" msgstr "" #: rc.cpp:820 rc.cpp:1541 #, kde-format msgid "Apply the &global hourly rate" msgstr "" #: rc.cpp:823 rc.cpp:1654 #, kde-format msgid "

        Add a unit

        " msgstr "" #: rc.cpp:826 rc.cpp:1657 #, kde-format msgid "Unit short" msgstr "" #: rc.cpp:829 rc.cpp:1660 #, kde-format msgid "Unit long" msgstr "" #: rc.cpp:832 rc.cpp:1663 #, kde-format msgid "Unit plural short" msgstr "" #: rc.cpp:835 rc.cpp:1666 #, kde-format msgid "Unit plural long" msgstr "" #: rc.cpp:838 rc.cpp:1700 #, kde-format msgid "" "This step checks if the database schema version is sufficient for this " "version of Kraft. \n" "\n" "In case it is not, the schema is updated automatically.\n" msgstr "" #: rc.cpp:853 rc.cpp:1715 #, kde-format msgid "Upgrade not yet started" msgstr "" #: rc.cpp:856 rc.cpp:1718 #, kde-format msgid "

        Add a Wage group

        " msgstr "" #: rc.cpp:859 rc.cpp:1721 #, kde-format msgid "Group name" msgstr "" #: rc.cpp:862 rc.cpp:1724 #, kde-format msgid "Wage" msgstr "" #: models/docbasemodel.cpp:33 #, kde-format msgid "Doc. Number" msgstr "" #: models/docbasemodel.cpp:34 #, kde-format msgid "Doc. Type" msgstr "" #: models/docbasemodel.cpp:36 #, kde-format msgid "Client Id" msgstr "" #: models/docbasemodel.cpp:37 #, kde-format msgid "Last modified" msgstr "" #: models/docbasemodel.cpp:38 #, kde-format msgid "Creation date" msgstr "" #: models/docbasemodel.cpp:40 #, kde-format msgid "Client Address" msgstr "" #: models/docbasemodel.cpp:41 #, kde-format msgid "Client" msgstr "" #: models/docbasemodel.cpp:109 #, kde-format msgid "Looking up address" msgstr "" #: models/docbasemodel.cpp:111 #, kde-format msgid "Lookup started" msgstr "" #: itemtagdialog.cpp:82 #, kde-format msgid "Edit Item Tags" msgstr "" #: itemtagdialog.cpp:89 #, kde-format msgid "Item Tags" msgstr "" #: itemtagdialog.cpp:90 #, kde-format msgid "Select all tags for the item should be tagged with." msgstr "" #: itemtagdialog.cpp:105 tagtemplatesdialog.cpp:150 #, kde-format msgid "Tag" msgstr "" #: itemtagdialog.cpp:106 tagtemplatesdialog.cpp:151 #, kde-format msgid "Color" msgstr "" #: itemtagdialog.cpp:107 tagtemplatesdialog.cpp:152 #, kde-format msgid "Description" msgstr "" #: numbercycledialog.cpp:52 #, kde-format msgid "Edit Number Cycles" msgstr "" #: numbercycledialog.cpp:67 #, kde-format msgid "" "The template may contain the following tags:
        • %y or %yyyy - the year " "of the documents date.
        • %yy - the year of the document (two digits).
        • %w - the week number of the documents date.
        • %ww - the week " "number of the documents date with leading zero.
        • %d - the day number " "of the documents date.
        • %dd - the day number of the documents date " "with leading zero.
        • %m or %M - the month number of the documents date." "
        • %MM - the month number with leading zero.
        • %c - the customer " "id from kaddressbook
        • %i - the unique counter
        • %type - the " "localised doc type (offer, invoice etc.)
        • %uid - the contact id of " "the client.
        %i needs to be part of the template." msgstr "" #: numbercycledialog.cpp:138 #, kde-format msgid "Doc-Type" msgstr "" #: numbercycledialog.cpp:215 #, kde-format msgid "Add Number Cycle" msgstr "" #: numbercycledialog.cpp:216 #, kde-format msgid "Enter the name of a new number cycle." msgstr "" #: numbercycledialog.cpp:274 #, kde-format msgid "The numbercycle %1 is still assigned to a document type." msgstr "" #: numbercycledialog.cpp:275 #, kde-format msgid "" "The number cycle can not be deleted as long as it is assigned to a document " "type." msgstr "" #: numbercycledialog.cpp:334 #, kde-format msgid "Dangerous Counter Change" msgstr "" #: numbercycledialog.cpp:335 #, kde-format msgid "The new counter is lower than the old one. " msgstr "" #: numbercycledialog.cpp:336 #, kde-format msgid "" "That has potential to create duplicate document numbers. Do you really want " "to decrease it?" msgstr "" #: portalview.cpp:65 #, kde-format msgid "About Kraft" msgstr "" #: portalview.cpp:83 #, kde-format msgid "Documents" msgstr "" #: portalview.cpp:89 #, kde-format msgid "Timeline" msgstr "" #: portalview.cpp:96 #, kde-format msgid "Catalogs" msgstr "" #: portalview.cpp:134 #, kde-format msgid "Kraft Document Overview" msgstr "" #: portalview.cpp:141 portalview.cpp:160 #, kde-format msgid "Available Catalogs" msgstr "" #: portalview.cpp:143 #, kde-format msgid "No catalogs available." msgstr "" #: portalview.cpp:192 #, kde-format msgid "Open" msgstr "" #: portalview.cpp:203 #, kde-format msgid "No templates yet." msgstr "" #: portalview.cpp:208 #, kde-format msgid "%1 templates in %2 chapters
        last modified at %3" msgstr "" #: portalview.cpp:215 #, kde-format msgid "XML Export" msgstr "" #: portalview.cpp:270 #, kde-format msgid "Kraft Website" msgstr "" #: portalview.cpp:273 #, kde-format msgctxt "The string is followed by a link to the GPL2 text" msgid "Kraft is free software licensed under the" msgstr "" #: portalview.cpp:274 #, kde-format msgctxt "The string is followed by the link to github" msgid "Kraft is maintained on " msgstr "" #: portalview.cpp:275 #, kde-format msgid "Authors" msgstr "" #: portalview.cpp:276 #, kde-format msgid "Developer and Maintainer" msgstr "" #: portalview.cpp:277 #, kde-format msgid "Developer" msgstr "" #: portalview.cpp:278 #, kde-format msgctxt "The person who provided the logo graphics" msgid "Logo design" msgstr "" #: portalview.cpp:279 #, kde-format msgctxt "The person who provided the user manual" msgid "User Manual" msgstr "" #: portalview.cpp:281 #, kde-format msgid "" "Kraft helps you to handle documents like quotes and invoices in your small " "business." msgstr "" #: portalview.cpp:282 #, kde-format msgid "Welcome to Kraft" msgstr "" #: portalview.cpp:283 #, kde-format msgid "Kraft Version" msgstr "" #: portalview.cpp:285 #, kde-format msgid "Codename" msgstr "" #: portalview.cpp:288 #, kde-format msgid "Country Setting" msgstr "" #: portalview.cpp:291 #, kde-format msgid "Language Setting" msgstr "" #: portalview.cpp:295 #, kde-format msgid "Kraft Initialisation Problem" msgstr "" #: portalview.cpp:296 #, kde-format msgid "" "There is a initialisation error on your system. Kraft will not work that way." msgstr "" #: portalview.cpp:303 #, kde-format msgid "Database Information" msgstr "" #: portalview.cpp:304 #, kde-format msgid "Kraft database name" msgstr "" #: portalview.cpp:309 #, kde-format msgid "Required Version" msgstr "" #: portalview.cpp:312 #, kde-format msgid "Database schema version" msgstr "" #: portalview.cpp:314 #, kde-format msgid "Qt database driver" msgstr "" #: portalview.cpp:318 #, kde-format msgid "established" msgstr "" #: portalview.cpp:318 #, kde-format msgid "NOT AVAILABLE!" msgstr "" #: portalview.cpp:319 #, kde-format msgid "Database connection" msgstr "" #: portalview.cpp:328 #, kde-format msgid "Database Version" msgstr "" #: portalview.cpp:336 #, kde-format msgid "Addressbook Backend" msgstr "" #: portalview.cpp:337 #, kde-format msgid "Backend type" msgstr "" #: portalview.cpp:339 #, kde-format msgid "running" msgstr "" #: portalview.cpp:339 #, kde-format msgid "not running" msgstr "" #: portalview.cpp:343 #, kde-format msgid "External Tools" msgstr "" #: portalview.cpp:344 #, kde-format msgid "RML to PDF conversion tool" msgstr "" #: portalview.cpp:347 #, kde-format msgid "not found!" msgstr "" #: portalview.cpp:350 #, kde-format msgid "iconv tool for text import" msgstr "" #: portalview.cpp:353 #, kde-format msgid "weasyprint for PDF generation" msgstr "" #: portalview.cpp:357 #, kde-format msgid "not available" msgstr "" #: portalview.cpp:362 #, kde-format msgid "Some Icons are made by" msgstr "" #: portalview.cpp:363 #, kde-format msgid "Acknowledgements" msgstr "" #: prefsdialog.cpp:66 #, kde-format msgid "Configure Kraft" msgstr "" #: prefsdialog.cpp:100 #, kde-format msgid "Document Defaults" msgstr "" #: prefsdialog.cpp:101 #, kde-format msgid "Taxes" msgstr "" #: prefsdialog.cpp:102 #, kde-format msgid "Document Types" msgstr "" #: prefsdialog.cpp:104 #, kde-format msgid "Wages" msgstr "" #: prefsdialog.cpp:106 #, kde-format msgid "Units" msgstr "" #: prefsdialog.cpp:107 #, kde-format msgid "Own Identity" msgstr "" #: prefsdialog.cpp:154 #, kde-format msgid "Tax rates beginning at date:" msgstr "" #: prefsdialog.cpp:162 prefsunits.cpp:53 prefswages.cpp:51 #, kde-format msgid "ID" msgstr "" #: prefsdialog.cpp:163 #, kde-format msgid "Full Tax [%]" msgstr "" #: prefsdialog.cpp:164 #, kde-format msgid "Reduced Tax [%]" msgstr "" #: prefsdialog.cpp:165 #, kde-format msgid "Start Date" msgstr "" #: prefsdialog.cpp:205 #, kde-format msgid "" "Select the identity of the sending entity of documents. That's your " "companies address." msgstr "" #: prefsdialog.cpp:221 #, kde-format msgid "Select Identity..." msgstr "" #: prefsdialog.cpp:227 #, kde-format msgid "From AddressBook" msgstr "" #: prefsdialog.cpp:232 setupassistant.cpp:340 #, kde-format msgid "Manual Entry" msgstr "" #: prefsdialog.cpp:244 #, kde-format msgid "Manual Address" msgstr "" #: prefsdialog.cpp:299 #, kde-format msgid "&Default document type on creation:" msgstr "" #: prefsdialog.cpp:304 #, kde-format msgid "New documents default to the selected type." msgstr "" #: prefsdialog.cpp:313 #, kde-format msgid "Default &Tax for Documents:" msgstr "" #: prefsdialog.cpp:318 #, kde-format msgid "The default tax setting for all documents." msgstr "" #: prefsdialog.cpp:330 #, kde-format msgid "Document Date Format:" msgstr "" #: prefsdialog.cpp:336 #, kde-format msgid "The default date format for documents." msgstr "" #: prefsdialog.cpp:338 #, kde-format msgid "ISO-Format: %1" msgstr "" #: prefsdialog.cpp:340 #, kde-format msgid "Short-Date: %1" msgstr "" #: prefsdialog.cpp:342 #, kde-format msgid "Long-Date: %1" msgstr "" #: prefsdialog.cpp:344 #, kde-format msgid "RFC 2822-Format: %1" msgstr "" #: prefsdialog.cpp:346 #, kde-format msgid "\"German Format\": %1" msgstr "" #: prefsdialog.cpp:347 #, kde-format msgid "Custom Setting in Settingsfile" msgstr "" #: prefsdialog.cpp:382 #, kde-format msgid "" "The old default doc type for new documents was just deleted.Please check the " "setting in the Document Defaults in the Kraft preferences Dialog." msgstr "" #: prefsdialog.cpp:385 #, kde-format msgid "Document Default Change" msgstr "" #: prefsdialog.cpp:593 #, kde-format msgid "The identity can not be found." msgstr "" #: prefsdialog.cpp:595 #, kde-format msgid "" "

        Kraft Addressbook Integration down.

        The address book backend " "is not up and running.

        Please check your addressbook integration setup." "

        " msgstr "" #: prefsdialog.cpp:601 #, kde-format msgid "The identity is not listed in an address book." msgstr "" #: prefsdialog.cpp:603 #, kde-format msgid "" "

        Kraft does not know your identity.

        Please pick one from the " "address books by clicking on the Button below.

        Not having an identity " "selected can make your documents look incomplete.

        " msgstr "" #: prefsdialog.cpp:620 #, kde-format msgid "Your identity can be found in the address books." msgstr "" #: prefsdialog.cpp:631 #, kde-format msgid "Work Phone" msgstr "" #: prefsdialog.cpp:632 #, kde-format msgid "Fax" msgstr "" #: prefsdialog.cpp:633 #, kde-format msgid "Cell Phone" msgstr "" #: prefsunits.cpp:54 #, kde-format msgid "Short" msgstr "" #: prefsunits.cpp:55 #, kde-format msgid "Long" msgstr "" #: prefsunits.cpp:56 #, kde-format msgid "Short plural" msgstr "" #: prefsunits.cpp:57 #, kde-format msgid "Long plural" msgstr "" #: prefsunits.cpp:152 #, kde-format msgid "Edit a unit" msgstr "" #: prefsunits.cpp:195 #, kde-format msgid "

        Edit unit

        " msgstr "" #: prefswages.cpp:52 #, kde-format msgid "Code" msgstr "" #: prefswages.cpp:54 #, kde-format msgid "Sortkey" msgstr "" #: prefswages.cpp:80 #, kde-format msgid "Up" msgstr "" #: prefswages.cpp:85 #, kde-format msgid "Down" msgstr "" #: prefswages.cpp:201 #, kde-format msgid "Edit a wage group" msgstr "" #: prefswages.cpp:241 #, kde-format msgid "

        Edit wage group

        " msgstr "" #: reportgenerator.cpp:113 #, kde-format msgid "Document generation process is still running." msgstr "" #: reportgenerator.cpp:167 #, kde-format msgid "Template file is not accessible." msgstr "" #: reportgenerator.cpp:192 #, kde-format msgid "The template conversion failed." msgstr "" #: reportgenerator.cpp:200 #, kde-format msgid "Saving to temporar file failed." msgstr "" #: reportgenerator.cpp:292 #, kde-format msgid "No converter error." msgstr "" #: reportgenerator.cpp:295 #, kde-format msgid "The ReportLab based converter script can not be executed." msgstr "" #: reportgenerator.cpp:298 #, kde-format msgid "An unknown error happened." msgstr "" #: reportgenerator.cpp:301 #, kde-format msgid "The ReportLab python module is not installed." msgstr "" #: reportgenerator.cpp:304 #, kde-format msgid "The source file can not be read." msgstr "" #: reportgenerator.cpp:307 #, kde-format msgid "The target can not be opened to write." msgstr "" #: reportgenerator.cpp:310 #, kde-format msgid "The target file does not exist." msgstr "" #: reportgenerator.cpp:313 #, kde-format msgid "The WeasyPrint tool is not installed." msgstr "" #: reportgenerator.cpp:316 #, kde-format msgid "The PDF merger utility failed." msgstr "" #: reportgenerator.cpp:335 #, kde-format msgid "There is not template defined for %1." msgstr "" #: reportgenerator.cpp:340 #, kde-format msgid "The template file %1 for document type %2 does not exist." msgstr "" #: reportgenerator.cpp:344 #, kde-format msgid "The template file %1 for document type %2 can not be read." msgstr "" #: setupassistant.cpp:36 #, kde-format msgid "Welcome to the Kraft Setup Assistant" msgstr "" #: setupassistant.cpp:55 #, kde-format msgid "Select the Database Backend" msgstr "" #: setupassistant.cpp:92 #, kde-format msgid "Sqlite File Name" msgstr "" #: setupassistant.cpp:146 #, kde-format msgid "MySql Detail Information" msgstr "" #: setupassistant.cpp:188 #, kde-format msgid "Create Database" msgstr "" #: setupassistant.cpp:210 setupassistant.cpp:223 #, kde-format msgid "0/%1" msgstr "" #: setupassistant.cpp:242 setupassistant.cpp:252 setupassistant.cpp:290 #, kde-format msgid "%1/%2" msgstr "" #: setupassistant.cpp:265 #, kde-format msgid "Upgrade the Database" msgstr "" #: setupassistant.cpp:313 #, kde-format msgid "Your Company Address" msgstr "" #: setupassistant.cpp:318 #, kde-format msgid "" "Select your companies address either from the address book or enter it " "manually. It is set as a consigner on the documents." msgstr "" #: setupassistant.cpp:325 #, kde-format msgid "Select from Addressbook" msgstr "" #: setupassistant.cpp:416 #, kde-format msgid "Final Status" msgstr "" #: setupassistant.cpp:510 #, kde-format msgid "" "

        Can't connect to your database. Are you sure your credentials are correct " "and the database exists?

        " msgstr "" #: setupassistant.cpp:521 #, kde-format msgid "

        Can't open your database file, check the permissions and such." msgstr "" #: setupassistant.cpp:530 #, kde-format msgid "" "

        The database is already existing, no action needs to be taken here.

        Please hit next to proceed.

        " msgstr "" #: setupassistant.cpp:579 #, kde-format msgid "

        The database setup was successfully completed.

        " msgstr "" #: setupassistant.cpp:580 #, kde-format msgid "

        You can start to work with Kraft now. Please do not forget to

        " msgstr "" #: setupassistant.cpp:582 #, kde-format msgid "
      • adjust various settings in the Kraft Preferences dialog.
      • " msgstr "" #: setupassistant.cpp:583 #, kde-format msgid "
      • Check the Catalog chapter list.
      • " msgstr "" #: setupassistant.cpp:584 #, kde-format msgid "
      • Make your business and have fun.
      • " msgstr "" #: setupassistant.cpp:586 #, kde-format msgid "" "

        If you press Finish now, the new database configuration is stored " "in Krafts configuration.

        " msgstr "" #: setupassistant.cpp:602 #, kde-format msgid "" "The Database can not be connected. Please check the database credentials." msgstr "" #: setupassistant.cpp:608 #, kde-format msgid "The database core tables do not exist. Please check initial setup." msgstr "" #: setupassistant.cpp:615 #, kde-format msgid "Database is up to date. No upgrade is required." msgstr "" #: setupassistant.cpp:620 #, kde-format msgid "Parse Update Commands..." msgstr "" #: setupassistant.cpp:663 #, kde-format msgid "The Upgrade failed!" msgstr "" #: setupassistant.cpp:665 #, kde-format msgid "The Upgrade succeeded, the current schema version is %1!" msgstr "" #: setupassistant.cpp:678 #, kde-format msgid "" "The Database can not be connected. Please check the database credentials!" msgstr "" #: setupassistant.cpp:684 #, kde-format msgid "Parse Create Commands..." msgstr "" #: setupassistant.cpp:692 #, kde-format msgid "Parse database fillup commands..." msgstr "" #: setupassistant.cpp:705 #, kde-format msgid "Processing database creation commands..." msgstr "" #: setupassistant.cpp:722 #, kde-format msgid "Process database fillup commands..." msgstr "" #: setupassistant.cpp:732 #, kde-format msgid "Successfully finished commands." msgstr "" #: setupassistant.cpp:734 #, kde-format msgid "Failed to perform all commands." msgstr "" #: setupassistant.cpp:778 #, kde-format msgid "" "This assistant guides you through the basic settings of your Kraft " "installation." msgstr "" #: setupassistant.cpp:788 #, kde-format msgid "There was no database configuration found." msgstr "" #: setupassistant.cpp:790 #, kde-format msgid "A valid current database configuration file was found." msgstr "" #: setupassistant.cpp:812 #, kde-format msgid "The database schema version is too low. It will be updated." msgstr "" #: setupassistant.cpp:815 #, kde-format msgid "The current database schema version is too high. Leaving untouched! " msgstr "" #: setupassistant.cpp:822 #, kde-format msgid "" "

        The database can be opened, but does not contain valid content.

        A " "new database can be created automatically from scratch.

        " msgstr "" #: setupassistant.cpp:829 #, kde-format msgid "

        Kraft failed to connect to the configured database.

        " msgstr "" #: setupassistant.cpp:831 #, kde-format msgid "

        Please check the database server setup and restart Kraft to connect." msgstr "" #: setupassistant.cpp:833 #, kde-format msgid "

        Please check the database file." msgstr "" #: setupassistant.cpp:835 #, kde-format msgid "or create a new database by hitting next.

        " msgstr "" #: setupassistant.cpp:843 #, kde-format msgid "

        Please hit next and follow the instructions.

        " msgstr "" #: tagtemplatesdialog.cpp:47 #, kde-format msgid "Edit Tag Template" msgstr "" #: tagtemplatesdialog.cpp:54 #, kde-format msgid "Edit a Tag Template" msgstr "" #: tagtemplatesdialog.cpp:55 #, kde-format msgid "Adjust settings for name, color and description." msgstr "" #: tagtemplatesdialog.cpp:63 #, kde-format msgid "Description:" msgstr "" #: tagtemplatesdialog.cpp:69 #, kde-format msgid "Associated Color:" msgstr "" #: tagtemplatesdialog.cpp:135 tagtemplatesdialog.cpp:140 portal.cpp:165 #, kde-format msgid "Edit Tag Templates" msgstr "" #: tagtemplatesdialog.cpp:141 #, kde-format msgid "Add, edit and remove tag templates for use in the documents." msgstr "" #: tagtemplatesdialog.cpp:165 #, kde-format msgid "Add..." msgstr "" #: tagtemplatesdialog.cpp:167 #, kde-format msgid "Edit..." msgstr "" #: tagtemplatesdialog.cpp:170 #, kde-format msgid "Delete..." msgstr "" #: tagtemplatesdialog.cpp:223 #, kde-format msgid "Do you really want to delete the template?" msgstr "" #: taxeditdialog.cpp:36 #, kde-format msgid "Edit Tax Rates" msgstr "" #: templkataloglistview.cpp:48 #, kde-format msgid "Calc. Type" msgstr "" #: templkataloglistview.cpp:55 #, kde-format msgid "Template Catalog" msgstr "" #: templkatalogview.cpp:101 #, kde-format msgid "" msgstr "" #: templtopositiondialogbase.cpp:51 #, kde-format msgid "the Header of the Document as first item" msgstr "" #: texteditdialog.cpp:42 #, kde-format msgid "Edit Text Templates" msgstr "" #: texteditdialog.cpp:62 #, kde-format msgid "Edit %1 Template" msgstr "" #: textselection.cpp:42 #, kde-format msgid "Template Collection" msgstr "" #: textselection.cpp:100 #, kde-format msgid "Template Actions" msgstr "" #: textselection.cpp:129 #, kde-format msgid "This is the standard text used in new documents." msgstr "" #: textselection.cpp:138 #, kde-format msgid "%1 Templates for %2" msgstr "" #: textselection.cpp:146 #, kde-format msgid "" "There is no %1 template text available for document type %2.
        Click the " "add-button below to create one." msgstr "" #: textselection.cpp:201 #, kde-format msgid "&Use in Document" msgstr "" #: texttemplate.cpp:103 #, kde-format msgid "Failed to open template source" msgstr "" #: portal.cpp:102 #, kde-format msgid "&Quit" msgstr "" #: portal.cpp:107 #, kde-format msgid "&Cut" msgstr "" #: portal.cpp:112 #, kde-format msgid "C&opy" msgstr "" #: portal.cpp:117 #, kde-format msgid "&Paste" msgstr "" #: portal.cpp:122 #, kde-format msgid "&Settings" msgstr "" #: portal.cpp:127 #, kde-format msgid "&Create Document" msgstr "" #: portal.cpp:131 #, kde-format msgid "&Copy Document" msgstr "" #: portal.cpp:135 #, kde-format msgid "Create &Followup Document" msgstr "" #: portal.cpp:140 #, kde-format msgid "Print Document" msgstr "" #: portal.cpp:145 #, kde-format msgid "Show Document" msgstr "" #: portal.cpp:150 #, kde-format msgid "Edit Document" msgstr "" #: portal.cpp:155 #, kde-format msgid "Open Archived Document" msgstr "" #: portal.cpp:160 #, kde-format msgid "Mail Document" msgstr "" #: portal.cpp:170 #, kde-format msgid "Redo Initial Setup..." msgstr "" #: portal.cpp:175 #, kde-format msgid "Kraft Handbook..." msgstr "" #: portal.cpp:179 #, kde-format msgid "About Qt..." msgstr "" #: portal.cpp:183 #, kde-format msgid "About Kraft..." msgstr "" #: portal.cpp:187 #, kde-format msgid "Quits the application" msgstr "" #: portal.cpp:189 #, kde-format msgid "Cuts the selected section and puts it to the clipboard" msgstr "" #: portal.cpp:190 #, kde-format msgid "Copies the selected section to the clipboard" msgstr "" #: portal.cpp:191 #, kde-format msgid "Pastes the clipboard contents to current position" msgstr "" #: portal.cpp:193 #, kde-format msgid "Creates a new Document" msgstr "" #: portal.cpp:194 #, kde-format msgid "Print and archive this Document" msgstr "" #: portal.cpp:195 #, kde-format msgid "Creates a new document which is a copy of the selected document" msgstr "" #: portal.cpp:196 #, kde-format msgid "Create a followup document for the current document" msgstr "" #: portal.cpp:197 #, kde-format msgid "Opens the document for editing" msgstr "" #: portal.cpp:198 #, kde-format msgid "Opens a read only view on the document." msgstr "" #: portal.cpp:199 #, kde-format msgid "Send document per mail" msgstr "" #: portal.cpp:200 #, kde-format msgid "" "Edit the available tag templates which can be assigned to document items." msgstr "" #: portal.cpp:201 #, kde-format msgid "Configure the Database Kraft is working on." msgstr "" #: portal.cpp:202 #, kde-format msgid "Open a viewer on an archived document" msgstr "" #: portal.cpp:219 #, kde-format msgid "&Edit" msgstr "" #: portal.cpp:238 #, kde-format msgid "Kraft" msgstr "" #: portal.cpp:241 #, kde-format msgid "&Preferences" msgstr "" #: portal.cpp:245 #, kde-format msgid "Toolbars" msgstr "" #: portal.cpp:251 #, kde-format msgid "&Help" msgstr "" #: portal.cpp:336 #, kde-format msgid "Database not running" msgstr "" #: portal.cpp:337 #, kde-format msgid "" "Kraft was started in readonly mode, but the configured database can not be " "connected.\n" "\n" "Kraft will abort." msgstr "" #: portal.cpp:353 #, kde-format msgid "" "Kraft can not connect to the specified MySQL server. Please check the Kraft " "database settings, check if the server is running and verify if a database " "with the name %1 exits!" msgstr "" #: portal.cpp:357 #, kde-format msgid "" "The database with the name %1 does not exist on the database server. Please " "make sure the database exists and is accessible by the user running Kraft." msgstr "" #: portal.cpp:361 #, kde-format msgid "" "The Qt database driver could not be loaded. That probably means, that they " "are not installed. Please make sure the Qt database packages are installed " "and try again." msgstr "" #: portal.cpp:365 #, kde-format msgid "There is a database problem: %1" msgstr "" #: portal.cpp:382 #, kde-format msgid "Database Problem." msgstr "" #: portal.cpp:389 #, kde-format msgid "Check commandline actions" msgstr "" #: portal.cpp:481 #, kde-format msgid "Welcome to Kraft, %1" msgstr "" #: portal.cpp:510 #, kde-format msgid "Creating new document..." msgstr "" #: portal.cpp:536 #, kde-format msgctxt "" "Dialog title of the followup doc dialog, followed by the id of the source " "doc" msgid "Create follow up document for %1" msgstr "" #: portal.cpp:581 #, kde-format msgctxt "Title of the new doc dialog, %1 is the source doc id" msgid "Create new Document as Copy of %1" msgstr "" #: portal.cpp:616 #, kde-format msgid "Opening document to view..." msgstr "" #: portal.cpp:646 #, kde-format msgid "Generating PDF..." msgstr "" #: portal.cpp:668 #, kde-format msgid "Generating PDF for EMail" msgstr "" #: portal.cpp:688 #, kde-format msgid "Doc Generation Error" msgstr "" #: portal.cpp:745 #, kde-format msgid "Printing archived document..." msgstr "" #: portal.cpp:797 #, kde-format msgid "Opening document %1" msgstr "" #: portal.cpp:973 #, kde-format msgid "Cutting selection..." msgstr "" #: portal.cpp:980 #, kde-format msgid "Copying selection to clipboard..." msgstr "" #: portal.cpp:987 #, kde-format msgid "Inserting clipboard contents..." msgstr "" #: portal.cpp:1000 #, kde-format msgid "" "Ready. Kraft is running in read only mode. Document editing is prohibited." msgstr "" #: addressselectorwidget.cpp:229 #, kde-format msgid "Name" msgstr "" #: addressselectorwidget.cpp:231 #, kde-format msgid "Address" msgstr "" #: addressselectorwidget.cpp:331 #, kde-format msgid "Edit Contact..." msgstr "" #: addressselectorwidget.cpp:332 #, kde-format msgid "Edit the currently selected contact" msgstr "" #: addressselectorwidget.cpp:335 #, kde-format msgid "New Contact..." msgstr "" #: addressselectorwidget.cpp:336 #, kde-format msgid "Create a new Contact" msgstr "" #: timecalcpart.cpp:82 #, kde-format msgid "Minutes" msgstr "" #: timecalcpart.cpp:84 #, kde-format msgid "Hours" msgstr "" #: timecalcpart.cpp:86 #, kde-format msgid "Seconds" msgstr "" #: texttemplateinterface.cpp:53 #, kde-format msgid "No file name given for template" msgstr "" #: texttemplateinterface.cpp:61 #, kde-format msgid "Could not find template file %1" msgstr "" kraft-0.97/po/nl/000077500000000000000000000000001410616450300136075ustar00rootroot00000000000000kraft-0.97/po/nl/kraft.po000066400000000000000000002723521410616450300152710ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # # Translators: # Ronald Stroethoff , 2021 # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-06-14 21:37+0200\n" "PO-Revision-Date: 2018-10-31 21:56+0000\n" "Last-Translator: Ronald Stroethoff , 2021\n" "Language-Team: Dutch (Netherlands) (https://www.transifex.com/not-applicable-6/teams/93132/nl_NL/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: nl_NL\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: addeditchapterdialog.cpp:35 #, kde-format msgid "Add/Edit Catalog Chapter" msgstr "Map in catalogus toevoegen/bewerken" #: addeditchapterdialog.cpp:41 #, kde-format msgid "Create a new Catalog Chapter" msgstr "Nieuwe map in de catalogus aanmaken" #: addeditchapterdialog.cpp:45 #, kde-format msgid "Chapter Name:" msgstr "Map-naam:" #: addeditchapterdialog.cpp:49 #, kde-format msgid "Chapter Description:" msgstr "Omschrijving van map:" #: addeditchapterdialog.cpp:76 #, kde-format msgid "Create new Catalog Chapter below chapter %1" msgstr "Een nieuwe catalogus-map in map %1 aanmaken" #: addeditchapterdialog.cpp:83 #, kde-format msgid "Edit name and description of chapter %1" msgstr "Naam en beschrijving van map %1 bewerken" #: alldocsview.cpp:64 #, kde-format msgid "All documents" msgstr "Alle documenten" #: alldocsview.cpp:65 #, kde-format msgid "Documents of last week" msgstr "Documenten van laatste week" #: alldocsview.cpp:66 #, kde-format msgid "Documents of last month" msgstr "Documenten van laatste maand" #: alldocsview.cpp:72 #, kde-format msgid "&Show: " msgstr "&Tonen" #: alldocsview.cpp:79 #, kde-format msgid "&Search: " msgstr "&Zoeken" #: alldocsview.cpp:134 rc.cpp:634 rc.cpp:1328 portal.cpp:289 portal.cpp:290 #, kde-format msgid "Document Actions" msgstr "Document acties" #: archdoc.cpp:70 #, kde-format msgid "%1 for %2 (Id %3)" msgstr "%1 voor %2 (ID %3)" #: catalogselection.cpp:50 #, kde-format msgid "Selected &Catalog: " msgstr "Geselecteerde &Catalogus: " #: catalogselection.cpp:159 #, kde-format msgid "Append to Document" msgstr "Achter document voegen" #: catalogtemplate.cpp:52 #, kde-format msgid "Manual Price" msgstr "Handmatige prijs" #: catalogtemplate.cpp:54 #, kde-format msgid "Calculated" msgstr "Berekend" #: catalogtemplate.cpp:56 #, kde-format msgid "AutoCalc" msgstr "AutoCalc" #: catalogtemplate.cpp:57 #, kde-format msgid "Err: Unknown type %d" msgstr "Fout: onbekend type %d" #: docassistant.cpp:52 #, kde-format msgid "Show &Templates" msgstr "Document tonen" #: docassistant.cpp:57 #, kde-format msgid "Show mask to create or select templates to be used in the document" msgstr "" "Filter tonen voor het creëren of selecteren van in dit document gebruikte \n" "sjablonen" #: docassistant.cpp:121 #, kde-format msgid "Add a template to the document" msgstr "Voeg een sjabloon toe aan het document" #: docassistant.cpp:127 #, kde-format msgid "Create a new template" msgstr "Nieuw sjabloon aanmaken" #: docassistant.cpp:134 #, kde-format msgid "Edit the current template" msgstr "Dit sjabloon bewerken" #: docassistant.cpp:141 #, kde-format msgid "Delete the current template" msgstr "Dit sjabloon verwijderen" #: docassistant.cpp:305 #, kde-format msgid "" "Do you really want to delete the template permanently?\n" "It can not be recovered." msgstr "" "Wilt u het sjabloon permanent verwijderen? Er is geen methode om het " "sjabloon terug te halen!" #: docdigestdetailview.cpp:224 rc.cpp:78 rc.cpp:120 rc.cpp:988 rc.cpp:1030 #, kde-format msgid "Amount" msgstr "Aantal" #: docdigestdetailview.cpp:225 #, kde-format msgid "Type" msgstr "Type" #: docdigestdetailview.cpp:226 #, kde-format msgid "Sum" msgstr "Totaal" #: docdigestdetailview.cpp:255 #, kde-format msgid "Results in %1 %2" msgstr "Resultaten in %1 %2" #: docdigestdetailview.cpp:256 docdigestdetailview.cpp:289 #, kde-format msgid "Year" msgstr "Jaar" #: docdigestdetailview.cpp:258 #, kde-format msgid "Month" msgstr "Maand" #: docdigestdetailview.cpp:291 #, kde-format msgid "Results in Year %1" msgstr "Resultaten in jaar %1" #: docdigestdetailview.cpp:310 docdigestdetailview.cpp:319 #, kde-format msgid "Customer" msgstr "Klant" #: docdigestdetailview.cpp:314 #, kde-format msgid "not set" msgstr "niet ingesteld" #: docdigestdetailview.cpp:330 #, kde-format msgid "The address is not listed in an address book." msgstr "Het adres komt niet voor in de adresboeken." #: docdigestdetailview.cpp:332 #, kde-format msgid "" "The client has the address book id %1 but can not found in our address " "books." msgstr "" "De klant heeft het adresboek-ID %1 maar kan niet gevonden worden in onze " "adresboeken." #: docdigestdetailview.cpp:335 #, kde-format msgid "The client can be found in our address books." msgstr "De klant is gevonden in onze adresboeken." #: docdigestdetailview.cpp:346 prefsdialog.cpp:636 #, kde-format msgid "preferred address" msgstr "voorkeur-adres" #: docdigestdetailview.cpp:350 prefsdialog.cpp:640 #, kde-format msgid "home address" msgstr "Privé-adres" #: docdigestdetailview.cpp:354 prefsdialog.cpp:644 #, kde-format msgid "work address" msgstr "Werk-adres" #: docdigestdetailview.cpp:358 prefsdialog.cpp:648 #, kde-format msgid "postal address" msgstr "Post-adres" #: docdigestdetailview.cpp:362 prefsdialog.cpp:652 #, kde-format msgid "international address" msgstr "internationaal adres" #: docdigestdetailview.cpp:366 prefsdialog.cpp:656 #, kde-format msgid "domestic address" msgstr "adres in eigen land" #: docdigestdetailview.cpp:370 prefsdialog.cpp:660 #, kde-format msgid "unknown" msgstr "onbekend" #: docdigestdetailview.cpp:407 models/docbasemodel.cpp:32 #, kde-format msgid "Date" msgstr "Datum" #: docdigestdetailview.cpp:412 models/docbasemodel.cpp:35 #, kde-format msgid "Whiteboard" msgstr "Whiteboard" #: docdigestdetailview.cpp:417 models/docbasemodel.cpp:39 #, kde-format msgid "Project" msgstr "Project" #: docdigestdetailview.cpp:427 #, kde-format msgid "This document was never printed." msgstr "Dit document is nog nooit afgedrukt." #: docdigestdetailview.cpp:434 #, kde-format msgid "Last printed" msgstr "Laatst afgedrukt" #: docdigestdetailview.cpp:435 #, kde-format msgid "Opens last created PDF document" msgstr "Opent het laatst gemaakte PDF document" #: docdigestdetailview.cpp:436 #, kde-format msgid "open" msgstr "Openen" #: docdigestdetailview.cpp:440 #, kde-format msgid "One older print" msgstr "Een oudere afdruk" #: docdigestdetailview.cpp:442 #, kde-format msgid "%1 older prints" msgstr "%1 oudere afdrukken" #: docdigestdetailview.cpp:446 #, kde-format msgid "Archived documents can not be found. Check PDF Output dir." msgstr "" "De documenten zijn niet in het archief te vinden. Controleer de PDF uitvoer " "map." #: docpostcard.cpp:34 #, kde-format msgid "Document Overview" msgstr "Overzicht documenten" #: docpostcard.cpp:122 #, kde-format msgid "Netto:" msgstr "Netto:" #: docpostcard.cpp:133 docpostcard.cpp:140 #, kde-format msgid "+ %1% Tax:" msgstr "+ %1% BTW:" #: docpostcard.cpp:146 #, kde-format msgid "Sum Tax:" msgstr "Totaal belasting:" #: docpostcard.cpp:151 #, kde-format msgid "Total:" msgstr "Totaal:" #: docpostcard.cpp:243 #, kde-format msgid "%1 Items" msgstr "%1 Items" #: docpostcard.cpp:245 #, kde-format msgid "%1 Items, netto %2" msgstr "%1 Items, netto %2" #: doctext.cpp:61 #, kde-format msgid "Standard" msgstr "Standaard" #: doctext.cpp:76 #, kde-format msgid "Header Text" msgstr "Tekst in briefhoofd" #: doctext.cpp:77 #, kde-format msgid "Footer Text" msgstr "Voettekst" #: doctext.cpp:78 #, kde-format msgid "Items" msgstr "Items" #: doctext.cpp:80 defaultprovider.cpp:55 positionviewwidget.cpp:413 #, kde-format msgid "Unknown" msgstr "Onbekend" #: doctypeedit.cpp:48 #, kde-format msgid "" msgstr "" #: doctypeedit.cpp:49 #, kde-format msgid "
        " msgstr "
        " #: doctypeedit.cpp:132 doctypeedit.cpp:158 #, kde-format msgid "Add Document Type" msgstr "Documenttype toevoegen" #: doctypeedit.cpp:133 #, kde-format msgid "Enter the name of a new document type" msgstr "Voer de naam in van een nieuw documenttype" #: doctypeedit.cpp:159 #, kde-format msgid "Edit the name of a document type" msgstr "Bewerk de naam van een documenttype" #: documentman.cpp:93 #, kde-format msgctxt "" "Text to be inserted into a doc, if the sum of another doc needs to be " "substracted ie. in a final invoice if there was a partial invoice before%1 " "is substited by the doc type, %2 by the id of the predecessor doc." msgid "Substract sum from %1 %2" msgstr "Bedrag te verminderen op %1 %2" #: documenttemplate.cpp:112 #, kde-format msgctxt "Sequence number printed on the document" msgid "No." msgstr "Nee" #: documenttemplate.cpp:113 #, kde-format msgctxt "Document item printed on the document" msgid "Item" msgstr "Item" #: documenttemplate.cpp:114 #, kde-format msgctxt "Abbrev. of Quantity printed on the document" msgid "Qty." msgstr "Aantal" #: documenttemplate.cpp:115 #, kde-format msgctxt "Unit printed on the document" msgid "Unit" msgstr "Eenheid" #: documenttemplate.cpp:116 #, kde-format msgctxt "Price of an item printed on the document" msgid "Price" msgstr "Prijs" #: documenttemplate.cpp:117 #, kde-format msgctxt "Printed on the document" msgid "Sum" msgstr "Totaal" #: documenttemplate.cpp:118 #, kde-format msgctxt "printed on the document" msgid "Net" msgstr "Netto" #: documenttemplate.cpp:119 #, kde-format msgctxt "Printed on the document" msgid "VAT" msgstr "BTW" #: documenttemplate.cpp:121 #, kde-format msgctxt "Printed on the document" msgid "Phone" msgstr "Tel." #: documenttemplate.cpp:122 #, kde-format msgctxt "Printed on the document" msgid "FAX" msgstr "FAX" #: documenttemplate.cpp:123 #, kde-format msgctxt "Printed on the document" msgid "Mobile" msgstr "GSM" #: documenttemplate.cpp:124 #, kde-format msgctxt "Printed on the document" msgid "Email" msgstr "Email" #: documenttemplate.cpp:125 #, kde-format msgctxt "Printed on the document" msgid "Website" msgstr "Website" #: documenttemplate.cpp:127 #, kde-format msgctxt "Printed on the document" msgid "Page" msgstr "Pagina" #: documenttemplate.cpp:128 #, kde-format msgctxt "the 'of' in page X of Y" msgid "of" msgstr "van" #: documenttemplate.cpp:129 #, kde-format msgctxt "Document number on document" msgid "Document No." msgstr "Document Nr." #: documenttemplate.cpp:130 #, kde-format msgctxt "Date on document" msgid "Date" msgstr "Datum" #: documenttemplate.cpp:131 #, kde-format msgctxt "Project label" msgid "Project" msgstr "Project" #: documenttemplate.cpp:132 #, kde-format msgctxt "Customer ID on document" msgid "Customer Id" msgstr "Klant nummer" #: documenttemplate.cpp:267 #, kde-format msgid "" "Please note: This offer contains %1 alternative or demand positions, printed" " in italic font. These do not add to the overall sum." msgstr "" "Opmerking: in deze offerte komen %1 alternatieven of meerwerk-posten voor, " "deze zijn cursief gedrukt. Deze zijn niet bij het totaal bedrag opgeteld." #: documenttemplate.cpp:278 #, kde-format msgid "tax free items (%1 pcs.)" msgstr "belastingvrije items (%1 stuks.)" #: documenttemplate.cpp:284 #, kde-format msgid "items with reduced tax of %1% (%2 pcs.)" msgstr "items met het lage BTW van %1% (%2 stuks.)" #: documenttemplate.cpp:293 #, kde-format msgid "No label: items with full tax of %1% (%2 pcs.)" msgstr "Geen label: items zonder het hoge BTW van %1% (%2 stuks.)" #: documenttemplate.cpp:323 kraftview_ro.cpp:240 #, kde-format msgid "reduced VAT" msgstr "Hoge BTW" #: documenttemplate.cpp:331 kraftview_ro.cpp:212 kraftview_ro.cpp:249 #: rc.cpp:21 rc.cpp:931 #, kde-format msgid "VAT" msgstr "BTW" #: documenttemplate.cpp:363 #, kde-format msgid "Template to convert is not existing!" msgstr "Het te converteren sjabloon bestaat niet!" #: documenttemplate.cpp:366 #, kde-format msgid "Can not read template file!" msgstr "Kan het sjabloon niet openen!" #: calcpart.cpp:98 #, kde-format msgid "Base" msgstr "Stam" #: filterheader.cpp:40 addressselectorwidget.cpp:294 #, kde-format msgid "&Search:" msgstr "&Zoeken:" #: fixcalcdialog.cpp:36 #, kde-format msgid "Calculation Fix Item" msgstr "Vaste kosten berekenen" #: flostempldialog.cpp:71 #, kde-format msgid "Create or Edit Template Items" msgstr "Wijzig of maak sjablonen aan" #: flostempldialog.cpp:213 flostempldialog.cpp:604 #, kde-format msgid "No" msgstr "Nee" #: flostempldialog.cpp:214 flostempldialog.cpp:604 #, kde-format msgid "Yes" msgstr "Ja" #: flostempldialog.cpp:251 #, kde-format msgid "Calculated Price: " msgstr "Berekende prijs: " #: flostempldialog.cpp:255 #, kde-format msgid "Manual Price: " msgstr "Handmatige prijs:" #: flostempldialog.cpp:260 #, kde-format msgid "(+%1%)" msgstr "(+%1%)" #: flostempldialog.cpp:264 #, kde-format msgid "%1%" msgstr "%1%" #: flostempldialog.cpp:266 #, kde-format msgid ": " msgstr ": " #: flostempldialog.cpp:388 #, kde-format msgid "Template Error" msgstr "Fout in sjabloon" #: flostempldialog.cpp:388 #, kde-format msgid "Saving of this template failed, sorry" msgstr "Opslaan van dit sjabloon is mislukt." #: flostempldialog.cpp:416 #, kde-format msgid "The template has been modified." msgstr "Sjabloon is gewijzigd" #: flostempldialog.cpp:417 #, kde-format msgid "Do you want to discard your changes?" msgstr "Wilt u echt uw wijzigingen annuleren?" #: flostempldialog.cpp:446 #, kde-format msgid "" "The catalog chapter was changed for this template.\n" "Do you really want to move the template to the new chapter?" msgstr "" "Voor deze sjabloon is de map in de catalogus gewijzigd.\n" "Wilt u de sjabloon naar de nieuwe map verplaatsen?" #: flostempldialog.cpp:448 #, kde-format msgid "Chapter Change" msgstr "Gewijzigde map" #: flostempldialog.h:104 #, kde-format msgid "Calculated material" msgstr "Berekende materialen" #: katalog.cpp:137 #, kde-format msgid "not found" msgstr "Niet gevonden" #: main.cpp:53 #, kde-format msgid "Open document with arch doc number " msgstr "Open document met arch doc nummer " #: main.cpp:54 #, kde-format msgid "Open Kraft in read only mode - document changes prohibited" msgstr "" "Open Kraft in alleen lezen modus - document wijzigen is niet toegestaan." #: importfilter.cpp:50 #, kde-format msgid "Unable to find filter called %1" msgstr "Kon filter genaamd %1 niet vinden" #: importfilter.cpp:58 #, kde-format msgid "Could not open the definition file!" msgstr "Kon het definitiebestand niet openen!" #: importfilter.cpp:143 #, kde-format msgid "Unknown tags: " msgstr "Onbekende tags: " #: importfilter.cpp:184 #, kde-format msgid "Could not recode input file!" msgstr "Kon invoerbestand niet opnieuw indelen!" #: importfilter.cpp:192 #, kde-format msgid "Unable to open temp file " msgstr "Kon tijdelijk bestand niet openen " #: importfilter.cpp:198 #, kde-format msgid "Could not open the import source file!" msgstr "Kon het importbestand niet openen!" #: importitemdialog.cpp:47 #, kde-format msgid "Import Items From File" msgstr "Items vanuit bestand importeren" #: importitemdialog.cpp:116 #, kde-format msgid "the Header of the Document" msgstr "het briefhoofd van het document" #: importitemdialog.cpp:125 templtopositiondialogbase.cpp:58 #, kde-format msgid "..." msgstr "..." #: inserttempldialog.cpp:105 #, kde-format msgid "Create a new Item" msgstr "Nieuw item aanmaken" #: inserttempldialog.cpp:107 #, kde-format msgid "Create a new Item from Template" msgstr "Van sjabloon nieuw item aanmaken" #: kataloglistview.cpp:350 #, kde-format msgid "A catalog chapter can not be deleted as long it has children." msgstr "Kan map niet uit catalogus verwijderen zolang het niet leeg is." #: kataloglistview.cpp:351 #, kde-format msgid "Chapter can not be deleted" msgstr "Kan map niet verwijderen" #: katalogview.cpp:157 #, kde-format msgid "Edit Sub chapter" msgstr "Submap wijzigen" #: katalogview.cpp:159 #, kde-format msgid "Edit a catalog sub chapter" msgstr "Submap in catalogus wijzigen" #: katalogview.cpp:163 #, kde-format msgid "Add a sub chapter" msgstr "Submap toevoegen" #: katalogview.cpp:165 #, kde-format msgid "Add a sub chapter below the selected one" msgstr "In deze map een submap toevoegen" #: katalogview.cpp:169 katalogview.cpp:171 #, kde-format msgid "Remove a sub chapter" msgstr "Submap verwijderen" #: katalogview.cpp:175 rc.cpp:3 rc.cpp:913 #, kde-format msgid "Edit Template" msgstr "Sjabloon bewerken" #: katalogview.cpp:177 #, kde-format msgid "Opens the editor window for templates to edit the selected one" msgstr "Opent het bewerkingsvenster om het geselecteerde sjabloon te bewerken" #: katalogview.cpp:182 #, kde-format msgid "New template" msgstr "Nieuw sjabloon" #: katalogview.cpp:184 #, kde-format msgid "Opens the editor window for templates to enter a new template" msgstr "" "Opent het bewerkingsvenster voor sjablonen om een nieuw sjabloon in te " "voeren" #: katalogview.cpp:189 #, kde-format msgid "Delete template" msgstr "Sjabloon verwijderen" #: katalogview.cpp:191 #, kde-format msgid "Deletes the template" msgstr "Verwijdert het sjabloon " #: katalogview.cpp:196 #, kde-format msgid "Export catalog" msgstr "Catalogus exporteren" #: katalogview.cpp:198 #, kde-format msgid "Export the whole catalog as XML encoded file" msgstr "De hele catalogus als XML-gecodeerd bestand exporteren" #: katalogview.cpp:203 #, kde-format msgid "Import catalog" msgstr "Catalogus importeren" #: katalogview.cpp:205 #, kde-format msgid "Import a catalog from a XML file" msgstr "Catalogus uit een XML-bestand importeren" #: katalogview.cpp:209 rc.cpp:487 rc.cpp:1358 #, kde-format msgid "&Catalog" msgstr "&Catalogus" #: katalogview.cpp:227 #, kde-format msgid "Opening file..." msgstr "Bestand wordt geopend..." #: katalogview.cpp:229 katalogview.cpp:295 katalogview.cpp:309 #: katalogview.cpp:318 katalogview.cpp:327 portal.cpp:659 portal.cpp:683 #: portal.cpp:1002 #, kde-format msgid "Ready." msgstr "Gereed." #: katalogview.cpp:234 portal.cpp:946 #, kde-format msgid "Exiting..." msgstr "Bezig met afsluiten..." #: katalogview.cpp:291 #, kde-format msgid "Exporting file..." msgstr "Exporteren van bestand..." #: katalogview.cpp:300 #, kde-format msgid "Importfile... (not yet implemented)" msgstr "Importbestand...(nog niet geïmplementeerd)" #: katalogview.cpp:305 #, kde-format msgid "Creating a new sub chapter..." msgstr "Bezig met het aanmaken van een submap..." #: katalogview.cpp:314 #, kde-format msgid "Editing a sub chapter..." msgstr "Bezig met het bewerken van een submap..." #: katalogview.cpp:323 #, kde-format msgid "Removing a sub chapter..." msgstr "Bezig met het verwijderen van een submap..." #: katalogview.cpp:355 #, kde-format msgid "Created at:%1" msgstr "Aangemaakt op:%1" #: katalogview.cpp:358 #, kde-format msgid "  Last used:%1" msgstr "  Laatste keer gebruikt:%1" #: katalogview.cpp:365 #, kde-format msgid "Modified at:%1" msgstr "Gewijzigd op:%1" #: katalogview.cpp:368 #, kde-format msgid "  Use Count:%1" msgstr "%1   keer toegepast:" #: kraftdoc.cpp:159 #, kde-format msgctxt "First argument is the doctype, like Invoice, followed by the ID" msgid "%1 (Id %2)" msgstr "%1 (Id %2)" #: kraftdoc.cpp:307 #, kde-format msgctxt "Document part header" msgid "Header" msgstr "Briefhoofd" #: kraftdoc.cpp:309 #, kde-format msgctxt "Document part footer" msgid "Footer" msgstr "Voettekst" #: kraftdoc.cpp:311 #, kde-format msgctxt "Document part containing the items" msgid "Items" msgstr "Items" #: kraftdoc.cpp:313 #, kde-format msgid "Unknown document part" msgstr "Onbekend documentgedeelte" #: positionviewwidget.cpp:92 #, kde-format msgid "Item Actions" msgstr "Item Acties" #: positionviewwidget.cpp:95 #, kde-format msgid "Item Kind" msgstr "Soort item:" #: positionviewwidget.cpp:96 positionviewwidget.cpp:671 #, kde-format msgid "Normal" msgstr "Normaal" #: positionviewwidget.cpp:98 positionviewwidget.cpp:679 #, kde-format msgid "Alternative" msgstr "Alternatief" #: positionviewwidget.cpp:100 #, kde-format msgid "On Demand" msgstr "Op verzoek" #: positionviewwidget.cpp:105 rc.cpp:265 rc.cpp:874 #, kde-format msgid "Tax" msgstr "Belasting" #: positionviewwidget.cpp:108 #, kde-format msgid "Taxfree Item" msgstr "Belastingvrije item" #: positionviewwidget.cpp:114 #, kde-format msgid "Reduced Tax" msgstr "Laag tarief BTW" #: positionviewwidget.cpp:120 #, kde-format msgid "Full Tax" msgstr "Hoog tarief BTW" #: positionviewwidget.cpp:129 #, kde-format msgid "Move Up" msgstr "Omhoog verplaatsen" #: positionviewwidget.cpp:131 #, kde-format msgid "Move Down" msgstr "Omlaag verplaatsen" #: positionviewwidget.cpp:133 #, kde-format msgid "Lock Item" msgstr "Item vastzetten" #: positionviewwidget.cpp:135 #, kde-format msgid "Unlock Item" msgstr "Item ontgrendelen" #: positionviewwidget.cpp:137 #, kde-format msgid "Delete Item" msgstr "Verwijder item" #: positionviewwidget.cpp:221 #, kde-format msgid "All items" msgstr "Alle items" #: positionviewwidget.cpp:231 #, kde-format msgid "%1-tagged items" msgstr "%1-gemarkeerde items" #: positionviewwidget.cpp:263 #, kde-format msgid "Tag: %1" msgstr "Tag: %1" #: positionviewwidget.cpp:265 #, kde-format msgid "Tags:
          " msgstr "Tags:
            " #: positionviewwidget.cpp:276 #, kde-format msgid "No tags assigned yet." msgstr "Nog geen tags toegewezen." #: positionviewwidget.cpp:405 #, kde-format msgid "Active" msgstr "Actief" #: positionviewwidget.cpp:407 #, kde-format msgid "New" msgstr "Nieuw" #: positionviewwidget.cpp:409 #, kde-format msgid "Deleted" msgstr "Verwijdert" #: positionviewwidget.cpp:411 #, kde-format msgid "Locked" msgstr "Vastgezet" #: positionviewwidget.cpp:618 #, kde-format msgid "" "This is an alternative item.

            Use the position toolbox to change " "the item type." msgstr "" "Dit item is een alternatief.

            Bij de knoppen voor positie is een \n" "knop waar u het item-type kunt wijzigen." #: positionviewwidget.cpp:634 #, kde-format msgid "" "This item is either completely optional or its amount varies depending on " "the needs.

            Use the item toolbox to change the item type." msgstr "" "Dit item is compleet optioneel of het aantal is afhankelijk van het \n" "gebruik.

            Bij de knoppen voor positie is een knop waar u het type \n" "item kunt wijzigen." #: positionviewwidget.cpp:675 #, kde-format msgid "Demand" msgstr "Op verzoek" #: kraftdocfooteredit.cpp:52 #, kde-format msgid "Document Footer" msgstr "Voettekst van document" #: kraftdocheaderedit.cpp:56 #, kde-format msgid "Document Header" msgstr "Briefhoofd van document" #: kraftdocheaderedit.cpp:64 #, kde-format msgid "Manually set in address field." msgstr "Handmatig het adresveld instellen." #: kraftdocpositionsedit.cpp:94 #, kde-format msgid "Add Item..." msgstr "Item toevoegen..." #: kraftdocpositionsedit.cpp:96 #, kde-format msgid "Add a normal item to the document manually." msgstr "Een normaal item handmatig aan het document toevoegen." #: kraftdocpositionsedit.cpp:100 #, kde-format msgid "Add Discount Item" msgstr "Korting geven" #: kraftdocpositionsedit.cpp:103 #, kde-format msgid "" "Adds an item to the document that allows discounts on other items in the " "document" msgstr "" "Voegt een item aan het document toe dat kortingen op andere items in het " "document toestaat" #: kraftdocpositionsedit.cpp:106 #, kde-format msgid "Import Items..." msgstr "Items importeren..." #: kraftdocpositionsedit.cpp:109 #, kde-format msgid "Opens a dialog where multiple items can be imported from a text file." msgstr "" "Opent een dialoog waar meerdere items geïmporteerd kunnen worden uit een " "tekstbestand." #: kraftdocpositionsedit.cpp:118 #, kde-format msgid "Document Items" msgstr "Document-items" #: kraftview.cpp:87 kraftview_ro.cpp:75 #, kde-format msgid "Document" msgstr "Document" #: kraftview.cpp:261 #, kde-format msgid "Successor of %1" msgstr "Vervolg van %1" #: kraftview.cpp:398 kraftview.cpp:843 #, kde-format msgid "" "The address label is not empty and different from the selected one.
            Do " "you really want to replace it with the text shown below?
            %1
            " msgstr "" "Het adreslabel is niet leeg en verschilt van de geselecteerde.
            Wilt u \n" "het vervangen door de onderstaande tekst?
            %1
            " #: kraftview.cpp:445 #, kde-format msgid "" "

            The Document Items List is still empty, but Items can be added " "now.

            To add items to the document either
            • Press the 'Add item' " "button above.
            • Open the template catalog by clicking on the 'show " "Template' button on the right and pick one of the available " "templates.
            " msgstr "" "

            De lijst met items in het documenten is nog leeg, maar u kunt vanaf nu items toevoegen.

            om items aan het document toe te voegen, kunt u naar keuze
            • op de \n" "knop \"toevoegen\" erboven drukken.
            • het dialoog met de catalogus met \n" "sjablonen openen door op de 'Sjabloon weergeven' knop rechts te klikken en een van de \n" "beschikbare sjablonen te selecteren.
            " #: kraftview.cpp:630 prefsdialog.cpp:319 #, kde-format msgid "Display no tax at all" msgstr "Helemaal geen belasting tonen" #: kraftview.cpp:631 prefsdialog.cpp:320 #, kde-format msgid "Calculate reduced tax for all items" msgstr "Bereken lage BTW voor alle items" #: kraftview.cpp:632 prefsdialog.cpp:321 #, kde-format msgid "Calculate full tax for all items" msgstr "Bereken hoge BTW voor alle items" #: kraftview.cpp:633 #, kde-format msgid "Calculate individual tax for each item" msgstr "Bereken voor elk item de belasting apart" #: kraftview.cpp:690 #, kde-format msgid "Tax Settings Overwrite" msgstr "belastinginstellingen overschrijven" #: kraftview.cpp:691 #, kde-format msgid "Really overwrite all individual tax settings of the items?" msgstr "" "Wilt u alle individuele belastinginstellingen van de items overschrijven? " #: kraftview.cpp:842 #, kde-format msgid "Address Overwrite" msgstr "Overschrijven van adres" #: kraftview.cpp:1099 #, kde-format msgid "Discount" msgstr "Korting" #: kraftview.cpp:1419 #, kde-format msgid "The document has been modified." msgstr "Het document is gewijzigd." #: kraftview.cpp:1420 #, kde-format msgid "Do you want to save your changes?" msgstr "Wilt u alle wijzigingen verwerpen?" #: kraftview_ro.cpp:213 #, kde-format msgid "Reduced TAX" msgstr "Lage BTW" #: newdocassistant.cpp:52 newdocassistant.cpp:96 #, kde-format msgid "New Document Settings" msgstr "Instellingen voor nieuwe documenten" #: newdocassistant.cpp:55 #, kde-format msgid "" "Please select a customer as addressee for the document. If there is no entry" " for the customer in the addressbook yet, it can be opened by clicking on " "the button below." msgstr "" "Selecteer een klant als geadresseerde voor het document. Als er voor de " "klant nog geen item in het adresboek is, dan kunt u deze openen door op de " "knop eronder te klikken." #: newdocassistant.cpp:100 #, kde-format msgid "" "Select a document type and a date. A comment on the whiteboard helps to " "classify the document." msgstr "" "Selecteer een documenttype en een datum. Een omschrijving op het whiteboard " "helpt bij het classificeren van het document." #: newdocassistant.cpp:107 #, kde-format msgid "Customer: Not yet selected!" msgstr "Klant: niet geselecteerd!" #: newdocassistant.cpp:116 #, kde-format msgid "Document &Type:" msgstr "Document &type:" #: newdocassistant.cpp:120 #, kde-format msgid "Document Date: " msgstr "Datum document:" #: newdocassistant.cpp:123 #, kde-format msgid "Whiteboard Content:" msgstr "Inhoud Whiteboard:" #: newdocassistant.cpp:127 #, kde-format msgid "Copy document items from predecessor document" msgstr "Kopieer items uit voorgaande document" #: newdocassistant.cpp:175 #, kde-format msgid "Create a new Kraft Document" msgstr "Nieuw Kraft document aanmaken" #: newdocassistant.cpp:265 #, kde-format msgid "Followup Document for %1" msgstr "Opvolg document voor %1" #: materialkataloglistview.cpp:39 rc.cpp:108 rc.cpp:643 rc.cpp:1018 #: rc.cpp:1675 #, kde-format msgid "Material" msgstr "Materiaal" #: materialkataloglistview.cpp:40 #, kde-format msgid "Pack" msgstr "Verpakt per" #: materialkataloglistview.cpp:41 rc.cpp:123 rc.cpp:1033 #, kde-format msgid "Unit" msgstr "Eenheid" #: materialkataloglistview.cpp:42 #, kde-format msgid "Purchase" msgstr "Inkoop" #: materialkataloglistview.cpp:43 #, kde-format msgid "Sale" msgstr "Verkoop" #: materialkataloglistview.cpp:44 #, kde-format msgid "Last Modified" msgstr "Laatst gewijzigd" #: materialkataloglistview.cpp:50 #, kde-format msgid "Material Catalog" msgstr "Materiaalcatalogus" #: materialkatalogview.cpp:106 #, kde-format msgid "" msgstr "" #: materialkatalogview.cpp:136 templkatalogview.cpp:139 #, kde-format msgid "Do you really want to delete the template from the catalog?" msgstr "Wilt u het sjabloon uit de catalogus verwijderen?" #: materialselectdialog.cpp:39 #, kde-format msgid "Add Material to Calculation" msgstr "Materiaal toevoegen aan de berekening" #: materialselectdialog.cpp:44 #, kde-format msgid "

            Add Material to Calculation

            " msgstr "

            Materiaal toevoegen aan de berekening

            " #: rc.cpp:6 rc.cpp:916 templkataloglistview.cpp:46 texteditdialog.cpp:76 #, kde-format msgid "Template" msgstr "Sjabloon" #: rc.cpp:9 rc.cpp:919 #, kde-format msgid "Text:" msgstr "Tekst:" #: rc.cpp:12 rc.cpp:922 #, kde-format msgid "&Store in Chapter" msgstr "Opge&slagen in map" #: rc.cpp:15 rc.cpp:925 #, kde-format msgid "&Unit" msgstr "&Eenheid" #: rc.cpp:18 rc.cpp:928 #, kde-format msgid "&Count Time for Overalltime" msgstr "Meetellen voor totaaltijd" #: rc.cpp:24 rc.cpp:934 #, kde-format msgid "full" msgstr "Hoog" #: rc.cpp:27 rc.cpp:937 #, kde-format msgid "half" msgstr "Laag" #: rc.cpp:30 rc.cpp:940 #, kde-format msgid "Time Calculation" msgstr "Post arbeid" #: rc.cpp:33 rc.cpp:72 rc.cpp:111 rc.cpp:943 rc.cpp:982 rc.cpp:1021 #, kde-format msgid "text" msgstr "tekst" #: rc.cpp:36 rc.cpp:946 #, kde-format msgid "Time measureable effort for this template:" msgstr "De voor dit sjabloon nodige tijd:" #: rc.cpp:39 rc.cpp:81 rc.cpp:117 rc.cpp:949 rc.cpp:991 rc.cpp:1027 #, kde-format msgid "Label" msgstr "Label" #: rc.cpp:42 rc.cpp:952 #, kde-format msgid "Duration" msgstr "Tijdsduur" #: rc.cpp:45 rc.cpp:955 #, kde-format msgid "Hourly Rate" msgstr "Uurtarief" #: rc.cpp:48 rc.cpp:958 #, kde-format msgid "Glob. Rate" msgstr "Project-tarief" #: rc.cpp:51 rc.cpp:961 #, kde-format msgid "Adds a new time calculation part to the template" msgstr "Voeg nieuwe post tijd aan het sjabloon toe" #: rc.cpp:54 rc.cpp:93 rc.cpp:132 rc.cpp:964 rc.cpp:1003 rc.cpp:1042 #, kde-format msgid "new..." msgstr "Nieuw..." #: rc.cpp:57 rc.cpp:967 #, kde-format msgid "Edits the current time calculation part" msgstr "Bewerk de geselecteerde post tijd" #: rc.cpp:60 rc.cpp:99 rc.cpp:138 rc.cpp:970 rc.cpp:1009 rc.cpp:1048 #, kde-format msgid "edit..." msgstr "bewerken..." #: rc.cpp:63 rc.cpp:973 #, kde-format msgid "Deletes the current time calculation part" msgstr "Verwijder de geselecteerde post tijd" #: rc.cpp:66 rc.cpp:105 rc.cpp:144 rc.cpp:976 rc.cpp:1015 rc.cpp:1054 #, kde-format msgid "delete" msgstr "verwijderen" #: rc.cpp:69 rc.cpp:979 #, kde-format msgid "Fix Costs" msgstr "Vaste kosten" #: rc.cpp:75 rc.cpp:985 #, kde-format msgid "Fix costs for this template per one unit:" msgstr "Vaste kosten in dit sjabloon per eenheid:" #: rc.cpp:84 rc.cpp:994 #, kde-format msgid "Single Price" msgstr "Stuksprijs" #: rc.cpp:87 rc.cpp:997 #, kde-format msgid "Overall Price" msgstr "Totale prijs" #: rc.cpp:90 rc.cpp:1000 #, kde-format msgid "adds a new fix calculation part" msgstr "Voeg nieuwe post vaste kosten toe" #: rc.cpp:96 rc.cpp:1006 #, kde-format msgid "edits the current fix calculation part" msgstr "Bewerk de geselecteerde post vaste kosten" #: rc.cpp:102 rc.cpp:1012 #, kde-format msgid "deletes the current fix calculation part" msgstr "Verwijder de geselecteerde post vaste kosten" #: rc.cpp:114 rc.cpp:1024 #, kde-format msgid "Needed materials for one unit of this template:" msgstr "Benodigde materiaal voor een exemplaar van dit sjabloon:" #: rc.cpp:126 rc.cpp:1036 prefswages.cpp:53 templkataloglistview.cpp:47 #, kde-format msgid "Price" msgstr "Prijs" #: rc.cpp:129 rc.cpp:1039 #, kde-format msgid "adds a new material calculation part" msgstr "Voeg nieuwe post materiaal toe" #: rc.cpp:135 rc.cpp:1045 #, kde-format msgid "edits the current material part" msgstr "Bewerk de geselecteerde post materiaal" #: rc.cpp:141 rc.cpp:1051 #, kde-format msgid "deletes the current material calculation part" msgstr "Verwijdert de geselecteerde post materiaal" #: rc.cpp:147 rc.cpp:1057 #, kde-format msgid "Overall Price per Unit" msgstr "Totale prijs per eenheid" #: rc.cpp:150 rc.cpp:1060 #, kde-format msgid "&Manual Price" msgstr "Hand&matige prijs" #: rc.cpp:153 rc.cpp:1063 #, kde-format msgid "Calculated Price" msgstr "Berekende prijs" #: rc.cpp:156 rc.cpp:1066 #, kde-format msgid "Fix Costs Part:" msgstr "Post vaste kosten:" #: rc.cpp:159 rc.cpp:1069 #, kde-format msgid "Material Part:" msgstr "Post materiaal:" #: rc.cpp:162 rc.cpp:1072 #, kde-format msgid "Profit:" msgstr "Winst:" #: rc.cpp:166 rc.cpp:1076 #, no-c-format, kde-format msgid " %" msgstr " %" #: rc.cpp:169 rc.cpp:1079 #, kde-format msgid "Time Calculation Part:" msgstr "Post arbeid:" #: rc.cpp:172 rc.cpp:1082 #, kde-format msgid "Calculated Price:" msgstr "Berekende prijs: " #: rc.cpp:175 rc.cpp:1085 #, kde-format msgid "88.888,88 €" msgstr "88.888,88 €" #: rc.cpp:178 rc.cpp:1088 #, kde-format msgid "Database creation and initial schema setup:" msgstr "Database aanmaken en de initiële aanmaak opnieuw uitvoeren." #: rc.cpp:181 rc.cpp:190 rc.cpp:844 rc.cpp:1091 rc.cpp:1100 rc.cpp:1706 #, kde-format msgid "0 / 129" msgstr "0 / 129" #: rc.cpp:184 rc.cpp:193 rc.cpp:847 rc.cpp:1094 rc.cpp:1103 rc.cpp:1709 #, kde-format msgid "X" msgstr "X" #: rc.cpp:187 rc.cpp:1097 #, kde-format msgid "Filling the database with initial values:" msgstr "Bezig met het vullen van de database met beginwaarden:" #: rc.cpp:196 rc.cpp:850 rc.cpp:1106 rc.cpp:1712 #, kde-format msgid "Status: " msgstr "Status: " #: rc.cpp:199 rc.cpp:1109 #, kde-format msgid "Database setup status..." msgstr "Status van het instellen van de database..." #: rc.cpp:202 rc.cpp:1112 #, kde-format msgid "Bruns Data File:" msgstr "Bruns gegevensbestand:" #: rc.cpp:205 rc.cpp:1115 #, kde-format msgid "Bruns Key File:" msgstr "Bruns indexbestand:" #: rc.cpp:208 rc.cpp:1118 #, kde-format msgid "Qt Database Driver:" msgstr "Qt Database Driver:" #: rc.cpp:211 rc.cpp:1121 #, kde-format msgid "Database Server:" msgstr "Database Server:" #: rc.cpp:214 rc.cpp:1124 #, kde-format msgid "Server Port:" msgstr "Server Port:" #: rc.cpp:217 rc.cpp:1127 #, kde-format msgid "Database Name" msgstr "Database naam" #: rc.cpp:220 rc.cpp:700 rc.cpp:1130 rc.cpp:1555 #, kde-format msgid "Database User:" msgstr "Database gebruiker:" #: rc.cpp:223 rc.cpp:1133 #, kde-format msgid "Database Password:" msgstr "Database wachtwoord:" #: rc.cpp:226 rc.cpp:1136 #, kde-format msgid "The default database name when creating new databases" msgstr "De standaard database-naam bij de aanmaak van een nieuwe database" #: rc.cpp:229 rc.cpp:1139 #, kde-format msgid "File Name" msgstr "Bestandsnaam" #: rc.cpp:232 rc.cpp:1142 #, kde-format msgid "The path where database file are stored. Leave empty!" msgstr "De locatie waar database-bestanden zijn opgeslagen. Laat dit leeg!" #: rc.cpp:235 rc.cpp:1145 #, kde-format msgid "Database Update:" msgstr "Bijwerken database:" #: rc.cpp:238 rc.cpp:1148 #, kde-format msgid "Overall Progress:" msgstr "Totale voortgang:" #: rc.cpp:241 rc.cpp:1151 #, kde-format msgid "Detail Progress:" msgstr "Detail voortgang:" #: rc.cpp:244 rc.cpp:1154 #, kde-format msgid "Status:" msgstr "Status: " #: rc.cpp:247 rc.cpp:1289 #, kde-format msgid "" "

            Kraft uses a database backend to store values. By " "default it uses a file based database with easy setup targeted to single " "user mode.


            " msgstr "" "

            Kraft gebruikt als backend een database om waarden in " "op te slaan. Standaard gebruikt het een makkelijk op te zetten database " "bedoeld voor gebruik door een enkele " "gebruiker.


            " #: rc.cpp:250 rc.cpp:1292 #, kde-format msgid "SQLite 3 - file based database (default)" msgstr "SQLite 3 - bestand gebaseerde database (standaard)" #: rc.cpp:253 rc.cpp:1295 #, kde-format msgid "MySQL Serverbased Database for advanced Setups" msgstr "MySQL Servergebaseerde Database voor een geavanceerde opzet" #: rc.cpp:256 rc.cpp:865 #, kde-format msgid "Footer Texts" msgstr "Voettekst" #: rc.cpp:259 rc.cpp:868 #, kde-format msgid "&Summary Text on Last Page:" msgstr "Samenvatting op laatste pagina:" #: rc.cpp:262 rc.cpp:871 #, kde-format msgid "&Greeting:" msgstr "Aanhef" #: rc.cpp:268 rc.cpp:877 #, kde-format msgid "Document &Tax:" msgstr "Belas&ting per document:" #: rc.cpp:271 rc.cpp:880 #, kde-format msgid "TextLabel" msgstr "Tekstlabel" #: rc.cpp:274 rc.cpp:883 #, kde-format msgid "&Project:" msgstr "&Project:" #: rc.cpp:277 rc.cpp:886 #, kde-format msgid "&Whiteboard:" msgstr "&Whiteboard:" #: rc.cpp:280 rc.cpp:889 #, kde-format msgid "" "Enter a label that describes the project. This may appear on the customer " "document." msgstr "" "Geef een label op dat het project beschrijft. Dit kan ook op het document " "voor de klant verschijnen." #: rc.cpp:283 rc.cpp:892 #, kde-format msgid "Postal &Address:" msgstr "Post &Adres:" #: rc.cpp:286 rc.cpp:895 #, kde-format msgid "not selected" msgstr "niet ingesteld" #: rc.cpp:289 rc.cpp:898 #, kde-format msgid "Select an addressee from the address books." msgstr "Selecteer een adres in het adresboek." #: rc.cpp:292 rc.cpp:901 #, kde-format msgid "select..." msgstr "Selecteren..." #: rc.cpp:295 rc.cpp:904 #, kde-format msgid "&Salutatory Address:" msgstr "Aanhef:" #: rc.cpp:298 rc.cpp:907 #, kde-format msgid "Customer:" msgstr "Klant:" #: rc.cpp:301 rc.cpp:910 #, kde-format msgid "&Entry Text on First Page:" msgstr "Geeft tekst op voor eerste pagina:" #: rc.cpp:304 rc.cpp:733 rc.cpp:1157 rc.cpp:1588 #, kde-format msgid "Click to add a new document type to the list." msgstr "Klik om een nieuw documenttype aan de lijst toe te voegen." #: rc.cpp:307 rc.cpp:1160 prefsdialog.cpp:184 prefsunits.cpp:79 #: prefswages.cpp:90 #, kde-format msgid "Add" msgstr "Toevoegen" #: rc.cpp:310 rc.cpp:1163 #, kde-format msgid "click to edit the selected document type name" msgstr "Klik om de naam van het geselecteerde documenttype te bewerken." #: rc.cpp:313 rc.cpp:1166 prefsunits.cpp:83 prefswages.cpp:94 #, kde-format msgid "Edit" msgstr "Bewerken" #: rc.cpp:316 rc.cpp:739 rc.cpp:1169 rc.cpp:1594 #, kde-format msgid "click to remove the current document type" msgstr "Klik om het geselecteerde documenttype te verwijderen." #: rc.cpp:319 rc.cpp:1172 portalview.cpp:220 prefsdialog.cpp:188 #: prefsunits.cpp:88 prefswages.cpp:99 #, kde-format msgid "Remove" msgstr "Verwijderen" #: rc.cpp:322 rc.cpp:1175 #, kde-format msgid "Unique Document Number" msgstr "Uniek documentnummer" #: rc.cpp:325 rc.cpp:1178 #, kde-format msgid "Number &Cycle:" msgstr "Volg&nummer:" #: rc.cpp:328 rc.cpp:337 rc.cpp:340 rc.cpp:724 rc.cpp:1181 rc.cpp:1190 #: rc.cpp:1193 rc.cpp:1579 #, kde-format msgid "example" msgstr "voorbeeld" #: rc.cpp:331 rc.cpp:1184 #, kde-format msgid "ident Template:" msgstr "ident Template:" #: rc.cpp:334 rc.cpp:718 rc.cpp:1187 rc.cpp:1573 #, kde-format msgid "Example Id:" msgstr "Voorbeeld volgnummer:" #: rc.cpp:343 rc.cpp:1196 #, kde-format msgid "Counter:" msgstr "Teller:" #: rc.cpp:346 rc.cpp:1199 #, kde-format msgid "&Edit Number Cycles..." msgstr "Volgnummersysteem inst&ellen..." #: rc.cpp:349 rc.cpp:1202 #, kde-format msgid "Template for PDF Creation" msgstr "Sjabloon voor aanmaak PDF" #: rc.cpp:352 rc.cpp:1205 #, kde-format msgid "&Template File" msgstr "Sjabloon-bes&tand" #: rc.cpp:355 rc.cpp:1208 #, kde-format msgid "W&atermark:" msgstr "W&atermerk:" #: rc.cpp:358 rc.cpp:1211 #, kde-format msgid "no watermark" msgstr "geen watermerk" #: rc.cpp:361 rc.cpp:1214 #, kde-format msgid "on first page" msgstr "op eerste pagina" #: rc.cpp:364 rc.cpp:1217 #, kde-format msgid "on all pages" msgstr "op alle pagina´s" #: rc.cpp:367 rc.cpp:1220 #, kde-format msgid "&Watermark File" msgstr "&Watermerk bestand" #: rc.cpp:370 rc.cpp:1223 #, kde-format msgid "Calculation Parts Fix" msgstr "Post vaste kosten" #: rc.cpp:373 rc.cpp:1226 #, kde-format msgid "

            Fix Cost Parts

            " msgstr "

            Post vaste kosten

            " #: rc.cpp:376 rc.cpp:1229 #, kde-format msgid "Add a fix cost for one unit of the template:" msgstr "Voert een post vaste kosten toe aan dit sjabloon:" #: rc.cpp:379 rc.cpp:808 rc.cpp:1232 rc.cpp:1529 #, kde-format msgid "&Label:" msgstr "&Label:" #: rc.cpp:382 rc.cpp:1235 #, kde-format msgid "describing text" msgstr "Omschrijving" #: rc.cpp:385 rc.cpp:1238 #, kde-format msgid "amortisation" msgstr "korting" #: rc.cpp:388 rc.cpp:680 rc.cpp:1241 rc.cpp:1511 #, kde-format msgid "&Amount:" msgstr "&Aantal:" #: rc.cpp:391 rc.cpp:1244 #, kde-format msgid "amount multiplier" msgstr "amount multiplier" #: rc.cpp:394 rc.cpp:1247 #, kde-format msgid "at &Price:" msgstr "voor de prijs van:" #: rc.cpp:397 rc.cpp:683 rc.cpp:1250 rc.cpp:1514 #, kde-format msgid "Price for one piece" msgstr "Prijs per stuk" #: rc.cpp:400 rc.cpp:1253 #, kde-format msgid "€" msgstr "€" #: rc.cpp:403 rc.cpp:1256 #, kde-format msgid "Form" msgstr "Formulier" #: rc.cpp:406 rc.cpp:1259 tagtemplatesdialog.cpp:58 #, kde-format msgid "Name:" msgstr "Naam:" #: rc.cpp:409 rc.cpp:1262 #, kde-format msgid "Organization:" msgstr "Organisatie:" #: rc.cpp:412 rc.cpp:1265 #, kde-format msgid "Street:" msgstr "Straat:" #: rc.cpp:415 rc.cpp:1268 #, kde-format msgid "Post Code:" msgstr "Postcode:" #: rc.cpp:418 rc.cpp:1271 #, kde-format msgid "City:" msgstr "Stad:" #: rc.cpp:421 rc.cpp:1274 #, kde-format msgid "Phone:" msgstr "Telefoon:" #: rc.cpp:424 rc.cpp:1277 #, kde-format msgid "Fax:" msgstr "Fax:" #: rc.cpp:427 rc.cpp:1280 #, kde-format msgid "Mobile Phone:" msgstr "Mobiele telefoon:" #: rc.cpp:430 rc.cpp:1283 #, kde-format msgid "EMail:" msgstr "EMail:" #: rc.cpp:433 rc.cpp:1286 #, kde-format msgid "Website:" msgstr "Website:" #: rc.cpp:436 rc.cpp:1298 #, kde-format msgid "Import Document Items" msgstr "Document-items importeren" #: rc.cpp:439 rc.cpp:1301 #, kde-format msgid "Import information" msgstr "Informatie importeren" #: rc.cpp:442 rc.cpp:1304 #, kde-format msgid "Select a &File to import from:" msgstr "Selecteer het bestand wat u wilt importeren:" #: rc.cpp:445 rc.cpp:1307 #, kde-format msgid "FixMe!" msgstr "FixMe!" #: rc.cpp:448 rc.cpp:1310 #, kde-format msgid "Import &Schema:" msgstr "&Schema importeren" #: rc.cpp:451 rc.cpp:1313 #, kde-format msgid "this is interesting information about the selected schema" msgstr "Dit is interessante informatie over het gekozen schema" #: rc.cpp:454 rc.cpp:1316 #, kde-format msgid "Insert all Items &after" msgstr "Alle items invoegen n&a" #: rc.cpp:457 rc.cpp:481 rc.cpp:1319 rc.cpp:1352 #, kde-format msgid "Tags" msgstr "Tags" #: rc.cpp:460 rc.cpp:1331 templtopositiondialogbase.cpp:35 #, kde-format msgid "Create Item from Template" msgstr "Item van sjabloon creëren" #: rc.cpp:463 rc.cpp:1334 #, kde-format msgid "New Item Text" msgstr "Nieuw item-tekst" #: rc.cpp:466 rc.cpp:1337 #, kde-format msgid "&insert" msgstr "&Invoegen" #: rc.cpp:469 rc.cpp:1340 #, kde-format msgid "à" msgstr "a" #: rc.cpp:472 rc.cpp:1343 #, kde-format msgid "&after item" msgstr "N&a item" #: rc.cpp:475 rc.cpp:1346 #, kde-format msgid "Keep this item as template for future documents" msgstr "Dit item als sjabloon bewaren voor latere documenten" #: rc.cpp:478 rc.cpp:1349 #, kde-format msgid "save in &chapter" msgstr "Ops&laan in map" #: rc.cpp:484 rc.cpp:1355 portal.cpp:215 #, kde-format msgid "&File" msgstr "Bestand" #: rc.cpp:490 rc.cpp:1361 #, kde-format msgid "Do Database Initialisation" msgstr "Database opnieuw opstarten" #: rc.cpp:493 rc.cpp:1364 #, kde-format msgid "Do XML archiving of documents they're printed?" msgstr "Documenten bij het afdrukken als XML archiveren?" #: rc.cpp:496 rc.cpp:1367 #, kde-format msgid "" "Where Kraft stores the XML archive documents. If empty, KDEHOME/share/apps " "is used." msgstr "" "Waar Kraft de XML archieven opslaat. Als dit leeg is, dan wordt \n" "KDEHOME/share/apps gebruikt." #: rc.cpp:499 rc.cpp:1370 #, kde-format msgid "The local xml document storage path" msgstr "De locatie voor de lokaal opgeslagen XML documenten" #: rc.cpp:502 rc.cpp:1373 #, kde-format msgid "Default mail user agent. Set xdg for xdg-email" msgstr "" "Standaard email programma. Stel in op xdg voor het programma xdg-email." #: rc.cpp:505 rc.cpp:508 rc.cpp:1376 rc.cpp:1379 #, kde-format msgid "The default geometry of the document view dialog" msgstr "" "De standaard grootte van het dialoogvenster voor het weergeven van het " "document." #: rc.cpp:511 rc.cpp:1382 #, kde-format msgid "The current state of the portal" msgstr "De huidige status van de portal" #: rc.cpp:514 rc.cpp:1385 #, kde-format msgid "The current geometry of the portal" msgstr "De huidige grootte van de portal" #: rc.cpp:517 rc.cpp:1388 #, kde-format msgid "The current geometry of the new doc assistant" msgstr "De huidige grootte van de nieuwe document assistent" #: rc.cpp:520 rc.cpp:1391 #, kde-format msgid "The splitter position of the document view dialog" msgstr "De schuifpijl-positie van de document weergave" #: rc.cpp:523 rc.cpp:1394 #, kde-format msgid "The default size of the material catalog view" msgstr "" "De standaard grootte van het dialoogvenster voor de weergave van de \n" "materiaalcatalogus." #: rc.cpp:526 rc.cpp:1397 #, kde-format msgid "The splitter setting for the doc assistant" msgstr "De schuifpijl-instelling voor de document assistent" #: rc.cpp:529 rc.cpp:1400 #, kde-format msgid "The window size of the template to document dialog for plants" msgstr "" "De venstergrootte van het sjabloon voor het documentendialoog van de planten" #: rc.cpp:532 rc.cpp:1403 #, kde-format msgid "The window size of the template to document dialog" msgstr "De venstergrootte van het sjabloon voor het documentendialoog" #: rc.cpp:535 rc.cpp:1406 #, kde-format msgid "The digest list column arrangement for the Latest-list" msgstr "The digest list column arrangement for the Latest-list" #: rc.cpp:538 rc.cpp:1409 #, kde-format msgid "The digest list column arrangement for the all-list" msgstr "The digest list column arrangement for the all-list" #: rc.cpp:541 rc.cpp:1412 #, kde-format msgid "The digest list column arrangement for timeline" msgstr "The digest list column arrangement for timeline" #: rc.cpp:544 rc.cpp:1415 #, kde-format msgid "The sizes of the slider in the address picker Widget" msgstr "The sizes of the slider in the address picker Widget" #: rc.cpp:547 rc.cpp:1418 #, kde-format msgid "The state of the treeview inside the address selector widget" msgstr "The state of the treeview inside the address selector widget" #: rc.cpp:550 rc.cpp:1421 #, kde-format msgid "Size of the address select dialog" msgstr "Grootte van adres zoekvenster" #: rc.cpp:553 rc.cpp:1424 #, kde-format msgid "State of the window of the material catalog" msgstr "State of the window of the material catalog" #: rc.cpp:556 rc.cpp:1427 #, kde-format msgid "Geometry of the material catalog" msgstr "Opbouw van de materiaal catalogus" #: rc.cpp:559 rc.cpp:1430 #, kde-format msgid "State of the header of the material catalog" msgstr "State of the header of the material catalog" #: rc.cpp:562 rc.cpp:1433 #, kde-format msgid "State of the window of the template catalog" msgstr "State of the window of the template catalog" #: rc.cpp:565 rc.cpp:1436 #, kde-format msgid "Geometry the template catalog" msgstr "Geometry the template catalog" #: rc.cpp:568 rc.cpp:1439 #, kde-format msgid "State of the header of the template catalog" msgstr "State of the header of the template catalog" #: rc.cpp:571 rc.cpp:1442 #, kde-format msgid "" "Default percentage the sale price for a material should be higher than its " "purchase price" msgstr "" "Standaard zou voor materiaal de verkoopprijs hoger moeten zijn dan de " "inkoopprijs" #: rc.cpp:574 rc.cpp:1445 #, kde-format msgid "The name of the last selected chapter in the catalog" msgstr "The name of the last selected chapter in the catalog" #: rc.cpp:577 rc.cpp:1448 #, kde-format msgid "The complete filename of the trml2pdf binary" msgstr "Het complete bestandsnaam van het trml2pdf-programma" #: rc.cpp:580 rc.cpp:1451 #, kde-format msgid "The path to the output directory for document pdfs" msgstr "De locatie voor de document-pdfs" #: rc.cpp:583 rc.cpp:1454 #, kde-format msgid "The last created doc type." msgstr "Laatst aangemaakte documenttype." #: rc.cpp:586 rc.cpp:1457 #, kde-format msgid "The date format for print." msgstr "De datum-instelling voor uitgeprinte documenten." #: rc.cpp:589 rc.cpp:1460 #, kde-format msgid "The greeting below on the document footer." msgstr "The greeting below on the document footer." #: rc.cpp:592 rc.cpp:1463 #, kde-format msgid "The salut message on the document header." msgstr "De aanhef in het briefhoofd." #: rc.cpp:595 rc.cpp:1466 #, kde-format msgid "" "The name of the catalog chapter where to store new templates in by default" msgstr "" "De naam van de catalogusmap waar standaard nieuwe sjablonen worden " "opgeslagen" #: rc.cpp:598 rc.cpp:1469 #, kde-format msgid "The name of the last used import schema for items." msgstr "De naam van de laatst gebruikte schema voor het importeren van items." #: rc.cpp:601 rc.cpp:1472 #, kde-format msgid "The name of the last used input file for items." msgstr "The name of the last used input file for items." #: rc.cpp:604 rc.cpp:1475 #, kde-format msgid "" "User name as reference to the KAddressbook to identify 'my' address " "(DEPRECATED)." msgstr "" "Gebruikersnaam als referentie naar het KAddressbook om \"mijn\" adres te \n" "identificeren (Verouderd)." #: rc.cpp:607 rc.cpp:1478 #, kde-format msgid "" "UID of the user as reference to the KAddressbook to identify 'my' address." msgstr "" "UID of the user as reference to the KAddressbook to identify 'my' address." #: rc.cpp:610 rc.cpp:1481 #, kde-format msgid "The doc id template" msgstr "The doc id template" #: rc.cpp:613 rc.cpp:1484 #, kde-format msgid "Localization on document level" msgstr "Per document taal instellen" #: rc.cpp:616 rc.cpp:1487 #, kde-format msgid "The tax default for new documents." msgstr "De standaard belastingtarief voor nieuwe documenten." #: rc.cpp:619 rc.cpp:1490 #, kde-format msgid "The label for alternative positions" msgstr "Het label voor alternatieve voorstellen" #: rc.cpp:622 rc.cpp:625 rc.cpp:1493 rc.cpp:1496 #, kde-format msgid "The label for demand positions" msgstr "Het label voor meerwerk-posten" #: rc.cpp:628 rc.cpp:1322 portal.cpp:224 #, kde-format msgid "&Document" msgstr "&Document" #: rc.cpp:631 rc.cpp:1325 #, kde-format msgid "Settings" msgstr "Instellingen" #: rc.cpp:637 rc.cpp:1669 #, kde-format msgid "Edit Material" msgstr "Materiaal bewerken" #: rc.cpp:640 rc.cpp:1672 #, kde-format msgid "Store in C&hapter" msgstr "Op&slaan in map" #: rc.cpp:646 rc.cpp:1678 #, kde-format msgid "Pac&kaged:" msgstr "Verpa&kt:" #: rc.cpp:649 rc.cpp:1681 #, kde-format msgid "per P&ackage" msgstr "per pak" #: rc.cpp:652 rc.cpp:1684 #, kde-format msgid "Prices" msgstr "Prijzen" #: rc.cpp:655 rc.cpp:1687 #, kde-format msgid "= Price of &Sale:" msgstr "=Verkoopprij&s:" #: rc.cpp:658 rc.cpp:1690 #, kde-format msgid "pl&us" msgstr "pl&us" #: rc.cpp:662 rc.cpp:755 rc.cpp:1610 rc.cpp:1694 #, no-c-format, kde-format msgid "%" msgstr "%" #: rc.cpp:665 rc.cpp:1697 #, kde-format msgid "&Purchase Price:" msgstr "Inkoop&prijs:" #: rc.cpp:668 rc.cpp:1499 #, kde-format msgid "Calculation Item Material" msgstr "Materiaal toevoegen aan de berekening" #: rc.cpp:671 rc.cpp:1502 #, kde-format msgid "

            Calculation Part 'Material'

            " msgstr "

            Post materiaal

            " #: rc.cpp:674 rc.cpp:1505 #, kde-format msgid "Add Material to the template calculation." msgstr "Materiaal toevoegen aan de berekening in het sjabloon." #: rc.cpp:677 rc.cpp:1508 #, kde-format msgid "material" msgstr "materiaal" #: rc.cpp:686 rc.cpp:1517 #, kde-format msgid "unit" msgstr "Eenheid" #: rc.cpp:689 rc.cpp:1544 #, kde-format msgid "" "Please enter the MySQL Database server settings. \n" "\n" "For detailed setup instructions for the MySQL to use with Kraft please check the Kraft website." msgstr "" "Geef de instellingen op voor de MySQL Database server\n" "\n" "Ga voor gedetailleerde instructies voor hoe MySQL samen met Kraft te \n" "gebruiken naar de website van Kraft." #: rc.cpp:694 rc.cpp:1549 #, kde-format msgid "Database Host:" msgstr "Database Host:" #: rc.cpp:697 rc.cpp:1552 #, kde-format msgid "Database Name:" msgstr "Databasenaam:" #: rc.cpp:703 rc.cpp:1558 #, kde-format msgid "Password:" msgstr "Wachtwoord" #: rc.cpp:706 rc.cpp:1561 #, kde-format msgid "

            Edit Number Cycles

            " msgstr "

            Volgnummersysteem bewerken

            " #: rc.cpp:709 rc.cpp:1564 #, kde-format msgid "Number Cycle Details" msgstr "Details volgnummers" #: rc.cpp:712 rc.cpp:1567 #, kde-format msgid "&Number Cycle:" msgstr "Volg&nummers:" #: rc.cpp:715 rc.cpp:1570 #, kde-format msgid "&Counter:" msgstr "Teller:" #: rc.cpp:721 rc.cpp:1576 #, kde-format msgid "ident &Template:" msgstr "Volgnummer-sjabloon:" #: rc.cpp:727 rc.cpp:1582 #, kde-format msgid "&Select a number cycle and edit the details on the right:" msgstr "Kies een volgnummersysteem en stel de details rechts in:" #: rc.cpp:730 rc.cpp:1585 #, kde-format msgid "New Item" msgstr "Nieuw item" #: rc.cpp:736 rc.cpp:1591 #, kde-format msgid "add" msgstr "Toevoegen" #: rc.cpp:742 rc.cpp:1597 #, kde-format msgid "remove" msgstr "Verwijderen" #: rc.cpp:745 rc.cpp:1600 #, kde-format msgid "1." msgstr "1." #: rc.cpp:748 rc.cpp:751 rc.cpp:1603 rc.cpp:1606 #, kde-format msgid "D" msgstr "D" #: rc.cpp:758 rc.cpp:1613 #, kde-format msgid "of the sum of" msgstr "voor de som van" #: rc.cpp:761 rc.cpp:1616 #, kde-format msgid "" "Please enter the SQLite Database Settings.\n" "\n" "Pick a filename to name the SQLite database file or leave the default setting." msgstr "" "Voer de instellingen in voor de SQLite Database.\\n\n" "\\n\n" "Geef een bestandsnaam op voor het SQLite databasebestand of gebruik de \n" "standaardwaarde." #: rc.cpp:766 rc.cpp:1621 #, kde-format msgid "store the database file at default place." msgstr "Sla het databasebestand op de standaard locatie op." #: rc.cpp:769 rc.cpp:1624 #, kde-format msgid "select a file name:" msgstr "Bestandsnaam selecteren:" #: rc.cpp:772 rc.cpp:1627 #, kde-format msgid "

            Add a Tax Rate

            " msgstr "

            BTW-tarief toevoegen" #: rc.cpp:775 rc.cpp:1630 #, kde-format msgid "Start-Date:" msgstr "Startdatum:" #: rc.cpp:778 rc.cpp:1633 #, kde-format msgid "&Reduced Tax Rate:" msgstr "Laag BTW-tarief:" #: rc.cpp:781 rc.cpp:1636 #, kde-format msgid "&Full Tax Rate:" msgstr "Hoog BTW-tarief:" #: rc.cpp:784 rc.cpp:1639 #, kde-format msgid "Edit Document Text Template" msgstr "Edit Document Text Template" #: rc.cpp:787 rc.cpp:1642 #, kde-format msgid "&Name:" msgstr "&Naam:" #: rc.cpp:790 rc.cpp:1645 #, kde-format msgid "displayed as" msgstr "getoond als" #: rc.cpp:793 rc.cpp:1648 #, kde-format msgid "in doc type" msgstr "In documenttype" #: rc.cpp:796 rc.cpp:1651 #, kde-format msgid "&Text:" msgstr "&Tekst:" #: rc.cpp:799 rc.cpp:1520 #, kde-format msgid "Calculation Item Time" msgstr "Arbeid berekenen" #: rc.cpp:802 rc.cpp:1523 #, kde-format msgid "

            Calculation Part 'Time'

            " msgstr "

            Post arbeid

            " #: rc.cpp:805 rc.cpp:1526 #, kde-format msgid "" "Calculate time efforts here for one unit of the template.
            Note that the" " costs may depend on a global hourly rate." msgstr "" "Voer hier de benodigde tijd in voor een exemplaar van het sjabloon. " "
            Vergeet niet dat het project-tarief invloed kan hebben op de kosten." #: rc.cpp:811 rc.cpp:1532 #, kde-format msgid "Work" msgstr "Werk" #: rc.cpp:814 rc.cpp:1535 #, kde-format msgid "&Time Effort:" msgstr "Nodigde &tijd:" #: rc.cpp:817 rc.cpp:1538 #, kde-format msgid "&Hourly Rate:" msgstr "Uurtarief:" #: rc.cpp:820 rc.cpp:1541 #, kde-format msgid "Apply the &global hourly rate" msgstr "Project-tarief toepassen" #: rc.cpp:823 rc.cpp:1654 #, kde-format msgid "

            Add a unit

            " msgstr "

            Eenheid toevoegen

            " #: rc.cpp:826 rc.cpp:1657 #, kde-format msgid "Unit short" msgstr "Eenheid afgekort" #: rc.cpp:829 rc.cpp:1660 #, kde-format msgid "Unit long" msgstr "Eenheid voluit" #: rc.cpp:832 rc.cpp:1663 #, kde-format msgid "Unit plural short" msgstr "Eenheid meervoud afgekort" #: rc.cpp:835 rc.cpp:1666 #, kde-format msgid "Unit plural long" msgstr "Eenheid meervoud voluit" #: rc.cpp:838 rc.cpp:1700 #, kde-format msgid "" "This step checks if the database schema version is sufficient for this version of Kraft. \n" "\n" "In case it is not, the schema is updated automatically.\n" msgstr "" "Deze stap controleert of de versie van het databaseschema modern genoeg is voor deze versie van Kraft. \n" "\n" "Zo niet, dan wordt het schema automatisch bijgewerkt.\n" #: rc.cpp:853 rc.cpp:1715 #, kde-format msgid "Upgrade not yet started" msgstr "Opwaarderen nog niet gestart" #: rc.cpp:856 rc.cpp:1718 #, kde-format msgid "

            Add a Wage group

            " msgstr "

            Functiegroep toevoegen

            " #: rc.cpp:859 rc.cpp:1721 #, kde-format msgid "Group name" msgstr "Functienaam" #: rc.cpp:862 rc.cpp:1724 #, kde-format msgid "Wage" msgstr "Salaris" #: models/docbasemodel.cpp:33 #, kde-format msgid "Doc. Number" msgstr "Doc.nummer" #: models/docbasemodel.cpp:34 #, kde-format msgid "Doc. Type" msgstr "Doc.type" #: models/docbasemodel.cpp:36 #, kde-format msgid "Client Id" msgstr "Klant ID" #: models/docbasemodel.cpp:37 #, kde-format msgid "Last modified" msgstr "Laatst wijziging" #: models/docbasemodel.cpp:38 #, kde-format msgid "Creation date" msgstr "Aanmaakdatum" #: models/docbasemodel.cpp:40 #, kde-format msgid "Client Address" msgstr "Adres klant" #: models/docbasemodel.cpp:41 #, kde-format msgid "Client" msgstr "Klant" #: models/docbasemodel.cpp:109 #, kde-format msgid "Looking up address" msgstr "Bezig met het opzoeken van het adres" #: models/docbasemodel.cpp:111 #, kde-format msgid "Lookup started" msgstr "bezig met opzoeken" #: itemtagdialog.cpp:82 #, kde-format msgid "Edit Item Tags" msgstr "Item Tags bewerken" #: itemtagdialog.cpp:89 #, kde-format msgid "Item Tags" msgstr "Item Tags" #: itemtagdialog.cpp:90 #, kde-format msgid "Select all tags for the item should be tagged with." msgstr "Selecteer alle tags waarmee u het item wilt markeren." #: itemtagdialog.cpp:105 tagtemplatesdialog.cpp:150 #, kde-format msgid "Tag" msgstr "Tag" #: itemtagdialog.cpp:106 tagtemplatesdialog.cpp:151 #, kde-format msgid "Color" msgstr "Kleur" #: itemtagdialog.cpp:107 tagtemplatesdialog.cpp:152 #, kde-format msgid "Description" msgstr "Beschrijving" #: numbercycledialog.cpp:52 #, kde-format msgid "Edit Number Cycles" msgstr "Volgnummersysteem bewerken" #: numbercycledialog.cpp:67 #, kde-format msgid "" "The template may contain the following tags:
            • %y or %yyyy - the year " "of the documents date.
            • %yy - the year of the document (two " "digits).
            • %w - the week number of the documents date.
            • %ww - " "the week number of the documents date with leading zero.
            • %d - the " "day number of the documents date.
            • %dd - the day number of the " "documents date with leading zero.
            • %m or %M - the month number of the" " documents date.
            • %MM - the month number with leading " "zero.
            • %c - the customer id from kaddressbook
            • %i - the unique" " counter
            • %type - the localised doc type (offer, invoice " "etc.)
            • %uid - the contact id of the client.
            %i needs to be " "part of the template." msgstr "" "In het sjabloon mogen de volgende tags voorkomen:
            • %y of %yyyy - het " "jaar van het document datum.
            • %yy - thet jaar van het document (twee " "cijfers).
            • %w - het weeknummer van het document datum.
            • %ww - " "het week nummer van het document datum (twee cijfers).
            • %d - het " "dagnummer van het document.
            • %dd - het dagnummer van het document " "(twee cijfers).
            • %m of %M - de maand als nummer van het " "document.
            • %MM - de maand als nummer met twee cijfers.
            • %c - " "de klanten ID van kaddressbook
            • %i - het volgnummer
            • %type - " "het gelokaliseerde doc type (offerte, rekening enz.)
            • %uid - het " "contact ID van de klant.
            %i moet onderdeel zijn van het sjabloon." #: numbercycledialog.cpp:138 #, kde-format msgid "Doc-Type" msgstr "Doc-type" #: numbercycledialog.cpp:215 #, kde-format msgid "Add Number Cycle" msgstr "Volgnummersysteem toevoegen" #: numbercycledialog.cpp:216 #, kde-format msgid "Enter the name of a new number cycle." msgstr "Voer de naam in van een nieuw volgnummernummersysteem." #: numbercycledialog.cpp:274 #, kde-format msgid "The numbercycle %1 is still assigned to a document type." msgstr "Het Volgnummersysteem %1 wordt nog in een documenttype gebruikt." #: numbercycledialog.cpp:275 #, kde-format msgid "" "The number cycle can not be deleted as long as it is assigned to a document " "type." msgstr "" "Het volgnummersysteem kan niet worden verwijdert zolang het nog in een\n" "documenttype wordt gebruikt." #: numbercycledialog.cpp:334 #, kde-format msgid "Dangerous Counter Change" msgstr "Gevaarlijke wijziging stappen" #: numbercycledialog.cpp:335 #, kde-format msgid "The new counter is lower than the old one. " msgstr "De nieuwe stappen zijn kleiner dan de oude." #: numbercycledialog.cpp:336 #, kde-format msgid "" "That has potential to create duplicate document numbers. Do you really want " "to decrease it?" msgstr "" "Hierdoor kunnen documentnummers meerdere keren voorkomen. Wilt u het echt " "verkleinen?" #: portalview.cpp:65 #, kde-format msgid "About Kraft" msgstr "Info of Kraft" #: portalview.cpp:83 #, kde-format msgid "Documents" msgstr "Documenten" #: portalview.cpp:89 #, kde-format msgid "Timeline" msgstr "Tijdlijn" #: portalview.cpp:96 #, kde-format msgid "Catalogs" msgstr "Catalogi" #: portalview.cpp:134 #, kde-format msgid "Kraft Document Overview" msgstr "Overzicht documenten in Kraft" #: portalview.cpp:141 portalview.cpp:160 #, kde-format msgid "Available Catalogs" msgstr "Beschikbare catalogi" #: portalview.cpp:143 #, kde-format msgid "No catalogs available." msgstr "Geen catalogi beschikbaar." #: portalview.cpp:192 #, kde-format msgid "Open" msgstr "Openen" #: portalview.cpp:203 #, kde-format msgid "No templates yet." msgstr "Nog geen sjablonen." #: portalview.cpp:208 #, kde-format msgid "%1 templates in %2 chapters
            last modified at %3" msgstr "%1 sjablonen in %2 mappen
            gewijzigd op %3" #: portalview.cpp:215 #, kde-format msgid "XML Export" msgstr "XML-export" #: portalview.cpp:270 #, kde-format msgid "Kraft Website" msgstr "Kraft Website" #: portalview.cpp:273 #, kde-format msgctxt "The string is followed by a link to the GPL2 text" msgid "Kraft is free software licensed under the" msgstr "Kraft is vrije software met een licentie onder de" #: portalview.cpp:274 #, kde-format msgctxt "The string is followed by the link to github" msgid "Kraft is maintained on " msgstr "Kraft wordt onderhouden op" #: portalview.cpp:275 #, kde-format msgid "Authors" msgstr "Auteurs" #: portalview.cpp:276 #, kde-format msgid "Developer and Maintainer" msgstr "Ontwikkelaar en onderhouder" #: portalview.cpp:277 #, kde-format msgid "Developer" msgstr "Ontwikkelaar" #: portalview.cpp:278 #, kde-format msgctxt "The person who provided the logo graphics" msgid "Logo design" msgstr "Logo ontwerp" #: portalview.cpp:279 #, kde-format msgctxt "The person who provided the user manual" msgid "User Manual" msgstr "Gebruikershandleiding" #: portalview.cpp:281 #, kde-format msgid "" "Kraft helps you to handle documents like quotes and invoices in your small " "business." msgstr "" "Kraft is bedoelt voor kleine bedrijven voor hulp bij het beheer van " "documenten zoals offertes en rekeningen." #: portalview.cpp:282 #, kde-format msgid "Welcome to Kraft" msgstr "Welkom bij Kraft" #: portalview.cpp:283 #, kde-format msgid "Kraft Version" msgstr "Kraft versie" #: portalview.cpp:285 #, kde-format msgid "Codename" msgstr "Codenaam" #: portalview.cpp:288 #, kde-format msgid "Country Setting" msgstr "Ingesteld land:" #: portalview.cpp:291 #, kde-format msgid "Language Setting" msgstr "Ingestelde taal:" #: portalview.cpp:295 #, kde-format msgid "Kraft Initialisation Problem" msgstr "Kraft Initialisatie probleem" #: portalview.cpp:296 #, kde-format msgid "" "There is a initialisation error on your system. Kraft will not work that " "way." msgstr "" "Er is een initialisatie fout op uw systeem. Kraft zal op die manier niet " "werken." #: portalview.cpp:303 #, kde-format msgid "Database Information" msgstr "Database-informatie" #: portalview.cpp:304 #, kde-format msgid "Kraft database name" msgstr "Kraft database naam:" #: portalview.cpp:309 #, kde-format msgid "Required Version" msgstr "Vereiste versie" #: portalview.cpp:312 #, kde-format msgid "Database schema version" msgstr "Database-schemaversie:" #: portalview.cpp:314 #, kde-format msgid "Qt database driver" msgstr "Databasestuurprogramma voor Qt:" #: portalview.cpp:318 #, kde-format msgid "established" msgstr "tot stand gebracht" #: portalview.cpp:318 #, kde-format msgid "NOT AVAILABLE!" msgstr "NIET BESCHIKBAAR!" #: portalview.cpp:319 #, kde-format msgid "Database connection" msgstr "Databaseverbinding" #: portalview.cpp:328 #, kde-format msgid "Database Version" msgstr "Databaseversie" #: portalview.cpp:336 #, kde-format msgid "Addressbook Backend" msgstr "Addressbook Backend" #: portalview.cpp:337 #, kde-format msgid "Backend type" msgstr "Backend type" #: portalview.cpp:339 #, kde-format msgid "running" msgstr "Gestart" #: portalview.cpp:339 #, kde-format msgid "not running" msgstr "Niet gestart" #: portalview.cpp:343 #, kde-format msgid "External Tools" msgstr "Externe hulpprogramma's" #: portalview.cpp:344 #, kde-format msgid "RML to PDF conversion tool" msgstr "Hulpprogramma voor conversie van RML naar PDF:" #: portalview.cpp:347 #, kde-format msgid "not found!" msgstr "niet gevonden!" #: portalview.cpp:350 #, kde-format msgid "iconv tool for text import" msgstr "iconv hulpmiddel voor importeren van tekst" #: portalview.cpp:353 #, kde-format msgid "weasyprint for PDF generation" msgstr "weasyprint voor creatie van PDF" #: portalview.cpp:357 #, kde-format msgid "not available" msgstr "niet beschikbaar" #: portalview.cpp:362 #, kde-format msgid "Some Icons are made by" msgstr "Enkele pictogrammen zijn gemaakt door" #: portalview.cpp:363 #, kde-format msgid "Acknowledgements" msgstr "Met dank aan" #: prefsdialog.cpp:66 #, kde-format msgid "Configure Kraft" msgstr "Kraft instellen" #: prefsdialog.cpp:100 #, kde-format msgid "Document Defaults" msgstr "Standaarddocument" #: prefsdialog.cpp:101 #, kde-format msgid "Taxes" msgstr "Belastingen" #: prefsdialog.cpp:102 #, kde-format msgid "Document Types" msgstr "Documenttypes" #: prefsdialog.cpp:104 #, kde-format msgid "Wages" msgstr "Salarissen" #: prefsdialog.cpp:106 #, kde-format msgid "Units" msgstr "Eenheden" #: prefsdialog.cpp:107 #, kde-format msgid "Own Identity" msgstr "Uw eigen identiteit" #: prefsdialog.cpp:154 #, kde-format msgid "Tax rates beginning at date:" msgstr "Belastingtarieven beginnend op de datum:" #: prefsdialog.cpp:162 prefsunits.cpp:53 prefswages.cpp:51 #, kde-format msgid "ID" msgstr "ID" #: prefsdialog.cpp:163 #, kde-format msgid "Full Tax [%]" msgstr "Hoog BTW [%]" #: prefsdialog.cpp:164 #, kde-format msgid "Reduced Tax [%]" msgstr "Laag BTW [%]" #: prefsdialog.cpp:165 #, kde-format msgid "Start Date" msgstr "Startdatum" #: prefsdialog.cpp:205 #, kde-format msgid "" "Select the identity of the sending entity of documents. That's your " "companies address." msgstr "" "Selecteer de identiteit van de document-verzender. Dat is het adres van " "uw bedrijf." #: prefsdialog.cpp:221 #, kde-format msgid "Select Identity..." msgstr "Identiteit selecteren..." #: prefsdialog.cpp:227 #, kde-format msgid "From AddressBook" msgstr "Uit adresboek" #: prefsdialog.cpp:232 setupassistant.cpp:340 #, kde-format msgid "Manual Entry" msgstr "Handmatig toevoegen" #: prefsdialog.cpp:244 #, kde-format msgid "Manual Address" msgstr "Handmatige adres" #: prefsdialog.cpp:299 #, kde-format msgid "&Default document type on creation:" msgstr "Stan&daard documenttype bij aanmaken:" #: prefsdialog.cpp:304 #, kde-format msgid "New documents default to the selected type." msgstr "Nieuwe documenten hebben standaard het geselecteerde type." #: prefsdialog.cpp:313 #, kde-format msgid "Default &Tax for Documents:" msgstr "Standaard &belastingtarief voor documenten:" #: prefsdialog.cpp:318 #, kde-format msgid "The default tax setting for all documents." msgstr "" "De standaard instelling voor het belastingtarief te gebruiken in alle " "documenten." #: prefsdialog.cpp:330 #, kde-format msgid "Document Date Format:" msgstr "Datum instelling van document:" #: prefsdialog.cpp:336 #, kde-format msgid "The default date format for documents." msgstr "De standaard datum-instelling voor documenten." #: prefsdialog.cpp:338 #, kde-format msgid "ISO-Format: %1" msgstr "ISO-opmaak: %1" #: prefsdialog.cpp:340 #, kde-format msgid "Short-Date: %1" msgstr "Korte-datum: %1" #: prefsdialog.cpp:342 #, kde-format msgid "Long-Date: %1" msgstr "Lange-datum: %1" #: prefsdialog.cpp:344 #, kde-format msgid "RFC 2822-Format: %1" msgstr "RFC 2822-Opmaak: %1" #: prefsdialog.cpp:346 #, kde-format msgid "\"German Format\": %1" msgstr "\"Duitse Opmaak\": %1" #: prefsdialog.cpp:347 #, kde-format msgid "Custom Setting in Settingsfile" msgstr "Aangepaste instelling in het instellingenbestand" #: prefsdialog.cpp:382 #, kde-format msgid "" "The old default doc type for new documents was just deleted.Please check the" " setting in the Document Defaults in the Kraft preferences Dialog." msgstr "" "Het oude standaarddocument type voor nieuwe documenten is zojuist verwijderd. Controleer de instellingen voor document types in het \n" "configuratiedialoog van Kraft." #: prefsdialog.cpp:385 #, kde-format msgid "Document Default Change" msgstr "Wijzigen standaarddocument" #: prefsdialog.cpp:593 #, kde-format msgid "The identity can not be found." msgstr "De identiteit is niet te vinden." #: prefsdialog.cpp:595 #, kde-format msgid "" "

            Kraft Addressbook Integration down.

            The address book backend" " is not up and running.

            Please check your addressbook integration " "setup.

            " msgstr "" "

            Kraft adresboek Integratie werkt niet.

            De backend van het " "adresboek is niet opgestart.

            Controleer de instellingen de integratie " "van uw adresboek.

            " #: prefsdialog.cpp:601 #, kde-format msgid "The identity is not listed in an address book." msgstr "Deze identiteit komt niet in een adresboek voor." #: prefsdialog.cpp:603 #, kde-format msgid "" "

            Kraft does not know your identity.

            Please pick one from the " "address books by clicking on the Button below.

            Not having an identity " "selected can make your documents look incomplete.

            " msgstr "" "

            Kraft weet uw identiteit niet.

            Selecteer een uit het adres " "boek door te klikken op de knop eronder.

            Uw documenten kunnen " "incompleet er uit zien als u geen identiteit heeft geselecteerd.

            " #: prefsdialog.cpp:620 #, kde-format msgid "Your identity can be found in the address books." msgstr "Uw identiteit is te vinden in de adresboeken." #: prefsdialog.cpp:631 #, kde-format msgid "Work Phone" msgstr "Telefoon werk" #: prefsdialog.cpp:632 #, kde-format msgid "Fax" msgstr "Fax" #: prefsdialog.cpp:633 #, kde-format msgid "Cell Phone" msgstr "Mobiele telefoon" #: prefsunits.cpp:54 #, kde-format msgid "Short" msgstr "Afgekort" #: prefsunits.cpp:55 #, kde-format msgid "Long" msgstr "Compleet" #: prefsunits.cpp:56 #, kde-format msgid "Short plural" msgstr "Kort meervoud" #: prefsunits.cpp:57 #, kde-format msgid "Long plural" msgstr "Compleet meervoud" #: prefsunits.cpp:152 #, kde-format msgid "Edit a unit" msgstr "Bewerk eenheid" #: prefsunits.cpp:195 #, kde-format msgid "

            Edit unit

            " msgstr "

            Bewerk eenheid

            " #: prefswages.cpp:52 #, kde-format msgid "Code" msgstr "Code" #: prefswages.cpp:54 #, kde-format msgid "Sortkey" msgstr "Sneltoets" #: prefswages.cpp:80 #, kde-format msgid "Up" msgstr "Omhoog" #: prefswages.cpp:85 #, kde-format msgid "Down" msgstr "Omlaag" #: prefswages.cpp:201 #, kde-format msgid "Edit a wage group" msgstr "Salarisgroep bewerken" #: prefswages.cpp:241 #, kde-format msgid "

            Edit wage group

            " msgstr "

            Salarisgroep bewerken

            " #: reportgenerator.cpp:113 #, kde-format msgid "Document generation process is still running." msgstr "Ben nog bezig met het generen van documenten." #: reportgenerator.cpp:167 #, kde-format msgid "Template file is not accessible." msgstr "Kan sjabloon niet vinden." #: reportgenerator.cpp:192 #, kde-format msgid "The template conversion failed." msgstr "De conversie van het sjabloon is mislukt." #: reportgenerator.cpp:200 #, kde-format msgid "Saving to temporar file failed." msgstr "Opslaan naar tijdelijk bestand is mislukt." #: reportgenerator.cpp:292 #, kde-format msgid "No converter error." msgstr "Geen problemen bij de conversie." #: reportgenerator.cpp:295 #, kde-format msgid "The ReportLab based converter script can not be executed." msgstr "" "Het op ReportLab gebaseerde conversie script kon niet worden uitgevoerd." #: reportgenerator.cpp:298 #, kde-format msgid "An unknown error happened." msgstr "Een onbekend probleem trad op." #: reportgenerator.cpp:301 #, kde-format msgid "The ReportLab python module is not installed." msgstr "De ReportLab python module is niet geïnstalleerd." #: reportgenerator.cpp:304 #, kde-format msgid "The source file can not be read." msgstr "Kan het bronbestand niet openen." #: reportgenerator.cpp:307 #, kde-format msgid "The target can not be opened to write." msgstr "Het doelbestand kon niet worden geopend." #: reportgenerator.cpp:310 #, kde-format msgid "The target file does not exist." msgstr "Het doelbestand is niet te vinden." #: reportgenerator.cpp:313 #, kde-format msgid "The WeasyPrint tool is not installed." msgstr "Het WeasyPrint onderdeel is niet geïnstalleerd." #: reportgenerator.cpp:316 #, kde-format msgid "The PDF merger utility failed." msgstr "Het samenvoegen van PDF mislukte." #: reportgenerator.cpp:335 #, kde-format msgid "There is not template defined for %1." msgstr "Er is geen sjabloon gedefinieerd voor %1." #: reportgenerator.cpp:340 #, kde-format msgid "The template file %1 for document type %2 does not exist." msgstr "Het sjabloonbestand %1 voor documenttype %2 bestaat niet." #: reportgenerator.cpp:344 #, kde-format msgid "The template file %1 for document type %2 can not be read." msgstr "Kan het sjabloonbestand %1 voor documenttype %2 niet openen." #: setupassistant.cpp:36 #, kde-format msgid "Welcome to the Kraft Setup Assistant" msgstr "Welkom bij de Kraft setup assistent" #: setupassistant.cpp:55 #, kde-format msgid "Select the Database Backend" msgstr "Selecteer de Database Backend" #: setupassistant.cpp:92 #, kde-format msgid "Sqlite File Name" msgstr "SQLite bestandsnaam" #: setupassistant.cpp:146 #, kde-format msgid "MySql Detail Information" msgstr "Gedetailleerde MySql-informatie" #: setupassistant.cpp:188 #, kde-format msgid "Create Database" msgstr "Database creëren" #: setupassistant.cpp:210 setupassistant.cpp:223 #, kde-format msgid "0/%1" msgstr "0/%1" #: setupassistant.cpp:242 setupassistant.cpp:252 setupassistant.cpp:290 #, kde-format msgid "%1/%2" msgstr "%1/%2" #: setupassistant.cpp:265 #, kde-format msgid "Upgrade the Database" msgstr "Database opwaarderen" #: setupassistant.cpp:313 #, kde-format msgid "Your Company Address" msgstr "Uw bedrijfsadres" #: setupassistant.cpp:318 #, kde-format msgid "" "Select your companies address either from the address book or enter it " "manually. It is set as a consigner on the documents." msgstr "" "Selecteer in het adresboek uw eigen adres of voer het handmatig in. Deze " "wordt gebruikt als afzender in de documenten." #: setupassistant.cpp:325 #, kde-format msgid "Select from Addressbook" msgstr "Selecteer in adresboek" #: setupassistant.cpp:416 #, kde-format msgid "Final Status" msgstr "Final Status" #: setupassistant.cpp:510 #, kde-format msgid "" "

            Can't connect to your database. Are you sure your credentials are correct" " and the database exists?

            " msgstr "" "

            Kan geen verbinding maken met de database. Weet u zeker dat uw \n" "instellingen correct zijn en dat de database bestaat?

            " #: setupassistant.cpp:521 #, kde-format msgid "

            Can't open your database file, check the permissions and such." msgstr "" "Kan het databasebestand niet openen, controleer de permissies en " "dergelijke.

            " #: setupassistant.cpp:530 #, kde-format msgid "" "

            The database is already existing, no action needs to be taken " "here.

            Please hit next to proceed.

            " msgstr "" "

            De database bestaat al, verdere acties zijn niet nodig.

            Druk op " "volgende om verder te gaan.

            " #: setupassistant.cpp:579 #, kde-format msgid "

            The database setup was successfully completed.

            " msgstr "

            De database is succesvol gecreëerd.

            " #: setupassistant.cpp:580 #, kde-format msgid "

            You can start to work with Kraft now. Please do not forget to

            " msgstr "

            U kunt nu met Kraft gaan werken. Vergeet niet om

            " #: setupassistant.cpp:582 #, kde-format msgid "
          • adjust various settings in the Kraft Preferences dialog.
          • " msgstr "" "
          • in het dialoogvenster van Kraft voor de voorkeursinstellingen diverse \n" "instellingen aan te passen.
          • " #: setupassistant.cpp:583 #, kde-format msgid "
          • Check the Catalog chapter list.
          • " msgstr "
          • Bekijk de lijst met catalogus-mappen.
          • " #: setupassistant.cpp:584 #, kde-format msgid "
          • Make your business and have fun.
          • " msgstr "
          • Zet uw eigen bedrijf op en heb veel plezier ermee.
          • " #: setupassistant.cpp:586 #, kde-format msgid "" "

            If you press Finish now, the new database configuration is stored " "in Krafts configuration.

            " msgstr "" "

            Als u nu op Voltooien drukt, dan wordt de nieuwe database \n" "configuratie in de configuratie van Kraft opgeslagen.

            " #: setupassistant.cpp:602 #, kde-format msgid "" "The Database can not be connected. Please check the database credentials." msgstr "" "Verbinding met de database is niet mogelijk. Controleer de gegevens van \n" "database." #: setupassistant.cpp:608 #, kde-format msgid "The database core tables do not exist. Please check initial setup." msgstr "" "De basistabellen in de database zijn onvindbaar. Voer de initiële aanmaak \n" "opnieuw uit." #: setupassistant.cpp:615 #, kde-format msgid "Database is up to date. No upgrade is required." msgstr "De database is up-to-date. bijwerken is niet nodig." #: setupassistant.cpp:620 #, kde-format msgid "Parse Update Commands..." msgstr "Bezig met het bijwerken..." #: setupassistant.cpp:663 #, kde-format msgid "The Upgrade failed!" msgstr "Het bijwerken is mislukt!" #: setupassistant.cpp:665 #, kde-format msgid "The Upgrade succeeded, the current schema version is %1!" msgstr "Het bijwerken is gelukt, de versie van het huidige schema is %1!" #: setupassistant.cpp:678 #, kde-format msgid "" "The Database can not be connected. Please check the database credentials!" msgstr "" "Verbinding met de database is niet mogelijk. Controleer de gegevens van \n" "database." #: setupassistant.cpp:684 #, kde-format msgid "Parse Create Commands..." msgstr "Bezig met het aanmaken..." #: setupassistant.cpp:692 #, kde-format msgid "Parse database fillup commands..." msgstr "Bezig met het vullen van de database..." #: setupassistant.cpp:705 #, kde-format msgid "Processing database creation commands..." msgstr "Bezig met het aanmaken van de database..." #: setupassistant.cpp:722 #, kde-format msgid "Process database fillup commands..." msgstr "Bezig met het vullen van de database..." #: setupassistant.cpp:732 #, kde-format msgid "Successfully finished commands." msgstr "De commando's zijn succesvol voltooid." #: setupassistant.cpp:734 #, kde-format msgid "Failed to perform all commands." msgstr "Het uitvoeren van alle commando's is mislukt." #: setupassistant.cpp:778 #, kde-format msgid "" "This assistant guides you through the basic settings of your Kraft " "installation." msgstr "" "Deze assistent leidt u door de basisinstellingen van uw Kraft installatie." #: setupassistant.cpp:788 #, kde-format msgid "There was no database configuration found." msgstr "Er is geen database configuratie gevonden." #: setupassistant.cpp:790 #, kde-format msgid "A valid current database configuration file was found." msgstr "Er is een geldige database configuratie gevonden." #: setupassistant.cpp:812 #, kde-format msgid "The database schema version is too low. It will be updated." msgstr "De database-indeling is te oud, Het zal worden bijgewerkt." #: setupassistant.cpp:815 #, kde-format msgid "The current database schema version is too high. Leaving untouched! " msgstr "Het huidige database-schemaversie is te hoog. Laat het ongewijzigd!" #: setupassistant.cpp:822 #, kde-format msgid "" "

            The database can be opened, but does not contain valid content.

            A " "new database can be created automatically from scratch.

            " msgstr "" "

            De database kan worden geopend, maar heeft geen geldige inhoud.

            >

            U " "kunt een nieuwe database vanaf nul aanmaken.

            " #: setupassistant.cpp:829 #, kde-format msgid "

            Kraft failed to connect to the configured database.

            " msgstr "

            Kraft kon geen verbinding maken met de ingestelde database.

            " #: setupassistant.cpp:831 #, kde-format msgid "" "

            Please check the database server setup and restart Kraft to connect." msgstr "" "

            Controleer de instellingen van de database server en herstart Kraft om " "verbinding te maken." #: setupassistant.cpp:833 #, kde-format msgid "

            Please check the database file." msgstr "

            Controleer het databasebestand." #: setupassistant.cpp:835 #, kde-format msgid "or create a new database by hitting next.

            " msgstr "" "of maak een nieuwe database aan door te klikken op volgende.

            " #: setupassistant.cpp:843 #, kde-format msgid "

            Please hit next and follow the instructions.

            " msgstr "

            Klik op volgende en volg de instructies.

            " #: tagtemplatesdialog.cpp:47 #, kde-format msgid "Edit Tag Template" msgstr "Bewerk tag sjabloon" #: tagtemplatesdialog.cpp:54 #, kde-format msgid "Edit a Tag Template" msgstr "Tag sjabloon bewerken" #: tagtemplatesdialog.cpp:55 #, kde-format msgid "Adjust settings for name, color and description." msgstr "Instellingen wijzigen voor naam, kleur en beschrijving." #: tagtemplatesdialog.cpp:63 #, kde-format msgid "Description:" msgstr "Beschrijving:" #: tagtemplatesdialog.cpp:69 #, kde-format msgid "Associated Color:" msgstr "Gekozen kleur" #: tagtemplatesdialog.cpp:135 tagtemplatesdialog.cpp:140 portal.cpp:165 #, kde-format msgid "Edit Tag Templates" msgstr "Tag sjabloon bewerken" #: tagtemplatesdialog.cpp:141 #, kde-format msgid "Add, edit and remove tag templates for use in the documents." msgstr "" "Het toevoegen, bewerken en verwijderen van tag sjablonen te gebruiken in " "documenten." #: tagtemplatesdialog.cpp:165 #, kde-format msgid "Add..." msgstr "Toevoegen..." #: tagtemplatesdialog.cpp:167 #, kde-format msgid "Edit..." msgstr "Bewerken..." #: tagtemplatesdialog.cpp:170 #, kde-format msgid "Delete..." msgstr "Verwijderen..." #: tagtemplatesdialog.cpp:223 #, kde-format msgid "Do you really want to delete the template?" msgstr "Wilt u het sjabloon verwijderen? " #: taxeditdialog.cpp:36 #, kde-format msgid "Edit Tax Rates" msgstr "Belastingtarieven bewerken" #: templkataloglistview.cpp:48 #, kde-format msgid "Calc. Type" msgstr "Berekeningsmethode" #: templkataloglistview.cpp:55 #, kde-format msgid "Template Catalog" msgstr "Sjabloon catalogus" #: templkatalogview.cpp:101 #, kde-format msgid "" msgstr "" #: templtopositiondialogbase.cpp:51 #, kde-format msgid "the Header of the Document as first item" msgstr "Het briefhoofd van het document als eerste item" #: texteditdialog.cpp:42 #, kde-format msgid "Edit Text Templates" msgstr "Tekst sjablonen bewerken" #: texteditdialog.cpp:62 #, kde-format msgid "Edit %1 Template" msgstr "Bewerk %1 Sjabloon" #: textselection.cpp:42 #, kde-format msgid "Template Collection" msgstr "Verzameling sjablonen" #: textselection.cpp:100 #, kde-format msgid "Template Actions" msgstr "Sjabloon acties" #: textselection.cpp:129 #, kde-format msgid "This is the standard text used in new documents." msgstr "Dit is de standaard tekst voor nieuwe documenten." #: textselection.cpp:138 #, kde-format msgid "%1 Templates for %2" msgstr "%1 sjablonen voor %2" #: textselection.cpp:146 #, kde-format msgid "" "There is no %1 template text available for document type %2.
            Click the " "add-button below to create one." msgstr "" "Er is geen %1 tekstsjabloon beschikbaar voor document type %2.
            Klik op " "de knop voor toevoegen hieronder om een te creëren." #: textselection.cpp:201 #, kde-format msgid "&Use in Document" msgstr "Gebr&uikt in document" #: texttemplate.cpp:103 #, kde-format msgid "Failed to open template source" msgstr "Openen van sjablonen is mislukt" #: portal.cpp:102 #, kde-format msgid "&Quit" msgstr "&Afsluiten" #: portal.cpp:107 #, kde-format msgid "&Cut" msgstr "K&nippen" #: portal.cpp:112 #, kde-format msgid "C&opy" msgstr "&Kopiëeren" #: portal.cpp:117 #, kde-format msgid "&Paste" msgstr "&Plakken" #: portal.cpp:122 #, kde-format msgid "&Settings" msgstr "&Instellingen" #: portal.cpp:127 #, kde-format msgid "&Create Document" msgstr "&Nieuw document" #: portal.cpp:131 #, kde-format msgid "&Copy Document" msgstr "&Kopieer document" #: portal.cpp:135 #, kde-format msgid "Create &Followup Document" msgstr "Creëer &opvolg document" #: portal.cpp:140 #, kde-format msgid "Print Document" msgstr "Document afdrukken" #: portal.cpp:145 #, kde-format msgid "Show Document" msgstr "Document tonen" #: portal.cpp:150 #, kde-format msgid "Edit Document" msgstr "Document bewerken" #: portal.cpp:155 #, kde-format msgid "Open Archived Document" msgstr "Open gearchiveerd document" #: portal.cpp:160 #, kde-format msgid "Mail Document" msgstr "Verzend document" #: portal.cpp:170 #, kde-format msgid "Redo Initial Setup..." msgstr "De initiële aanmaak opnieuw uitvoeren..." #: portal.cpp:175 #, kde-format msgid "Kraft Handbook..." msgstr "Kraft Handboek.." #: portal.cpp:179 #, kde-format msgid "About Qt..." msgstr "Over Qt..." #: portal.cpp:183 #, kde-format msgid "About Kraft..." msgstr "Over Kraft..." #: portal.cpp:187 #, kde-format msgid "Quits the application" msgstr "Sluit het programma af" #: portal.cpp:189 #, kde-format msgid "Cuts the selected section and puts it to the clipboard" msgstr "Knipt de geselecteerde sectie uit en plaatst deze op het klembord" #: portal.cpp:190 #, kde-format msgid "Copies the selected section to the clipboard" msgstr "Kopieert de geselecteerde sectie naar het klembord" #: portal.cpp:191 #, kde-format msgid "Pastes the clipboard contents to current position" msgstr "Plakt de inhoud van het klembord op de huidige positie" #: portal.cpp:193 #, kde-format msgid "Creates a new Document" msgstr "Maakt een nieuw document aan" #: portal.cpp:194 #, kde-format msgid "Print and archive this Document" msgstr "Dit document afdrukken en archiveren" #: portal.cpp:195 #, kde-format msgid "Creates a new document which is a copy of the selected document" msgstr "Een nieuw document creëren als kopie van het geselecteerde document " #: portal.cpp:196 #, kde-format msgid "Create a followup document for the current document" msgstr "Creëert een opvolg document voor het huidige document" #: portal.cpp:197 #, kde-format msgid "Opens the document for editing" msgstr "Opent het document voor bewerken" #: portal.cpp:198 #, kde-format msgid "Opens a read only view on the document." msgstr "Opent het document alleen-lezen." #: portal.cpp:199 #, kde-format msgid "Send document per mail" msgstr "Document per email verzenden" #: portal.cpp:200 #, kde-format msgid "" "Edit the available tag templates which can be assigned to document items." msgstr "De aan de documenten te koppelen beschikbare tags bewerken." #: portal.cpp:201 #, kde-format msgid "Configure the Database Kraft is working on." msgstr "Stel de database in waarmee Kraft werkt." #: portal.cpp:202 #, kde-format msgid "Open a viewer on an archived document" msgstr "Gearchiveerd document weergeven" #: portal.cpp:219 #, kde-format msgid "&Edit" msgstr "Be&werken" #: portal.cpp:238 #, kde-format msgid "Kraft" msgstr "Kraft" #: portal.cpp:241 #, kde-format msgid "&Preferences" msgstr "&Voorkeuren" #: portal.cpp:245 #, kde-format msgid "Toolbars" msgstr "Werkbalken" #: portal.cpp:251 #, kde-format msgid "&Help" msgstr "&Help" #: portal.cpp:336 #, kde-format msgid "Database not running" msgstr "Database is niet gestart." #: portal.cpp:337 #, kde-format msgid "" "Kraft was started in readonly mode, but the configured database can not be connected.\n" "\n" "Kraft will abort." msgstr "" "Kraft is gestart in alleen lezen modus, maar een verbinding met de gekozen database is niet mogelijk.\n" "\n" "Kraft zal stoppen." #: portal.cpp:353 #, kde-format msgid "" "Kraft can not connect to the specified MySQL server. Please check the Kraft " "database settings, check if the server is running and verify if a database " "with the name %1 exits!" msgstr "" "Kraft kon geen verbinding met de opgegeven MySQL-server maken. Controleer de" " database-instellingen van Kraft, controleer of de server is gestart en of " "een database met de naam %1 bestaat!" #: portal.cpp:357 #, kde-format msgid "" "The database with the name %1 does not exist on the database server. Please " "make sure the database exists and is accessible by the user running Kraft." msgstr "" "De database met de naam %1 is bij deze database server niet te vinden.\n" "Controleer of deze database bestaat en door de gebruiker die Kraft gebruikt, geopend kan worden." #: portal.cpp:361 #, kde-format msgid "" "The Qt database driver could not be loaded. That probably means, that they " "are not installed. Please make sure the Qt database packages are installed " "and try again." msgstr "" "De Qt database driver kon niet worden geladen. Dit houd waarschijnlijk in dat het niet is geïnstalleerd. Controleer of de Qt database pakketten zijn \n" "geïnstalleerd en probeer het daarna opnieuw." #: portal.cpp:365 #, kde-format msgid "There is a database problem: %1" msgstr "Er is een probleem met de database: %1" #: portal.cpp:382 #, kde-format msgid "Database Problem." msgstr "Probleem met de database." #: portal.cpp:389 #, kde-format msgid "Check commandline actions" msgstr "Opdrachten op de commando controleren" #: portal.cpp:481 #, kde-format msgid "Welcome to Kraft, %1" msgstr "Welkom bij Kraft, %1" #: portal.cpp:510 #, kde-format msgid "Creating new document..." msgstr "Bezig met het aanmaken van een nieuw document..." #: portal.cpp:536 #, kde-format msgctxt "" "Dialog title of the followup doc dialog, followed by the id of the source " "doc" msgid "Create follow up document for %1" msgstr "Creëer &opvolg document voor %1" #: portal.cpp:581 #, kde-format msgctxt "Title of the new doc dialog, %1 is the source doc id" msgid "Create new Document as Copy of %1" msgstr "Kopie van %1 als nieuw document creëren" #: portal.cpp:616 #, kde-format msgid "Opening document to view..." msgstr "Bezig met het openen van document om deze weer te geven..." #: portal.cpp:646 #, kde-format msgid "Generating PDF..." msgstr "Bezig met generen van PDF..." #: portal.cpp:668 #, kde-format msgid "Generating PDF for EMail" msgstr "Bezig met generen van PDF voor de email" #: portal.cpp:688 #, kde-format msgid "Doc Generation Error" msgstr "Fout bij generen van document" #: portal.cpp:745 #, kde-format msgid "Printing archived document..." msgstr "Bezig met afdrukken gearchiveerd document..." #: portal.cpp:797 #, kde-format msgid "Opening document %1" msgstr "Bezig met het openen van document %1" #: portal.cpp:973 #, kde-format msgid "Cutting selection..." msgstr "Bezig met het knippen van de selectie..." #: portal.cpp:980 #, kde-format msgid "Copying selection to clipboard..." msgstr "Bezig met het kopiëren van de selectie naar het klembord..." #: portal.cpp:987 #, kde-format msgid "Inserting clipboard contents..." msgstr "Bezig met plakken van de inhoud van het klembord..." #: portal.cpp:1000 #, kde-format msgid "" "Ready. Kraft is running in read only mode. Document editing is prohibited." msgstr "" "Gereed, Kraft is in alleen lezen modus - document wijzigen is niet " "toegestaan." #: addressselectorwidget.cpp:229 #, kde-format msgid "Name" msgstr "Naam" #: addressselectorwidget.cpp:231 #, kde-format msgid "Address" msgstr "Adres" #: addressselectorwidget.cpp:331 #, kde-format msgid "Edit Contact..." msgstr "Contactpersoon bewerken..." #: addressselectorwidget.cpp:332 #, kde-format msgid "Edit the currently selected contact" msgstr "Deze contactpersoon bewerken" #: addressselectorwidget.cpp:335 #, kde-format msgid "New Contact..." msgstr "Nieuw contactpersoon..." #: addressselectorwidget.cpp:336 #, kde-format msgid "Create a new Contact" msgstr "Nieuw contactpersoon aanmaken" #: timecalcpart.cpp:82 #, kde-format msgid "Minutes" msgstr "Minuten" #: timecalcpart.cpp:84 #, kde-format msgid "Hours" msgstr "Uren" #: timecalcpart.cpp:86 #, kde-format msgid "Seconds" msgstr "Seconden" #: texttemplateinterface.cpp:53 #, kde-format msgid "No file name given for template" msgstr "Geen naam opgegeven voor sjabloon" #: texttemplateinterface.cpp:61 #, kde-format msgid "Could not find template file %1" msgstr "Kan sjabloonbestand %1 niet vinden" kraft-0.97/reports/000077500000000000000000000000001410616450300142565ustar00rootroot00000000000000kraft-0.97/reports/CMakeLists.txt000066400000000000000000000003171410616450300170170ustar00rootroot00000000000000add_subdirectory(pics) ########### install files ############### install(FILES delivery_receipt.trml invoice.trml kraft.css invoice.gtmpl DESTINATION ${DATA_INSTALL_DIR}/kraft/reports) kraft-0.97/reports/README.md000066400000000000000000000060711410616450300155410ustar00rootroot00000000000000# Report Templates From the templates in this directory, Kraft is generating the final documents in PDF format. ## Customizing ReportLab Templates Please refer to http://volle-kraft-voraus.de/Main/Documenttemplate about customizing the output document with the old ReportLab based system. ## Weasyprint WeasyPrint is a modern, HTML and CSS based way of creating PDF documents. The project homepage is [Weasyprint Project](https://weasyprint.org/). WeasyPrint will replace the so far used ReportLab based system after a deprecation period. ### Try it! WeasyPrint can be tested from Kraft Version 0.95 on. Just create a template and give it the file extension .gtmpl and Kraft will automatically use the Grantlee templating and WeasyPrint. The appearance of the printed page is mostly influenced by the CSS (Cascading Style Sheet) in file `invoice.css`. ## Internationalization All "human readable" strings in the doc templates are translated to the target language in the code, similar to the normal user interface. Instead of changing the template to the word in a non English language, one of the following template variables could be used: | Template Var. | Meaning | English Default| |---------------|----------------------------------------|----------------| |LAB_NO_SHORT |Sequence number printed on the document |No. | |LAB_ITEM |Document item printed on the document |Item| |LAB_QUANTITY_SHORT|Abbrev. of Quantity printed on the document|Qty.| |LAB_UNIT |Unit printed on the document|Unit| |LAB_PRICE |Price of an item printed on the document|Price| |LAB_SUM |Printed on the document |Sum | |LAB_NET |printed on the document |Net | |LAB_VAT |Printed on the document |VAT | |LAB_PHONE |Printed on the document |Phone| |LAB_FAX |Printed on the document |FAX | |LAB_MOBILE |Printed on the document |Mobile| |LAB_EMAIL |Printed on the document |Email| |LAB_WEBSITE |Printed on the document |Website| |LAB_SPECIAL_ITEMS|Text underneath the list of items to sign out special items like Demand or Alternative items|Please note: This offer contains %1 alternative or demand positions, printed in italic font..| |LAB_TAX_FREE_ITEMS|Label for the amount of tax free items|tax free items (%1 pcs.)| |LAB_TAX_REDUCED_ITEMS|Label for the amount of tax reduced items|items with reduced tax of %1% (%2 pcs.)| |LAB_TAX_FULL_ITEMS|Label for the amount of full tax items|No label: items with full tax of %1% (%2 pcs.)| |LAB_PAGE |Printed on the document |Page| |LAB_PAGE_OF |Printed on the document |of| |LAB_DOC_NO |Printed on the document |Document No.| |LAB_DATE |Printed on the document |Date| |LAB_PROJECT |Printed on the document |Project| |LAB_CUST_ID |Printed on the document |Customer Id| |LAB_CURRENCY_SIGN |Printed on the document |the currency symbol| kraft-0.97/reports/contrib/000077500000000000000000000000001410616450300157165ustar00rootroot00000000000000kraft-0.97/reports/contrib/kfg/000077500000000000000000000000001410616450300164655ustar00rootroot00000000000000kraft-0.97/reports/contrib/kfg/README000066400000000000000000000001671410616450300173510ustar00rootroot00000000000000A very plain output document with position texts over the whole line and prices below. To be printed on company paper. kraft-0.97/reports/contrib/kfg/invoice.trml000066400000000000000000000133771410616450300210340ustar00rootroot00000000000000 blockAlignment start="0,0" stop="3,0" value="RIGHT" />

            {{ADDRESS}}
            {{DOCTYPE}} Nr. {{DOCID}} {{SALUT}} {{PRETEXT}} {{#POSITIONS}} {{POS_NUMBER}}. {{POS_TEXT}} {{POS_AMOUNT}} {{POS_UNIT}} je {{POS_UNITPRICE}} {{POS_TOTAL}} {{/POSITIONS}} Netto {{NETTOSUM}} {{#SECTION_REDUCED_TAX}} +{{REDUCED_TAX}}% MwSt. {{REDUCED_TAX_SUM}} {{/SECTION_REDUCED_TAX}} {{#SECTION_FULL_TAX}} +{{FULL_TAX}}% MwSt. {{FULL_TAX_SUM}} {{/SECTION_FULL_TAX}} Gesamt {{BRUTTOSUM}} {{#SPECIAL_POS}} Bitte beachten Sie: Dieses Angebot enthält {{COUNT}} in Schrägschrift gedruckte Alternativ- oder Bedarfsposten. Diese sind in der Endsumme nicht enthalten. {{/SPECIAL_POS}} {{POSTTEXT}} {{GOODBYE}}
            kraft-0.97/reports/delivery_receipt.trml000066400000000000000000000142451410616450300205220ustar00rootroot00000000000000

            {{ADDRESS}}
            {{DATE}}

            {{DOCTYPE}} {{LAB_NO_SHORT}} {{DOCID}}

            {{SALUT}} {{PRETEXT}} {{LAB_NO_SHORT}} {{LAB_ITEM}} {{LAB_QUANTITY_SHORT}} {{LAB_UNIT}} {{#POSITIONS}} {{POS_NUMBER}}. {{POS_TEXT}} {{POS_AMOUNT}} {{POS_UNIT}} {{/POSITIONS}} {{POSTTEXT}} {{GOODBYE}}
            kraft-0.97/reports/invoice.gtmpl000066400000000000000000000121051410616450300167560ustar00rootroot00000000000000 Kraft Document {% autoescape off %}

            {{ me.ORGANISATION }} - {{ me.STREET }} - {{ me.POSTCODE }} {{ me.LOCALITY }}

            {{ doc.address }}
            {{ label.DOC_NO }}
            {{ doc.ident }}
            {{ label.DATE }}
            {{ doc.dateStr }}
            {% if doc.projectLabel %}
            {{ label.PROJECT }}
            {{ doc.projectLabel }}
            {% endif %}

            {{ doc.docType }} {{ doc.ident }}

            {{ doc.salut }}

            Foofoo
            {{ doc.preTextHtml|safe }}

            {% for item in doc.items %} {% endfor %}
            {{ label.NO_SHORT }} {{ label.ITEM }} {{ label.QUANTITY_SHORT }} {{ label.UNIT }} {{ label.PRICE }} {{ label.SUM }}
            {{ item.itemNumber }}. {% if item.kind == 'Alternative' or item.kind == 'Demand' %} {% endif %} {{ item.htmlText }} {% if item.kind == 'Alternative' or item.kind == 'Demand' %} {% endif %} {{ item.amount }} {{ item.unit }} {{ item.unitPrice }} {{ item.nettoPrice }} {% if doc.hasIndividualTaxation %} {{ item.taxMarker }}  {% endif %}
            {% if doc.hasIndividualTaxation %} {% else %} {% endif %}
            {{ label.NET }} {{ doc.nettoSumStr }}
            {{ doc.taxMarkerReduced }}  +{{ doc.reducedTaxPercentStr }}% {{ label.VAT }} {{ doc.reducedTaxSumStr }}
            {{ doc.taxMarkerFull }}  +{{ doc.fullTaxPercentStr }}% {{ label.VAT }} {{ doc.fullTaxSumStr }}
            +{{ doc.taxPercentStr }}% {{ label.VAT }} {{ doc.taxSumStr }}
            {{ label.SUM }} {{ doc.bruttoSumStr }}

            {{ doc.postTextHtml|safe }}

            {{ doc.goodbye }}

            {% endautoescape %} kraft-0.97/reports/invoice.trml000066400000000000000000000200601410616450300166100ustar00rootroot00000000000000

            {{ADDRESS}}
            {{DATE}}

            {{DOCTYPE}} {{LAB_NR_SHORT}} {{DOCID}}

            {{SALUT}} {{PRETEXT}} {{LAB_NO_SHORT}} {{LAB_ITEM}} {{LAB_QUANTITY_SHORT}} {{LAB_UNIT}} {{LAB_PRICE}} {{LAB_SUM}} {{#POSITIONS}} {{POS_NUMBER}}. {{POS_TEXT}} {{POS_AMOUNT}} {{POS_UNIT}} {{POS_UNITPRICE}} {{POS_TOTAL}} {{#TAX_FREE}} a) {{/TAX_FREE}} {{#REDUCED_TAX}} b) {{/REDUCED_TAX}} {{#FULL_TAX}} {{/FULL_TAX}} {{/POSITIONS}} {{LAB_NET}} {{NETTOSUM}} {{#SECTION_REDUCED_TAX}} +{{REDUCED_TAX}}% {{LAB_VAT}} {{REDUCED_TAX_SUM}} {{/SECTION_REDUCED_TAX}} {{#SECTION_FULL_TAX}} +{{FULL_TAX}}% {{LAB_VAT}} {{FULL_TAX_SUM}} {{/SECTION_FULL_TAX}} {{LAB_SUM}} {{BRUTTOSUM}} {{#TAX_FREE_ITEMS}} a)  {{LAB_TAX_FREE_ITEMS}} {{/TAX_FREE_ITEMS}} {{#REDUCED_TAX_ITEMS}} b)  {{LAB_TAX_REDUCED_ITEMS}} {{/REDUCED_TAX_ITEMS}} {{#FULL_TAX_ITEMS}} {{LAB_TAX_FULL_ITEMS}} {{/FULL_TAX_ITEMS}} {{#SPECIAL_POS}} {{LAB_SPECIAL_ITEMS}} {{/SPECIAL_POS}} {{POSTTEXT}} {{GOODBYE}}
            kraft-0.97/reports/invoice_kfg.gtmpl000066400000000000000000000143211410616450300176070ustar00rootroot00000000000000 Kraft Document {% autoescape off %}

            {{ me.ORGANISATION }} - {{ me.STREET }} - {{ me.POSTCODE }} {{ me.LOCALITY }}

            {{ doc.address }}
            {{ label.DOC_NO }}
            {{ doc.ident }}
            {{ label.DATE }}
            {{ doc.dateStr }}
            {% if doc.projectLabel %}
            {{ label.PROJECT }}
            {{ doc.projectLabel }}
            {% endif %}

            {{ doc.docType }} {{ doc.ident }}

            {{ doc.salut }}

            {{ doc.preTextHtml|safe }}

            {% for item in doc.items %}
            {{ item.itemNumber }}. {% if item.kind == 'Alternative' or item.kind == 'Demand' %} {% endif %} {{ item.htmlText }} {% if item.kind == 'Alternative' or item.kind == 'Demand' %} {% endif %}
            {{ item.amount }}  {{ item.unit }} {{ item.unitPrice }} {{ item.nettoPrice }} {% if doc.hasIndividualTaxation %} {{ item.taxMarker }}  {% endif %}
            {% endfor %} {% if doc.hasIndividualTaxation %} {% else %} {% endif %}
            {{ label.NET }} {{ doc.nettoSumStr }}
            {{ doc.taxMarkerReduced }}  +{{ doc.reducedTaxPercentStr }}% {{ label.VAT }} {{ doc.reducedTaxSumStr }}
            {{ doc.taxMarkerFull }}  +{{ doc.fullTaxPercentStr }}% {{ label.VAT }} {{ doc.fullTaxSumStr }}
            +{{ doc.taxPercentStr }}% {{ label.VAT }} {{ doc.taxSumStr }}
            {{ label.SUM }} {{ doc.bruttoSumStr }}

            {{ doc.postTextHtml|safe }}

            {{ doc.goodbye }}

            {% endautoescape %} kraft-0.97/reports/kraft.css000066400000000000000000000055611410616450300161060ustar00rootroot00000000000000 html { color: #14213d; font-family: Oxygen; font-size: 10pt; line-height: 1.6; } html body { margin: 0; padding-top: 2mm; } html h1 { color: #2f590a; font-family: Roboto; font-size: 14pt; margin: 0; } html address { font-style: normal; white-space: pre-line; margin-bottom: 9mm; height: 40mm; border: solid 0px; } html aside { display: flex; margin: 2em 0 4em; } html aside address { font-style: normal; white-space: pre-line; } html aside address#to { color: #a9a; flex: 1; } html aside address#from { text-align: right; } /* * A absolute positioned block to display document extra info * on the right top of the document. */ html dl { text-align: right; position: absolute; right: 0; top: 8mm; } html dl dt, html dl dd { display: inline; margin: 0; } html dl dt { color: #a9a; } html dl dt::before { content: ''; display: block; } html dl dt::after { content: ':'; } sup { position: relative; vertical-align: baseline; top: -0.4em; } /* */ html p#letterheader { font-size: 8pt; text-decoration: underline; } html p.entrytext { } html p.bottomtext { } html p.goodbye { } /* * Item-Table * formats the item list. Each item is a line. */ html table#items { border-collapse: collapse; width: 100%; table-layout: fixed; } /* * Table header. It is repeated on every page. */ html table th { border-bottom: .2mm solid #555; color: #555; font-size: 10pt; font-weight: 400; padding-bottom: .25cm; text-transform: uppercase; } html table td { vertical-align: top; padding-top: 6mm; // to debug: border: 1px solid black; } .col-center { text-align: center; } .col-right { text-align: right; } .col-left { text-align: left; } // The columns of the items table .col-No { width: 5%; } .col-Text { width: 50%; } .col-Amount { width: 8%; padding-right:0.8em; } .col-Unit { width: 10%; } .col-SPrice { width: 11%; } .col-Sum { width: 14%; color: #2f590a; font-weight: bold; } /* * The table to display the document sum */ html table#total { border-collapse: collapse; width: 40%; table-layout: fixed; text-align: right; bottom: 0; font-size: 10pt; /* The following margins pull the whole box to the right */ margin-right: 0px; margin-left: auto; margin-bottom: 12mm; } html table#total td { padding-top: 1mm; text-align: right; } .col-TotalSum { color: #2f590a; font-weight: bold; } /* the lines above the sums */ html table#total tr:nth-of-type(1) { border-top: solid 1px; } html table#total tr:last-of-type { border-top: solid 2px; } kraft-0.97/reports/kraft_kfg.css000066400000000000000000000073161410616450300167350ustar00rootroot00000000000000 @charset "UTF-8"; /* Use google web fonts */ @import url("https://fonts.googleapis.com/css?family=Pacifico|Open+Sans+Pro:400,700"); @import url("https://fonts.googleapis.com/css?family=Roboto"); @import url("https://fonts.googleapis.com/css?family=Oxygen"); @import url("https://fonts.googleapis.com/css2?family=Raleway"); @import url("https://fonts.googleapis.com/css2?family=Inconsolata:wght@700"); /* General page settings for print */ @page { size: A4; margin-left: 2.4cm; margin-top:27mm; margin-right: 1.6cm; margin-bottom: 2cm; color: #2f590a; } html { font-family: 'Oxygen'; font-size: 10pt; line-height: 1.6; } html body { margin: 0; padding-top: 2mm; } html h1 { color: #2f590a; font-family: Roboto; font-size: 14pt; margin: 0; } html address { font-style: normal; white-space: pre-line; margin-bottom: 9mm; height: 40mm; border: solid 0px; } html aside { display: flex; margin: 2em 0 4em; } html aside address { font-style: normal; white-space: pre-line; } html aside address#to { color: #a9a; flex: 1; } html aside address#from { text-align: right; } /* * A absolute positioned block to display document extra info * on the right top of the document. */ html dl { text-align: right; position: absolute; right: 0; top: 8mm; } html dl dt, html dl dd { display: inline; margin: 0; } html dl dt { color: #a9a; } html dl dt::before { content: ''; display: block; } html dl dt::after { content: ':'; } sup { position: relative; vertical-align: baseline; top: -0.4em; } img#logo { position: absolute; right: 0px; top: -150px; width: 178px; height: 191px; z-index: -1; } html p.letterheader { font-size: 8pt; text-decoration: underline; } html p.entrytext { } html p.bottomtext { } html p.goodbye { } /* * Item-Table * formats the item list. Each item is a line. */ html table.items { border-collapse: collapse; width: 16.8cm; table-layout: fixed; page-break-inside:avoid; margin-right: 0px; margin-left: 0px; } html table tr { page-break-inside:avoid; page-break-after:auto; } /* * Table header. It is repeated on every page. */ html table th { border-bottom: .2mm solid #555; color: #555; font-size: 10pt; font-weight: 400; padding-bottom: .25cm; text-transform: uppercase; } html table td { vertical-align: top; padding: 0px; padding-top: 3mm; } html table tr.secondline td { padding-top: 1mm; } .col-center { text-align: center; } .col-right { text-align: right; } .col-left { text-align: left; } /* The columns of the items table */ .col-No { width: 6%; } .col-Text { width: 75%; } .col-Amount { width: 52%; } .col-Unit { width: 12%; } .col-SPrice { width: 15%; } .col-Sum { width: 15%; color: #2f590a; font-weight: bold; } .col-total-first { width: 60%; } .col-total-mid { width: 20%; } .col-total-last { width: 20%; } /* * The table to display the document sum */ html table#total { border-collapse: collapse; width: 16.8cm; table-layout: fixed; text-align: right; bottom: 0; font-size: 10pt; /* The following margins pull the whole box to the right */ margin-right: 0px; margin-left: 0px; margin-bottom: 12mm; margin-top: 8mm; } html table#total td { padding-top: 1mm; text-align: right; } .col-total-line1 { border-top: solid black 1px; } .col-total-line2 { border-top: solid black 2px; } kraft-0.97/reports/offer_no_prices.trml000066400000000000000000000155451410616450300203320ustar00rootroot00000000000000 {{ADDRESS}} {{DATE}}

            {{DOCTYPE}} {{LAB_NO_SHORT}} {{DOCID}}

            {{SALUT}} {{PRETEXT}} {{LAB_NO_SHORT}} {{LAB_ITEM}} {{LAB_QUANTITY_SHORT}} {{#POSITIONS}} {{POS_NUMBER}}. {{POS_TEXT}} {{POS_AMOUNT}} {{POS_UNIT}} {{/POSITIONS}} {{#SPECIAL_POS}} {{LAB_SPECIAL_ITEMS}} {{/SPECIAL_POS}} {{POSTTEXT}} {{GOODBYE}}
            kraft-0.97/reports/pics/000077500000000000000000000000001410616450300152145ustar00rootroot00000000000000kraft-0.97/reports/pics/CMakeLists.txt000066400000000000000000000005021410616450300177510ustar00rootroot00000000000000########### install files ############### install(FILES lurchie.png DESTINATION ${DATA_INSTALL_DIR}/kraft/reports/images) install(FILES postit.png kraft_customer.png DESTINATION ${DATA_INSTALL_DIR}/kraft/reports/images/docdigestdetailview) install(FILES identity.png DESTINATION ${DATA_INSTALL_DIR}/kraft/reports/images) kraft-0.97/reports/pics/identity.png000066400000000000000000000110411410616450300175500ustar00rootroot00000000000000PNG  IHDR@@iqbKGD pHYs^tIME"eIDATx[ p\y}hw%$Kö0D2҄Li!S:f̄$t<BhIII@ cclecY-[kjuN9f%M̶>w 1~0_ ?{h|{ڎesQjO,OϑuF$lb!''z$Rbo w"[(~1JM0%ib$JB$g mƇW]|$=zE0Up )!['^Ň~z:[b&vaAM* bA4(eP ZqM-\#k8(դ0_vRJ:>>qz xV(h,"X|džnÇg[^0nMUq0 u-XrH'w 0H13~J_|JxX b)UQ,L<K Q_ROH!dMڏ 3sgy'=9)[~!2?&3ԈX}j̛T­{VOgQ$ !t=ӆ$RgE^F2Y榙 )#e㡄׭ Rhݺ Ľ\׹CJѱ #~-F[ rcYg3r$۶n~޿}-p~: 0g(5tD2:=Ncg5!}v^QeO^J$ (/WLOx' %b,^4Ԍy6ο:ԃ uIZŪqDטqUA):y$]t.|aX=+}{a7^~[i?ȻTpB0K#f^r^|;_r⭊bA,tw5 JGAE.ahvxqO[b̸*nѺS+S 0ˡw8Y _Iϼ HUUe --QE-g1w08E`qUEG>f4y4Gdr๶O ^(򺚫!@[ۜSNBJEc`zL_1];5`?$O Sng}ϬZ=?8m枿Κ=wAi"JES b3SهsPP?s|)jƯT+Fw 2BUH 4V9(a/0@ab C}֕B᪄D:]Ega}/\0a'=B44PJ3' D}KLUilH)!Q>If:M TwRHsdw-Xk;/1-$rl֭u9PJn`0J,53H/Fˎgx5 -mHgϚ=($(oU!{T+;<>utϥShmmCSKJ!vEUԀIdƽcc[(@gg'hpgh 뼦1Y@F6q R!<*T*dAa$T)t^3+HlLrX~ "jܵ~Ѯ7;R㑅DшSRU\O{('* U%?q9-[=ɲGFrouۡwv7}?`+~gtsc 甀sFS m^84>=60{#ǎV{3{kkҍ :g=!^ n) , -]sut #Z񈉈8ΌlTp+H._r pK/~L ;G. ^~f.oL.O^AQAze;qBd Ѱ" _nC\MP"BC= 5xE*4T{ws 5~$$ z05`U\^Bh.2R袤019*Be4˵@QEWf]p*\c2ܞ R攑kvA*&}BJBI-cZA@V0'y8 Q l0*&{bѹ“R? *-S⫰3 zy WLgn`۲tx;:C_ 7U24(S ۏ ޕma|oC vEquO摈GBHVNN!Y)(Q&{{Y[TT/Fy1jwWxkZΨK6V'"~gHc1Ţ"DX<}{ѓ1ʭ R&MX>wl(s(6Z43~[hUFXi'b"qV*$̩E#O¤S! J#,uLH +|+tKN'! [v'o{ R319Cx$s %UB /%%bBH FBA$vQ  PcSJr ,Y.i3{`شi-~lQn"qgpgq 5IENDB`kraft-0.97/reports/pics/kraft_customer.png000066400000000000000000000114401410616450300207520ustar00rootroot00000000000000PNG  IHDR00WtEXtSoftwareAdobe ImageReadyqe<IDATxZi]Y~wx;;N8iRph46 TЖPAS "AT@-E~T]( ޔiK8x6sG 1f sMpw<ව ꋏc~]` ю8QSuyh=R[=3`;6LY< W3 }<<_4d, ~?e^!]hi;2Fɑ4Lv729/֔,{a<'{;l+dz}:sdǂ0XVV+ô_mdO XӴˤ]-)3U`Q?+eOM^4%@7ip9<ݹGN{_ ĽKjٖ-"K;ô]:!ycFXyGEᑇnŁr o=VTݿ^z= ɛb$ftc?:d7l?sMC?KLDy׆\HBE(v:]ty;?{|6[q YSs]سK.Y• j6i92ꦇ=b lu+*dLF-TDwA:9TVѢ#ׁ{M bdD8_ysWuA\^lm9T6T6^=҉/:4OV{Ư i Aԏ&2@u XFͶD9 sþad6q~zuJIE# dFرs|R Kg:_AU& l07%Kz}|\]vhP0 oFʏ"td_;KK l7>V*-,5 窛ݡ8Jwt ˎ򐉁9Cm:)^zfBρ/kvM`6,ǁC5<ze(+ aY$2ܨ t]3:EL2K}epw+z>1NAo9t V]?ҷ/4;v abba26p@ NEq8y-7XD1Z (3AR ڍv>|^$F$6L]3{ʥ܁ geF th"y!f*X}w '=YXvqۑ]8zx'cn`!ɖn (-~$>U7K*hWMJmt5 e rb)9X 'jBR*#v,^qxiEJؐfK8VEx,J l.r Ik$mIB֙UmCϹkdߊ$8 y4  :B ZBu ( NT7XW]U 0`|8b0 U3f4 J[v|C{K)0(ӑ׽M1q1o,/1384sIKx)Y&>9<p| ftזt |Hō(1.,n/ [~tjT^^ax8԰uOWuF'عmxj~gq2;i:mvhA&>c<с\.3e0dK5 WdND,k}F?YF[N%CFet#)5|?jy5U 3K8 4:8h1CN_Bg=U<9d{x<5F,t{L6sbe"u`3ņsnFWc"vSp9#) n–ϠRbtg|Wkx1ߦ,a{"e%ք\L=V*f9Q9hӡ@#e gbqE|6-ߏ*f)|I+'!f$Rwގѹ#_{fm]3BkVkr54dRgFulx!x<[^_|_Bg #Mv-d9 RpJ4 =q\=C]{|-1G cR0Xkx2W~X Y\xMЉ<(a,[}oib03zTC”c2"wrɏ@E+D(g#2?s;8kP퀑v8#C>!Cٗ x A~/$9fh}YKae*2c *i! ~< \3~K}yTIĖ7?73ͫ/)l qLOa3į+QŽq%(Ρ,Xܨh$Xo'oY:y`M7+w,Nݍ0nB7Iﻟ`xI5'$^{9ŻIB/TqH&MsIL|s0 34ʵDqWQaYCXrz_1>gkfQL$0_G'7XFpMXWq.t}{OlYC1ìW;ǵlN89JaR* &GʸkrÅTrTƔSrHt,,.+Qcj$*a/=S.h=p>8)n,CF ohቸĕ_۞$ ö09>ZRer$\2+MsjVP6Ռ"QGQB |ȽX;x/^\_?}_C @/lp\/:fH9Rh4=>Dof⻭\QJ>5YYp]qF1iI Ѧ9 LyeҒ I$~Ze<<6!$NHH7T$F"L% <`&'$S7r-5ic;K"QP+xK[mjI$,xRݓj2x D}A&b]b qh<hG A3DyME?z],bHL !wv$-(Lx=Q-aj֦:{Gл6>RCD9q3fRž4yD Wg{&۝q$5~7WE^V7HG_Qճ-beOo,t,JZKk-it=xtSuA4g c {O_Z+FD;ݒ+YMo]][mf;.SE|@E[2HhEH"wVd$ GIK1իu]{ĺT&x#Qa-U/peYyB^ȴ;pzSW>0D`;@0cYdSR#&ힰMJ->SoE| 0ۉ%mϏ~/G|2S7L-+EO32ENm2$-HcV%V4A8J]Cz?s?|pZ3:arxI/$p#K P·3 F8`&-!eDUE%tk><}/3o` V7nIENDB`kraft-0.97/reports/pics/lurchie.png000066400000000000000000000533771410616450300173740ustar00rootroot00000000000000PNG  IHDRZbKGD pHYs  tIME  1$FtEXtCommentCreated with The GIMPd%n IDATxwT?Sԝ3ۗ, (bc%%`K$}_[$FE~ĈѨ(T:R3Svꙙ9K3}]s̩﹟!9I.$$$$䀜䀜䀜䀜ss菴RPPpԓ|ᇜ~߉꫑e ,ȡdje$ >3{9u۴i830L;3 XD"A}}=q'h'{۷x<`b;b#L&24 N3sNr@INr@INr@I)F`ɒ%\}՜r)X,t:L0'|H$u޽{̔)S9r$ݻwjy߿?iƋ/H:>}k?Պhd̝;Y;vp饗xjqeyf|#:{oL&$Iw̘1mv3uTFM=Z!7o#Fd2a63f ;o_nW\q%%%t:+زeK}7nM7Ĵi3f ={$//O=/z ƍ2Y*++ ,bK.v]?s-O?8WVV&M6~"u@TWWUZZ*ʲ_q"NwT*%zuSO=U\~bРA3f{>ݴi%%%?>|B O?0 BՊkV̟?_̛7OL2EfիW_oQL:5k\f͚bĉB@\~BNo~^z?OW_zjB׋_|1똹sf]x>px@O`ObΜ9B V{;yda0ľ}:$/)gϞ-joOź~m&Y`z^_B @s= v*^oֶ{NM+WT㢹YرCXVګVF`t',ѥKN7663fLה>ϔ)S (1mڴN1}b6Eiii?N3<ۧ^opbQ)I76nguVzؿ}gw:Õ @1r)>ߟvg@Lq%%%nO5sxg;ݧS^^-[@s="t{}f}_B=M:3qDu_[gB,]TTϘ":NN=co~ Q[[e͞=[444@^vX|y6|ņ :w2vUUUJ{ /Pݶw^;v&&MR[V1~xqw tzL :ՈDB|bժUYۿq>f? ΀I$ÇW}x/ϒ|@?~{Y/.1f~jcy iB<TE_QQqge_ :T,[,$ѳgϯqK$L&};^c{GO?| G.?]2i$6mԩSYh>X%LƯׯ۷oGe„ X,6lsᣏ>T*u\uV=ش'Cuڕ;vs-Æ 9oL#կB͛;,< .T{F4֭[;e 7ܐeu2%6QF߽9|Xg?Z'v2$IF~…\saF|8{ݺu; ˖-SP@BuL0TnA#SQYu]'h:PQ/tYNeĉ(?5О&6mz㳨*/f̘!裏~HVUYZ*ʢ_:lG?Qy=oȑHD̞=[<4ԤIc=&?ٳg{LL>] 0e?ϸqu]'LK.gyfVcر_uuubҥ*19s戚 B1gu$/_.0s=WgĈ+C)",GH̚5K,\P,\PL>] 1bƵ5Dyyݻ$IꫯV3nJ@΀o߾+FҸd~8t"~.L/,&)Ht^xq鵾V 755r_|5"??_Y~M7ݤTWWc N'<ˏx;C!wq֬Y =sbȑd2 ^/ $̙#tփ|?k4V#A zׯ5آE\ <h4jcNJ_~ôUǭfԾpB1x`bƍ .<3JĂ ɓT&Ə/^yh 8,~TUUg4Μ$}nO8/&.B P`f:엑t:M2Dբ)U}41J3%g#"%T*LJ*dHL a40GmF/ضmW]u Gys=CYYY-ٻw/wz{o`0,'$v.xӷݻUе;=O;ߝuxeڵvi;E)䣀7T*[vj֮[GϲH9%\0H6)jc6FSh)t~ǜ>wٌ1}*U` !HR4{Xr)䎒Uz VN 䃕+X8o;o󪠕LD+$|^!Ҝ>MK (RCLa1L9mX, ߏ5uX8X| hhDD aЂBIG"h)4rM:{xV:6KBvI:$R6=h4hi 1L˜1DUP_4mM 8X|EE_J9׬e |jvVA v )mۆΞ8{~[zx}^f?$s_z YN&4EʦGqaEӾGSOEqTA<gMk,~m|>/IhBIk}6v{'q,T˖Ss7r33 | 9[_UWܟ¶U{:pcXŵcm>9jC_Xb$:ݱjs!yߩX쉓#vOBWpۭF phĺ=YK.d^nbE``ϝwߍh"1vpbY=~y4D[+ eXXos^fse|!6P٧_8sw}}ƿoğའk.؏6ƕW_SV3Ͼ,g,$RiǟE+ټn=;gW/]Bc_#w-?R.(%QbF.2 %1p?'R|j06|SN}lƴ/LIDhA =4 oq߃R4^cI3q=|ޔ֮QMeORYײF5z`³$D E]eu v}a۷u"FBF س'ﯪ=>g!ڂB!5Qǀ6BN!rO*Lʥ%J\ҐُzDtZn& =I[ h+3[xO0 s@>exGN-#mQsu)NxPZ!?V=yOW$@ Zh#u!eй\EjN>/SFðÙuf3V}e|!V~*K`p߽%^jҮhc) ֐3qǝQE$ xIZa?`h(Jwx EjI3|r D`׊Fє2pfl4c T%N %ߎo]5)˫"3m,or JT3[7nwg %!K=~fN(ZAl )nycTAKަ %BÕH ^w9ԸPߜPs"vۃ:&" U^58M̌1rJh) uJ[Ң#mҶ FVv@RbZyʫQؙulz/JTJqۭټiSN#HR;dNjr/1cqE4s]s){jzpCѾtw@T&Y#U`J`C}LI\rpRNh]Ƈ6\ 6ģd'q.)p1M̛2iIExâ~rUƿֽ{~/6^\Vn>ݑ"5ujkf Zw(ZHT]$E޶I(J2myo]8eO+8 0փ`G^=/?Ӂ@J6+y~v;ڕx/(Ÿ?BZҲ+T[|ͧAC_JgAZx= Kəphº=)3S0=,s_x^MI5hcMyi>H Z͓VW֭~RN J%80rJXB$*Cj+c^Ciя>vm%ߣ!F`\vc=oBHS`֪ZVb%pҿf\y͏;h?~yr'= Ι^XyWE{8e5RÕKl5nbV0`݊`MZ4ohtim"yO+>v;ĺX*S"ǎ::tyZwdi$y2 L(BO`TݭI: M:dD!a ѵ:b?LjnFȤ:ϿI@bn.b-_[6p~.~o_oPY;Nծ=8t$)^ (#)6[8yS"0ʍcD9RbgىwJ  P58/rJ$~%>4U$rI=ߌn宻gu>۶kL79՜ڵzRe6r2wgmҮ$Ձi_]4MH_;ff~EB5U P)RUOM$]jp(T+.VZH ĺYI%,;Chci~Ӿ8Isep;?WW^~w~>[kja2(҅R6SMm>iKRZa]O2߈yoc$^fT&NH_;@F-)}(IxS.(SZ Z MhI u1Z)&<ȉ/'4iSIC`yh4=V|JxԧZi m7^տL4g!Y]N? $"\3'u܎5J8e|!7N^[nLBV DO&Z&22-Vm] AKh;uHM1~2#¼+vIED10m!VPEYX7V@ŕ}kź(ǴIuH;]&푇or9KZ +mGyG5N/f݃PQ)5x,o:sRs7nR6=ee;MݷnIDR6#qMU*I6UA5BgGI))4c=6SyR2dcܒ]!5h>p?yZp~H'*][ osU} eg…N+'0փ&k}mhX#Rii݊{֯GVc$ΆkM'^Q]!aΙ<3aX<3~ES&ynbBԦt8W4&|cZBI[[͇^(p?;vh<{eZO$4ds3 E"#ԗ@ѐtе&I;u7'p~Hт}mh;ڛztI \+L?x^xŌ9tlZq$-:?s@W祵tv{{;8(eἼ}1od^"R"kx cC} `؂S#|ߡjH-2JΩC"TFfS a5z IDAT1bJʬ^_ݝOShM-rbc\wXJd4܇$p-k$ejn.[ |# 瑴s`Vx-e;JPc2Dt qXEH# Z Bӟ~!."vA8kרBIZ:1CH,B4[L=T0U1*=)&1>vlZn "mlk%QKt(94ĥ`2/6Z"iN-Cj~C²+ȾC8}ɓy70D sagrA ȋ_bh,'H~zSm\s|$(;mg"f֭~(6pJҦw}M: C]cm c}KFߜjƇqoXu$J7!KUۆ ʺUqh*+^j"^f&Q`$QbVhU^[Ļ[I.:HhJAJbӊcerS3G"i%0.e+m"nKpޅgz̿Æ13y qbW2ᬉ'V>ix[6租"S N߈%8nNg5_[AGr *)zc$݊&)CC La o}kxF dX4S  *'B!V: 4$,XĻX0414Fy8iT0@ANRσDOtp2r0$ yb1nVZ1b+{ʂhP%E?Ya#FvnFCAaBh([pWsퟋ,' q)Z:ѣ;cE&e@l+Z>0ƣ +- qahT8]TԞu{@RB̌ugP1[{}=~xɔ#?hA4 RiC,}kuH$់/5(2a٩^ye}}%[T(6#5({sC$4 uxnsVi0c *y c<!Ri#Kڛza )6~W&KZ5#$xެXw^HO[QY/J%oSE1vN^y ?ޕX xiWqS@JcWbV +P4oi}KEO26`rJ9Awj|2ژֶ#\i7Π% #) TDe ok^ +X Q&~kа */AvnG+v]_7dgUM\cwwt:~>v.XubƻXWcOOt*)DhDwv_De2ӸgQj$ͪgӚrhci}80W rcݪ# I:5>BmEiBI?ka؂ZQ̤F~گC|Ht}/;XuaxFt$ +A6mnr9Jv:"yI:ڷw/>PpiN{MjVLecǼ38glYp~e}Kxw+MrfK4oNd1h4 -&x8HU݉jEM/6a"iK㴲IދwJ !574O/cƎ=2y6C]X7[zM 9 7QWsǃ^s_{9nlu80 *SvRN֡m!e{ZGUjKFCڨp)_FW7yGMXل==oAJkW5b66/3YTC oV^FeRvŔ LG_ֈmcX5/38x iG aF I1mKM], Ox 3>TF?h.wY1r] 06Ui>.Ac>Z:el[Їdm 4uZ&abR#uJCB$唈TڱSZU\`RmЈ|a5ýXYmcyR !H: /6cǺOD G{8@[8ŻXr3B\n@+h I n yU k^Č-1r% .y1k+H:$a݊6D*$4"RS aЪ= S^S5pF_&oIDz۰ ;Du@Y#oC JTAj t4I: xcbR5CEr"v&i>dR _Ϸ0㦟2l`>YV6.(fAlIwM:$gY짟"K^/@v:Ƣ6Fó2˻Cѥ{wv?ΣjnJu6 n%ցN{Zȇm6B.0aGF-Vz\g'4D-UyaMq/ýX XۺI[tSVE㶙0)gVf_&^bRAbU"ݭ9@ѼJ3y\?pu:ހd@ZRAk(D~~~# TuZ.R6m_W3y w0 *Uy*핧:tx >%p2I8C.4oI(v(2aݮDۿ%-:"m)|5M~Nn$eӣ % YTj:%lܲǨfEH:#ĻXõ`XC7`JiI 6hU ̦i}PeBRR,4^'Ϯj̊-2"부9wZ9N Mq#QعyZrnFV(i8?hT/S>[YOߜ@.2OI˺!$ڢDՀF0(]39eӀqoX˝y9{$Ǐ,<X85̱ZD[0mѫ:q~PrRPZź#>UPوw$ۀmh/6r$cW-BIjIk^mTCRCLu-p~BBf?8D[ ;&VUJiײ&%5' D+ ZV~18#&YuP3`kG>^rStdP_Ϧv]!Cj2˺a&N]^7A#f t:إ0mZUjQ6h7IAm|hPs3^֭~ui Oi"0ڍ\dR uöwROYT6u-ؗ_}\TPR_C# X$EG>nq#zP2܌1JWhjrN{GS7K]P&Jm$mrE>|vv\+1WFevrvW2Kԩ 5rZ5Kڤt^1U,yb#$x{J:}; (֭~L(6#)VC_vnE7 5'FSӕ 8"kUHژ޶[ض)M^..ǵ1Ιi&z#dP^Wqh$\iCIei~tfx`[߂u{f♿v6R.0푗(@T`.gg钷:r2Is!EIST>mR{:>j"P|]M~$C_E2A47PWsXJJK XViWRtlK-h4ȅ&l[-2^yJ -E&cIE*R4?GI^XJzgg믪q1 #5UdBI@D=ŴcOaEz==VϯGRTT@UH }@d9z\EmSuANSScS}qip{0HfzOY @De8^cПauĺZh>DKM*'I1W)]}q7wuzȝw͌ne!\+U@BI/:.rhT463#q$q}ՊXlR_.'"'{1W%-_b0EU$Bq9!۶mPW7+{"K0훊E|^Bj We^jz_8v.JeeiEkB2:M# >/s_xEG"Su+H`Xr@:G]$Κ՟b'gc_mBܹ|^u·$OhrEE9\ۃY&~z5EGϡKe1;[@0jp?]!1.2O lT@j5̸.?KIDATXSGRYE)I' !xϳG'͸'68ɤgϞ=c\㏜ F{!zmgPWr-op'.N: !8m^tmW.&^z6mpBoe)8.Zw_DmcmoNIiiߤd<~ n.d9o @ oɞ/ i2P)XcaO 2@*}$,1|V!6oчm!\<0|^IJ#YO ZVE~x9cN?ۆ̻BFp^[۔'N@vOBaVQnf6nӟBYy9'%&NƛL{&ǟjbH8JJ) lk{hZ?mE !sKD+Uʌ8L\T\#e֓6)/R\smmپÐ6b1S+Zw^ P$-k9x͘j8Vy{b|>/ϸo3ICi_~mN'*W?***Nj |'jy睜=qu>D+xPs7\o g}@P]iTSW~BF@@d*QVq©ũVZVZ.mjֶW+ZjUB TpVrϾs} ;Y3ܫ<&ٕ>ޜs6> _oFސR|9qgX+yb"D\\.GsM >л !=^IF9ƌ{/_QJKf{{|m;BvKPqe4pҥͰT.{kuޚ:!wBXmMdpJ󇽐v\즛Ph1cTu33[{-,zۣ Sh4/fM_h}ᜢRė_C,7 %K`ڹaF3;*Oer,W=O)~?_xKꊒDz^j.JfY# HO)?7o DºlD"neyP2=*}v[fYh%|̖5@E 'fL( ١bH21|H|͑&M |}}'  4e, A+2>ðe4TXas? (UJ|j&M|BZC7x:xx'ڼ1'l6IdQzRgglf͎S!Pn;JE>[1 O-ؗ$W2qPӧL@·[i/F雾 B|.Z}_2ލ]bCb7Jq(vh¯IR)qw*8QAzJ >_C`ER?~ 5-|v&|Yh4ph27|s08pQ1]Y\BQ 7nCai"@EE*\Ƽsޫ._sI=f6zX`т ~̩Ψ>qJ:q nS^JRF K+ƈ#F5`/#GO|TWWc˖؞*dyp+%<4p>dRʻ7NWGͽUZ"h58{￷Uv( odFV 1 .h46~ݏwĵ|NPB&_QsI 11y mnDrssбc'؍<|=yq7)ԁ. W*a#d27!!c0xh0[ΎÐDQR?[ z#)ɞ 8/QIxϓљx.: {w! DRA\ΆJdX\`\pɒf3K'`@z~<.F x\h;K[qvė*|ݺwG=л7|<qkr40sBZhυL b.+Cu)4V{ϗDx5o28ˍUwuB 3z?+٢KJJ !uqkZm-~?r[7YYжZ.Am{GUp^` zT!QjjV aZ.O w2@.d/`1Ȏ)S6@tJۯ'zzH(b-`M&*~×^h.Ny@|8z@Q J kj@Vu>PskYy'2 nj,7o>3cZ~5yڛ4-1[k^2f F"m`ȏ[ґ|Μ=BTJF)zW @zK ƿՁԁ6 3Agk|]vmzX,F||`z =ΰ)22 \C.בٱcEFFRhh(#s_۷of-b @!!!ԡC@u߰a 3gO;w1cH$tF]QQˋc֬Yd2sSSS),,BBB5L&-_PǎiƍO?ѼyΎx<}w͎-TWW7JdkiӦZ-ݹs\BbIII!֜nݺ|nZh4>}D~D7oӗ@ ۷o3myyyO ,YȞ?޵FϞ=~6oXJJJ=z<֜{=WAѣG3n64h49:`ܹ礤$#w^$3dȐ[W=CH$5 JKK)))ҥKx6Oe˖푉+h!!!lϽdãPM>7O6mv?=-~: ??I si"t:={|Vhj `$7yڨa4o֤+KgǏ23P(tNb_j 52^&ATBVC.PA6 V5ZvfS)((qȑF媫'~P^^&&&2!,AdddlȖ ^ "B\\C~Axw˗/odYPn̙O?Q#G`֬Y͊/m۶G5*wQdd>iIQEE͟?ݩUV̅ #&+uWwޥ Zn#c4??&kg} k7U{c޽{I"o555O_|EDDYb9r$ѦM(>>(::uVoز fAff&I$Ņr9 H$R* sδtRtT\\LJHHB*--%|hܸquC~~~ hlD***"Z\HDǎtTZZJNbdݻQ-_(/z6mh4uTҥ cRMM KEYYYq ˫wׯSxx8ye2M6rrr-X^ӧOcǎ5_aa5/Bcǎ%777p8$im6FS>EѢE(Z\Ҡf޵kL&cb@iǎM<Xxd,Y`̂Kd,X"`̂K&L@UU{#X"ظr O@!-- l͛7DGGCoUbAmgo'm JeeтCұcǘ k׮}7Rtt4JY\\l%r3ZG;wjDD{PQ^(77u=,[ aaa1cV\3rssѿp8_Æ ֮] G̞=qqq{.͛ŋcoKkgO^W6٦h'OAAAfMPHtiZbE999LdLL L&pq8R*4g裏ƍ@f͢d""hԨQMF?*bd<-??ʕ+̙CiеkW9sPjjj@ubsrrH*2M&]t\\\D #2N>>>BqqqԶm& `%##kZ^Ĕ)S(k1w.kZf /\xjkky\>}PXX;wCm8\nY GTTRM9$$6mddBLL gVB&APd2!!! .l!,,PFn ر#Y&NHWf[PTT/"" hٲe"##)//ƎKr DB>|84i$JOOo>&Ov[j 2e ͟?iϞ=9j#"RT4}tI&Jֳ`M ,X"` ,X"` ,XDf ,X<#?:bdIENDB`kraft-0.97/reports/pics/postit.png000066400000000000000000001217631410616450300172560ustar00rootroot00000000000000PNG  IHDR/f?sRGBbKGDC pHYs  tIME 9iTXtCommentCreated with GIMPd.e IDATx̽[k~U+UmmEK$`"`Po x^;/ x€7QB!\*BP/$[tөꪽy;~?ךk4ijac<9TPUW@U=?wիFڟU".F.?KKKUo zqPHAo C 0IPM}@ϵ>I]~-5#|υxS_5_APw;|G"3R\%jWaD (@#>?>g6}m ڧ)B?;ECH:|χ=O?߱U㧺3J1S}GN C5CMO=8oxՋKun ׵z)I?ҳ:_/~U8,\Sy=?[kLx9lk(E|ޕHZwk?ߴ he#^BÓ{5͟LoQ{ͮ`L @ &O'SZ1,@'@9w$]CP3kVIҟp#[ZťҬ @^<=_\`@^&wR^^ji+ɺKsѫ36^|0=^wx$p'=~w._?2H*$u]9 =ɋa(~-]恂~BR4?+ Tw=t}:A4ta0"P^UW]#xў\Ao xB.6Is=SZבjV iΆ9gpT碽 (X "NX+"Rc% cWzqo'h+( K,Nqvi+Hϵjd/ڡD􂼃T&TﵝӾ^5PR&H(P|o_>$ /Q;oG{_a pͨ:_u fW6 ϲuQB ɡba3.I0Nj-lfhtew/0I|FQ=L-4Lva`( /Ң`KӋgrLXkχt w`zLgX`!;q٥3+Ʈ#^ _Lwsm@`" a0]pWtߓA6ZP~<=<СS+UֱJ=htlfAˌN۔i HTg{mDU7;B ȩ{'ݣbA{;T{;}y+P?OF]TI=oO9{_<5OFY7[dU{]|vD|1O *W25PDZCPGNN Щh37Pk`޶;6DGE#J/eSO] yWP}X!mῈ6y^ٰ^Ǒ Prǹj}aVfP'᝵:9gN CvLi `޵V )N [sIPG5N`?.CEŤPMMps?@ĄfA$8m _8vVQp;{lIj.g( H9q*:l6qd "$>h˧xKO Hexܲ`s4rI %c|X TuI@U;PdLN_'2gTImOnL9q=Xt@jFԀ;G ;w+&`ߝGC'Fk5}e?/0[oB}&v #M?5ſ_!M`j$(F7? ~ǁ}- ŏٺ6"#}u*=Z-kc ׿3/n}s F EBK^>)SĹW9C^UୡFDW9 ڟء eZ]8VE n-81{Z~D;5q ׸}};eq|UB51,mQpzs Yw.ͯǏH}5/ \e63dؙ*Xf%7Z.m+`46FcvဍO@`:l`a3A'hOfU:xY~؇)8f9rDUjLɒXCL CtT,! hŎz08hJwc^״1D " I!W%%s*1d6H$1@:8 `  .P.Dlggg_(1ϴwJ0͟' fq,8Ƽ~]j6dlbϘ8vt(Pt%^5w-N_ɾ\و٦7Ta#KWJpCYû_bQ+Dd' k{-wOjKqsF5Ru.k]8> Lf?(ZD bq L ʂOF<#17m U` ^Ȃ4\l, SjEw`M̊R~F/U&P}` z_\'NΉ]v9"IDoX+m`T4tڠ`^0ӱ ~cI!dOR76T=9#& oɢ(a%bxSGo](RE!ٺ))cԺ^)l׍y4,!Y E7=a]#c?i|9?{k䍯c~305aJ,#O{!\FNe(* `ݠٟ%$|`h2Tf :A!Y15lkQ Pt>dz Ĵ " I/i Df3(:E9FJ!^L:KrΉ9ϘsdNc)-P]_zJ8pu@kʈ% dkI<- nGHNM :i'S ]hDlRI*jh E)ᇟտ}u-pz ]ϗ?IO<~i|Sg?x^mD~*~ccPT;Tx+AH HyGA~ J"g83蛢20.{î/bZpuCNjhumAftئ/}F>'쭷S"wcγqߍE D;j5(a*X N5Vsx dE7r^PPChL2rm_/IrmZ Z/Nk8fa¤J"b9%jd`cزY76`<>e~ W )>xɷ㯃V)/_w[-PbH1 Ħ(^Llj+OԷFf":As )k$?' O ޟM?@\n},_k%ܟϵ;WL *Ftޮm2''_J%Wv_2}pܪ 6^L!t+~TLkxDI)HZ Z8'pNZPpH"ې odP,N];qVmX-h7sǛN ٺ}ǁ _nZ2u+@&^&>z4 PLGrY( \);SP:m`gA}e(+\,RAPP,&h(rM,bmHg ?#1Wwή~Ɗ'~!pfZ9w}e ]vT"3b2"3Uw#G,%\bl;ZGд/𕕜.[#XDap8%Ic*DCƯyp܊7='!7S-} %8y3~om k`u?3G??^r(jf1_fe |xfǶ1fss ؕJ0ilVDsD8c l}+[,&:/E:cblw _cGmۖ$]t?H_+;C3_dhw:Ƚ Ve[f」-5I]ݺiM['n(KS!piP^^)r`xbrL!:[ HDJ zf{qH L cR(7BN7xS(XȋO4cEv]- ^ç^?xo~TWm!;0v m\ <V4VPԋT|['N l.ٲm;Cdk 0κ: $ԓ+lC)  02|„Tݯ7J[q2S^b;G1H㟮-iM$>5k:EGju$/!EVTǃWsm\H0$ ͸`w6̔< ;#J 6xQLҴ"p\vKuG2L;/R<趺:ެs4R$Yu`4 ~Lrj 'v /0:~$eIq1={W/θ۷xP0ϧt/#Dg|0^k/#| ^]}g'ܜ0( r Nb63?-M.m5B o6ObiO)c#<xtHswASZ"" "n O?“nNضf2FYM[1y~` z7f# Éeϩ 6n@v IDATpx1ߠ4]H;2V|<jna]V>[Snۼ1Jj%^`q_K>zD;`G`Ǫ`(qQ+DiJ+fNіs` @ZPGblaZ8]'l/qƪ6CcCֳ:ܐ9Y0N`tt!+n q6V!鄧O(pssGgy}7=@x 't(̔(IC`5+rsd aT@t $pmTXzkJA ݯ 6]mʉç³޼ٱ8/m'=zOn[<}r>zǏo no7nnp6cNKR{h+BK6r.>gdHk[weKDoseӽ3t5rG ?)(%RjfF"}Pr٢luYW^wBҶJ/t̍!yؤBW[n&u Og.Mo63l;4rujk̤ eX\!Q NXiՂwd7*Fz: <|s{'7k+</_g{Уoiͣ O'On-<9#>N÷+18=HB!|`JDEw\Vdis$bQjVl(%٧+@J  Q:~xIs1%4kܓ"pC\aaf6mQ!sfRC#n n}zƍeY_ݞ2OQYK:!Дl(^0=3}bN6bO><=ѭa٪񆛛 7N c`ia^Vߛ.|R5E=jB6mteJ4SsBl(@lYl',6 0ВdY*f9) C9JYWAS˜IZk(?ut/Yea0 軆qo"8RC;1C*-m"ch/.3(jA(l=5Ls4-k2 ˆJl79?L"%*ڞΥ-쵎6oj/dlct"f1p6nxwOvwg}ƤA86ܞN8N8n8nm03͋ċ8(ׯ9Q#F$C\G!!gR` yccQjǘ7Ž^~,)QN_^")ǻq'Q1p̱^oC)0|^hy~/h0) 5ʺRƾ!u;gSZǃV4 H@^ĉdx߷kjJH'] J+sBq3QPkQעUtvXZ]Bԩ<ƁYyױM($aF-7Ցvo\/=O%-nӎ-o6Pژ䠑3ѱNiSh*eшwrG&@LȌWj`"UĶeXp sVNX]r@c-ͽ-MNDȸA58֚k9/֧]Ӏg49msRn$=z^ S2?03cU*aRZTQ I3.SHn[ X,H2S:.uhb4Դ$/K/2i89nh_Qc{ڨT+nnD0▗"DQ,*m6{D߀Nt"Pku$j[Mr/iA٥,mF ;:WFO /u+v2\3Y(О|($u&C^b׭(2.^rlhuҋ $D8 |xAcIuW߶(L1%;*UqY{~D\@Lg]g=KeN^&Dݡ(\ [ohnr.ڗh7a VC[∜8~P3MqVb(A4=xiFsP)QI~_F4qyY^ْ;9t8x88/'ߦOpcJSgS3O]dԈh6p;dDU`YS(5B}YgȝYMM /+&AJ'dy|~oZhg-Ӭ_$pJə7Wt+|h*a[˪FKOc7Ed&}~s:M2= ^Ogrm8C !&r&nlȻ:3vfi~WSi4͐3?̩g2@ -Drzp^ w!`)`nV(L6ᖈrR86h#41_pxQc!r mOԚSWb%nLn` BE+M*xS@ёtV0Jem yE*NK!ʋSsY6>h+ͫ0"AH25:|Ѿ{^EEG3+9MwN@ q%9nsb)DNT*J( z0ŚO-= S _y4]սaTx `m[F.XaT ٫`UL\0,MгD JB}-aFUv٠bs.W~bRY]Ai Mg+gZ:| b6+aIۏ4aX$B. xmŖԔ mTZW.xP NLXXIe;y"8= կRE5&},7yZ`!Ip% ́)m4*m%xj#Iqsa }&3 t-:[ϴc4ޥPےd5k70\xu>T2fnA/^dD ǵ]+(#%k@7>p.IbӒdR}?mQB[R5+:9<=w9SY FvG1mW^KRvtӢjwfY%H^#R[N<\rH7OI0tw׮qpD XHy-x,Wb'N}'^:Vp8xx+L`&Z&F0Dq&WAk/#ܥ99N=0n5}psmtw8^6G0EG{k<ѥOxp$}!',e("vܥ% _`P4%ie WD1(5BDыYhTx.?<.7 צS{ɸG]T?kq̆#ڐ` 3'򘭲-'gӪ/" 86^fXI@9,#S Sދbqpu6|7 n7.hSj uIB+PBbA8٬М(I>,$ =m7X2-SE MJWWڀoZb&RLR@o\)m hM;fgUkeDx4LK ke.Ѧhyж+lT#b~`486PƠgaR)3m`&5ceK2zxrLqqt+-o)Fb64kH w.ad*%@,3#7JNBL 9 1yV-}_Y+SbTn_ETN4 $4R"5cv56HZٳYNuz _rXۜ 6o廿Kvn.L(Ki$XqMШQe4,Eӈ"G&]9LuGCۧi mS\שk5'ZmLatY14?^wZBm %1#+7 *? s$"!)'[3 A0#Otbt3U/+Ԇ/S]5}I$S/r]Mܞ7tZ \(QxudD*^gçZcEyQ!75M`蚎vƄ]&ާ`{JgjHZ?9k1\3l$eNRAI}iKۼWbe[e4TbTl#l(:+-ʤ"r0yUd%$W8!)| v!~p qiat(u-p#ğ):V=AniÊ0͓}nB\ToyuF&i \x-Bݛy#XRaauHm[Sgxv:b*4GʺeBo֢aQ^}bSXvϩGJYxvzjXp f6r!F(dCKlhTs Ni*Y`'Lar^Lv!(+"ʦ1hJڰ5P #?;)%uHSm@Ճ_ʽ!j.@(紨IfFb ,%nb2U@bռ<#1m5BZ+IԋteKs}ےj-% ے]F^.ޠWE]Bdjs4v-f Z]ї!!VO顪\]]0qwQ+z&D` GY:1鄩'}-Cvbopy)7 6lGn: 2#gZ: FX;لlZ"ъӀMrbA*KJf$e(U_k@dCb+ʼn0:h-C["|[4Ee +Õsp\#F.~ pwg,F=W:H3@͂Qjlx3Wĝ ]A8BimG0yFgK#fYDA A5G4.FFO)TIpgo:mvwzBT AD \UjcZ2W(f](CHǯ:Ry[isVk< S8]Zp3]Az`u.rmt";@kN2?(Azurh  +V`-^t RTW)E0i{y%7J@8՚C/\O0^xP\m[r:6'e2Ӱe%}\cĠu,Vz u1Ճg7%)AkLv0t7*$J6cF[xv'`+3Zwg#Jqv_vwAO)$~_g%lBZPRc+=Ƕpv*3ʼn{LkZȑ NKlum$Yz Z,HdETlá=ٳ,KJDK˪*`yܠ 8jo#l(mUj!֖Ttm^ psl6+YY< KQ00#pwtXci#K!>Pn:-PD,!֪̊uKmz n;k1QZ+woKph$msYՆʇ`d6)qU{k$uߍTpn8|UNmǼN`.: -\b֒z w^t= x/v`*;*t)_$"ֽ3-U0h"X]/[y4V@,[DșuiloO_C:ߪ{}GeN>3zm{K.a{BN΄Ǯ>tn ^cf,FkwۍFlZH=MPiN4fV&E" 6gdsqIJ"\f!.}UyX+3Cà+ՇѺ=֦V9 t F˓i]`5䐵 FU{jrjޖWudU'5*r&3ft2EZO%ƈ*&߳.ܺ ;wf $ڥ̱BH>8āV%/Xͦ:2ֶ*J|  T*SW6 Irt% I4K0M/Yi4 sf+Lɘ9/I/uK1`yTgk( %0=b>+8y5{w__f\ճVs~L˩LoXhCHNj D$k{<7kHH(M}+bR\^ 0DixR-2v0]A3H`L'>>2kq9DloM}$@ŸO10a S-< aCz"B[SgɁ[3Zꓫ `7bD QMXsCH#lzɴՠ =z+?s3 [!wBK)ɂ|q5b!((Y-Z%L#?5"S8ҟVVvzjmQ]XԼX(5k]1Z,'qRDt0iEf0ݼgC)ڹ#~a! kEHܰVy^S&Fsvn*Y"Wf 'N3lm3`FjSjosP%R] l +SSiC'P8-mCmo}RF/;k\?qi49- QwM:Y^PW)N8]GMÞk|a9JN )JGTt2jܧgA0| >AGhFOy(=i(s ?i4X֑]˞E[:IJcgصGG}*$=dpY:)IbEےv~;668TH1ӣ:+ _ej;JJ\s;v@[_{y$}1GtJeBW5g+]o a\պ4S`Q _/pk ^WP*-\̻eճHY,VNJl=^pb`d1 V"Zb;N e*]t m6cQXطh 5ϫj01=xEKTMOI( q|Jh:ծ?O%HWԢ MQ&9ኂ\MS3z@/_td‰ UPsOWz|u?I=\%ݏJ0mm M5PiE)2]wX5Q5zxK/a$r5|T@Xp'mnD'cֺ!N a[RT :YYdgfς_nUZ`yFݯe$CBX4ذq?Ysf4[̊0th4NTM ߨ~M~#lI #q Y`jqFl@#sBը5_Gb)j-Rg gլA J3ks<5%Es фQ)ۋ26dAT#"\ - ٌ{50|;Ch+F˨QM 1J'9jg^Wz+s4 |"2Pđ!S9\՚xzYjpc8i"ލ8s,銽[u [AuZv}%k?Rrx L/Тa,"|9٬ڮHczδ3GK=w98`/z$t RnKǰ|;^iq1?N5alA:+vncFR|zDƍft5 (EP\s4niX 36S=<7ɘS [I5do%KLU|*U/JtK/ "",Hfb1cC䨦eH]9 i埊˴v&ɮ b&:ON ./!sᕆOH:[EΝ>-A{>sh-Q Eb2ӖOO9 &73D(2v˫`6͐ܣe‹$.!PcB$Un3t{aԨ=p4&JMcR.9Xpط;aG)u؜m-ncʔ6i8H9M6$=7mÁcō>[1m39/TA^OFuo5=yWt*V.Tg^ 8MЋvOvjr^ Ej3+U훭pbnQHOE0F`OIs"*¼{݈7=Dl;yLl1YZ=I| 8 ^Qx)0</Ӗ2X6cۼΥ6D?Rl,ܮüej[W`Y e* >]up=#uRZ,ҩa447wE1$O mp[qV0iNiO4_< m /\5U0RO.V%so3 M3Ŕ¨8l7 9g.0d1P gEH% t[sQM6evlH1ExEb4 .`ʰ^B{Xk:땯׽*3贈kHbM-m}Ig8[hLG{H-e_WJg7Rp; &/97,E?32[q.NEHXV2Z!׿#lZ2Pl 0n6Tt9s},jv;7vX}lQml'i4b,ީF?i$mSU >&'hZB ;^@]1C)nܗ.s]vnnZET>Sx۶RkZ W\nRaEڄy)OḴ NZXÖeIh1eOtam//h92ړ|[.Z[mujfŸ'r]Ѷl 5r쓔>M%H!XsԎ QFm^,Zz Dc/$!e~I1x6<\ MIg2Z(u":an[`Pӡ['Y1)*) L5]EkCzQd&rijZ&x,,fi 5]ǬC˗`R=tӉAՅ$ef_d"XC&M 6qL34swtST˘>w}隓ڨ`@=p$ 3$[NjaBƻc^䶗TR4}q! 4uZJ)=0zXHwە[VmT6@Q\ NF zw(&ѹaPA e>4y;z;e E[bzޣKG۴5萞MKT!3UC JS*͝5[3]Rp2Qi/DVjBT/d >0*"(S"6geGCgÇFbTN]ee"bCDgjt+0o$@d-]̞WO `?7lXs$l2\Gsu'uVl[Y _2/) =NC!-JCӭAînbG'nZ[1RP"0_EŸe &)gݲ7zgQVp*n o~c1ʳs-{. by"4٩ ÏJ9{s֕G{ 㹝n٩4itJ<a+c`r˚*wp nA)kK#8Vk1J,+u6g )H$Ȁmejtk'<c!25TB ;TMDg0ܢDerc<,'yZgDo:kU 3Ha甌uw*.+ NlPȹ£6o.DsA$!nmɈf;0قjD2ܶŒF2>My!G?XCzdAI=T{"d70k84J?7_RՃYH3O U^٬tv% \S.2Ag6vX3l/@{oDz6ӫ6b)te, dfիa4J,O7y_-Sǐ)U\EgXi 癛A:N#EmNXl&Zg-Hv] rmٻqUe5Cv{7L^3(q]X"P;Wo_\%;̜E aѥْlm<(tQXo_>h^7Cf:iMy20Wt=*ՍOIg _܌$[ 6|/+Eny(yuZUJlJK%"RUEO/Zfմ7?]NW-XmKzme1Nn# 83U]k_;r@3l/|ӨkD˷gؼ@.J㻨H^YR59vn (yS$oN6pI6„{a6If^NOx)lLl v w0E Ve7ɥfU8g 9X@4rw;ٌy\'P`D<!%irP*ؽ{=+,FBͬVys1b5Dmw;Oj] tk`H(TiQ=zŷok w$?fOil  :HAx r%xLm?l, r7of11̔ :o2$]{GdB37)I-eSjI1][dq:Q|StӘL Ȓ"{*SYl^D{sM9[$r}uO$ o#ey&vd7F O?aW|Vcsy0#NIM>}JF]bzvDb+F3IBNIRQ m 7fd'>փ.a'ZxR+Xo/Qjeoriabxy iF z{/,\vuR^5$i󔞷 R_ƁULdsI B-`63ޔv`AmrK=0:jtks^@gЬd땯Eܾ\~q8&9eNjn(Q2- xfx#Hڍᩥ=͐rR@Lj3Sv:U 7b#2&G穲DZčMreEźNQeK]TV.chBk mɸm8~ ib5cE$_'77{/g x޼ 7QjdҏLZm䔶]Ľp;8rBʿ5Y;24@?:uS%PHW=ani^khHWNv4^V@j+6%ޒ4j.2׈su kLy "^0]@f~w;Y)]ޠf"c;Cs(o4mJ$`!7cѹc :|3C!O삈:lBx9exR;`4j v~iaHL"ś+i?m(/ɸH1J3oH+DЍ31 Lpr+˹8^Tze [HEMqFB6$ ϲy)`G$bP,Fܜ(a|LjXCFL )u}0ٶ )MأI!<Ŭ4$"1^s o^!ZƩi%`صP(:@jmjhdTٳGc7}5zdoLbP h# &#^(NY _܁ASGi8H*WDu e@ӛ[; ͢%š%%<% v"sDr4 P;wqkt0,/MƓple`Dt _Z!Q7ar RY_OLÄ*mׯ}/P4].i73S:uvtaEFۃ;t9 3Gzf?ŖLQ# qފE\հ)`Vʂ:~'aQu,["kTorU>y[#7#;=,ICH|)ɓafH,Ui*j9s!Ztc6"9Dr;Be*G&aK؆НR/wznj_Z+}q$[ Fń͏5B%4l—ivT:s׵w38^Ȋxz8S+Ok|6'9ٯC+bwo:zVwN\i>EeyO8\Jȍl%X&"%WjIa U0, Jkj Y)9ROg5URDLfDJlfSfJJ=L8%+F/P֎TajZelpX׈a)цsΥɉC3,A|zkH+Պ^-典"/Z}\A;0O7ƒ `$FMzY P)Hl[nEiK\#k0F?Il:/!ǽ;њR~u=l9X8 ;SNs.z,\o?mwN"8R--fqs+;J?hIm|HkFOkbњ8R7_!r ͊ظ)7 &'tE+m[1/ܢG܌⮎WD ]1Z~O:?[T=Z3C|6n꺈ٹ vEy d7} ],"U}:6G%4δowKM؞;EgmFZ")I8+& 6%['i.%IUoo.Dꯊ5h=-Nᦉ]bá8zQlcq`cD'pܔH ͪ0142SAȂiAJ*_ִ 8Uim&w6aq]W?4F\*lފwlk/^0 %G oJS׿ Vֆ<ᆘ 3NhI- 6=H)["q0Oo30Ȉd3zlt~*ffn.\>.ѕL ݝ3: B$'Xi-@k$y;cDFNJZ 6jrV dO)`PX޷E K'{53UL*;y+=6S|"_5M .~MxY4/?BKCpDw{kŽꄎ/&͢vGdCł,4YlJxn=Cw9B!Cxr5=wQ{Eì)#'h{1DXO;i[.qH.#Oϔ`YY(u_aVzx$+9n)ԭo k'an'$:s;I zzڶw h[[mVoMs2'+Ek ƳغJKX ߢ\ >r\oa.u Ӟq]74oqKȀڐ8nrk7wv_֘ÙfQZ8$%5(9#c,ZI4[[q8iUzUֳp] B6ͅ$J$KaZ>I'O(]ɱ;>-/t 0m.WuUM+\ؘݑKײ/eĔƷku?iTpq$#iYKI Ej4p،sB3LY5z+:^*uX$]D|+oq ;dKz+q&W-$f'1p2{!]֠ܤ:r|bk?uh_ @ i-lYRKٌЙ' 5{\Tbjc kI?P,97j wP>iN 07V2?ܬ^ Z=lBͧðs "a(^xM<\Ջ >)3K=ɋmM#>iX8/Q9geP$dxI6T}~&'ߢ_ P|2|w ĭjs_?:˱FAHԱv˔icdgQ"EN̹o Hy+!ev+̐丨`Dn`r6#$ퟱ&)GF|\JF}bh͈=ʂb8W]f^`_Rl,_S¥)hhrTqw dqVVeB7{|a٪zpC˖Nz^,.rigNRs^syr{vqO&fW 7uͥρ{e彞b:res+2U}FBm`} & c3.S؉|JuyD$.g(cg֪mbMWڀmO#Y℄ #z<(6vv2Dk͉+3;qT\붹wKt/GF0Xz:$KӶEl؆[:FI!ݶ w2|x=gȼ/korϺfZI*iNN;+K~V76. BeH'R "Yp;fXW Rr.r!nd'$t9kef3#T$}u$ M<8(7\kaFJ& )݈v&zu񘙾v:SWXy- 4^muU<ҰG}D'Z'w8ZV:|F+#(jA%TSp__!@&-:3+>i冻$|;Cjk7'"ZP9/y3XY V[9똑WUm) nI=ev f'Ǒy=Q6٣cXqzRy{9Zm<-odz_޷bq:HV$-@$/3p@V>Bȋф/$1]9q= Gc6}4򂗖I,,o&Bu 6FاOӢL _WX\l~ovR*_>~=iN7φUiWPzhgҹIqٌQvq|=׬ق)U0vc痌M*LWT.RqpRE;}93~!6E8v7t8g W>2X_PPV j%br%D[5sdә4""Ĉb߮%+I \4,)*ܒQ"ba=Q6mFr59txMVxݍ@ 8j IDAT1?cH7-{|!.1eq:DK?kQF#PJE+*yPn`^~B4@F-{NEiAR:KK7>B-W=pi3ܒJuO~9H>4VzM-]b*O{ZqNēWSi3AI-׾̛|_l1E\l{jѩr'M)0`F7ڷ+$C#1E|69(ʹϰ51]7x8ڀ,Vo>'+/m2mJMHwo>̦/LQ [PXD/y* 8o@ߒ<$aT ZsA, zqT!8' sȵ'k'1ŐCv 63i)8\%G.g(nSV" jA Ө[/(yޚbOdN,+QQ!s;EJ3zU6W@mI]q\Ȣ>y(CJWi ,$V?2,/ɘ Wƫ`29Kki 5P1z-jR1 ZSSؤҚ,::`y49ZAJ6ªFK==m LiAKG"عE0ݛcV9IPu4.27mKs7Qפ ПgtPPDAwӭx0Î $ļX|Ϙ/8Qewc Y,ߎH,M=ev)z@d6C9U&ITס0EԧMM;_jK=`ʔԯ4--Q$:i3U e9‚2wC38yJyH%dX;8wzIܳ4&"!sZ70L\`_Ǟ;1.\0.uy ,GʩtTϺpvYj`yGz(<ݧYuo[7vYu"fZTn3ؙf#tQjUM۶^@9#ÚisY\;VQRY'gJN~AI~,lN~w0sN`֤XdAEfynQ^\%$}smyvM㜙I)2OJfk=>w\IĻHN&)+#<~?TxkǑMxx|q<@Ԛ:U|&tq`Ea-`iA-h|$I':Yւ9Rcqghy1A3tk?!kU,Yo<;l<5zHa[C- ^pN4k=9BalB KE ^l"^By{ VJMcF"ؕ fɦ 1*hekMs)hZ$Ii?ÙMH 5/-QnZr!eU:.\1~e.aӿ%: myͧN]\ÅʏȌ//v"ԑ O1ZH F(dK%\m/wVTidXmG͹@e>jyRqS`Qٵլ(ͻuwDK8B|A,MnNIyq A-o8u<|| E␁~3sGs7!/!5d)^2J+qt47"׵‹_Tpbxl>+G!OLѡ<Xm͆‹䞖ͷrfWeSztui.nv a"/wn}4­|IJSYby_n[+<ĸ(ͅc ;\?<>/b p]5Ĭ3Ik )bLL&AJc,4ř,NnWQ@u=nË %.U5g'-\K(ce5`mЬє@ݦeUZ3ؓR u7ѐ)FqLpREm(jCv[&SYoxW[SxZ̹S0Lkmp9\f?V9 KR4+Ӻ&DKٽ :e m0oJLOX.^N5 a:u?;?{E#V\x|oW<kPgn;腥dHu(g41p/jwE!Jl?ݞb-&7-2ԘBbū5͛=N5-TI N!L' SqlX&"Raga~@/&٧Mrb=n6} v^׼{y|n-d,PZaR{,[)F{X[;t_S9CT) b I3ՙfswA5wuv; eD0_Aʺ&6pKGJR[%V*./_yHaW@M'wMQuY-yr扡i($:I. # /cL#z@6/D䚞_{.fcb`%>pkjۅ =x=!j٭s=AukT1Ù-WEY_. ͋TB? p:d܉ЦNGVz'qJrԴ U8ސ.xxx P~o<$5'+S:ф|jު4NY9/7j2M2Zl]o{ |?VAW(o}s9Q{7! `z|gȓ7Q/-<;c%trH;DNWe`Iݰ*5ByvHv\b1W2vuSI!4?¾]n]cq*"Q@ImyJ7A_ _bnmLw2y\aPRilW$Ӿ7$qB1_h~ 0瓳9a+³Sr<Ʀ c^ob8 *)–}B]=l'%3Gl['{z =A>79@Sx)u0;RWo݅q8lw̰qyIhm/k(rѽ|XRrZ7b!MH)ZSZS8 Nj6@ X3h+%ѺҾ})+,;(z +EFۜd)=ٵg'mo|L%gq`nciuiȑ$ؠ$WuT=Ż`8~k )fߒiwWI :4g #~)n_7-ߑ ܾgIU|kry\>}O*<7IT4'\De6 lt8lamĞt[B8D:/:ͬu#}B!2Ȇ/6 =ӵ-\`gf2(+SZ« 8lw"U ݢ5MR”S7SaIu]j-˳d]unncэ @8f[  #Bv L8 ~gk "Yn,=#V l^ËL\ވ\p(0 ߭vGH;q4(mgIt֊OsRHxfjv\ kZ1/̝C();;/ڽ/C<ҶKh÷2 x wHhA!j{,V39:#.)hڼ @!"I+[eoԼסc[Bu{EX7䶖Jo~-6l5aTYpn:5m:g]П (??Og8n~xG[7 Lh5$iIRwь;4vQDmʜ:uRI⓺,mQSEEй{c Л.WzV(#3ľEmT;÷kҡH'8+JSzAtz:Yv'3.abM{yq4{*2r=y=EsƼ1e(Ma?Dm[[m_0/<'=c[ϮFDjT8Z4»w1>i(tLy?u` F-k Yy$Z)ݵRD-̪rd{=BjtaȵIWE|Xh`޶SSul4- t>[x4߽זxL߂Sipgp <'›BC{vXt+1c<= 3(>NkMW Y`b9:eV4yuLpWL?<2>8 2!:¬xY-0|H71wOL!0||ŗ/Jwf̉SI8T 3Vo,)٫ZYhlPԵ4$^kROb`= 6IE N. -6D G&MCeYԒl{gxi)[ȸbvƊxDu9 8gelXn 3)'qm>WNL[Zؒg2Ns'˓,M-ƒXic@e< ވJIXLU4vՁfP)~76cFƷVDY+U_uVX[nsRԧ,ll9~2b%RkljۛCL4QB-IE+|V<:PEȈ|݀?Ȼ38zVocTY^x)2 VuU:员T!P7DVrئ0lm%XB1Av](ʙn. nԫÃwDg<. I ĢX1K\&|}: x&x*X~XZP=b7X,T*@Dka({+ڕ .<\ 2.?p5J8-\p[[Pc#6PA؋#ti"M.btnҩI Qzv B@qtJTLvP(Zfd#VWvD%t$5Ba2.SNu=kYk{HPeT CGt!c\0. f<,LXCDZpſHIϏO  _ A6`y yRJ)@I VB_Jx`{ٔ@Z)7RB ju`JGz jM ӓo3'>/]J $ػB29gyQw -z^i_wU ѐ|*CuW:ԡqt1VW%4&< ITV\=kVqy=޽'\&f_6IJf8 lZ.q tVk|1 Zqx8mP:Fl&W"!v mDCH\^{GV5F-֚$e4ZѬ&=,7˶CR_?}>:[5Kl*9HU>}iD[dדO~4P#ʞ>6}e[Mx vs\a^uj$Nmy*S0m,Dt=%}QnsluۭG+UN '5mŸ&˨']DV֩ݜBn*5Gtݠ/u5r#=V'g'CPe`+Ȗg$9h_@t&r*kP~*/vgr> fd\[;!0|UڰxcD߻"Zezl1wqնZ!nLo*mFt\8 *B`ĄGr MIDAT?ӯ-?^H|x)\b.c.O)jg)]&J$ľR3kk^CZ=?=N@)K¾ڐ!ާs6hl#u9ƕ[{s2Z!;="l]z疮Nɉ{-n# N#{~dd[%db`>}TrQyBHYQ1%~oQR[MCu811~ p%ݵaо 嵻+FG߼;U A]N8Mзo| /mD8O<8eD;/`:?hʛA\Qg۱Neރ{-"rs.3-"7)Pn:M{|+S a?i*#|1OIbeBS l s1b~[NV՛m@)B){J;!϶m31^~'^O;HY^eAm<9w'wos|#6l~>3O7nU^CmNJ[&w>#Z045ǸǮ6`bi3;uS \|3n7wV=uԹ9I7]_-^9#p98ķQEvY F_\s_W!\}$6~>;5{൯? LIENDB`kraft-0.97/reports/weasyprint/000077500000000000000000000000001410616450300164635ustar00rootroot00000000000000kraft-0.97/reports/weasyprint/Readme.md000066400000000000000000000013151410616450300202020ustar00rootroot00000000000000## Weasyprint This is a kind of case study if the [Weasyprint Project](https://weasyprint.org/) gives a viable way to use it for Kraft and lets us go away from the cumbersome and weakly maintained self knitted script based on Reportlab. ### Try it! The example here can easily be built: 1. Install weasyprint preferably using packages from your distro, see this [Install Instructions](https://weasyprint.readthedocs.io/en/stable/install.html). 2. Go into the directory and call the command with input- and output file as parameters: `weasyprint invoice.html invoice.pdf` 3. Check the output file. The appearance of the printed page is mostly influenced by the CSS (Cascading Style Sheet) in file `invoice.css`. kraft-0.97/reports/weasyprint/invoice.html000066400000000000000000000141731410616450300210130ustar00rootroot00000000000000 Invoice

            Kraft Enterprises - Seestraße 23 - 21843 Bad Beldorf

            Bernd Bolzen Bevenser Weg 4 91221 Bad Bildburg
            Dokumentnummer
            7-2019
            Leistungszeitraum
            12.04.-28.04.2010
            Datum
            31. März 2018

            Schlussrechnung 7-2019

            Sehr geehrter Herr Bolzen,

            wir erlauben uns hiermit, die auf Ihrem Anwesen erbrachten Leistungen im Bereich Garten- und Landschaftsbau in Rechnung zu stellen.

            Wir hoffen, alles wurde zu Ihrer vollsten Zufriedenheit durchgeführt.

            Nr. Posten Menge E.-Preis Summe
            1. Website design 10.0 Std. €134.20 €3,420.00
            2. Website development 100 pausch. € 2.345,50 € 4,550.00
            3. Website integration. Items are evenly distributed in the line with equal space around them. Note that visually the spaces aren't equal, since all the items have equal space on both sides. The first item will have one unit of space against the container edge, but two units of space between the next item because that next item has its own spacing that applies. 1 Stück €2.225,75 €2,575.00
            4. More Website design 1465 Std. €34.20 €3,420.00
            5. More Website development 13.20 Std. €45.50 €4,550.00
            6. More Website integration. items are evenly distributed in the line with equal space around them. Note that visually the spaces aren't equal, since all the items have equal space on both sides. The first item will have one unit of space against the container edge, but two units of space between the next item because that next item has its own spacing that applies. 102 Std. €25.75 €2,575.00
            7. More Website integration. items are evenly distributed in the line with equal space around them. Note that visually the spaces aren't equal, since all the items have equal space on both sides. The first item will have one unit of space against the container edge, but two units of space between the next item because that next item has its own spacing that applies. 100 Std. €25.75 €2,575.00
            8. More Website integration. items are evenly distributed in the line with equal space around them. Note that visually the spaces aren't equal, since all the items have equal space on both sides. The first item will have one unit of space against the container edge, but two units of space between the next item because that next item has its own spacing that applies. 100 Std. €25.75 €2,575.00
            9. More Website integration. items are evenly distributed in the line with equal space around them. Note that visually the spaces aren't equal, since all the items have equal space on both sides. The first item will have one unit of space against the container edge, but two units of space between the next item because that next item has its own spacing that applies. 100 Std. €25.75 €2,575.00
            10. If you mean that Caption and Enter here are not vertically aligned, despite using the suggested 'vertical-align: middle;' you may have to look into giving the columns a specific or minimum width. I am suspecting that the elements may not be perfectly positioned visually next to each other on the 'reading' level, due to the columns adjusting with top/bottom padding in order to maintain vertical alignment. Therefore being strict about the width of your columns may help. 100 Std. €25.75 €2,575.00
            Footer!
            Netto € 10.545,00
            +19% MwSt. € 2.003,55
            Gesamt € 12.548,55

            Bitte überweisen Sie den ausstehenden Betrag ohne Abzug bis zum 21.01.2020 auf eines unserer angegebenen Konten.

            Mit freundlichem Gruß,

            kraft-0.97/reports/weasyprint/invoice.pdf000066400000000000000000002711771410616450300206310ustar00rootroot00000000000000%PDF-1.5 % 4 0 obj << /Length 5 0 R /Filter /FlateDecode >> stream xY[sܶ~ǯ`J";ɌG8Qܤ[[xqdK+9֮li==/Ej!A\sU!Vp*^{x/|H LӚ0i]!}quZ(6B6~xp,)um.\imtIkhn&X$7VYh~>+ 4[7Q,nM];Д|Vhn&X$54Y]ęYsq&qfERAȶFY`S*G^+H@TR[ˑ2>qѮZXGk`UZ8Z˿U-*ꧣ o*.*J򠪍/7U\/O+z}\y]YZ+*JLL MJ@1mXK Dip=וLPK;y*hTu; pUW^­ *Jn4poW|?8Y<)Wn!G( ژ|8,}  FH (o~Z_nNXچ7Q]Rat5[Nƴ+d|$@ە/36. DAD j%/=d?5 m,_UmlKTDvЮkzǨ] wU%]{{M-rw׀0J5mPg֕-j&Y /mPy[|hZf8 |Qs_Y.wde[>8g{FB 4nc*U5*/M%j 9auD=Lgi`[񳀁 Ce#g* 3 ({b憈mYJ粊 zq?9*a/W99["DkDia== I|KO٘sx^頝Ɇ2W|XE"|e"P,/YwǛׯ㋣_j%_f*|PxMl%v #A&w"Gx2r"ba[`˺d"2B~\2h G?ԮڎHYM@Ž4qbk+Cw0wϹu7nY"[t?'N!"(KD*+OA$lo`R\fk5)CJ ~F4MٕNbWV=tjQi=OR:!/?%BAQIE%Y\X[,H<~@3?3 endstream endobj 5 0 obj 2699 endobj 3 0 obj << /ExtGState << /a0 << /CA 1 /ca 1 >> >> /Font << /f-0-0 6 0 R /f-0-1 7 0 R /f-1-0 8 0 R /f-2-0 9 0 R /f-2-1 10 0 R /f-3-0 11 0 R /f-4-1 12 0 R /f-3-1 13 0 R >> >> endobj 2 0 obj << /Type /Page % 1 /Parent 1 0 R /MediaBox [ 0 0 595 841 ] /Contents 4 0 R /Group << /Type /Group /S /Transparency /I true /CS /DeviceRGB >> /Resources 3 0 R >> endobj 16 0 obj << /Length 17 0 R /Filter /FlateDecode >> stream xZ[E~_1>9m`51<>,썄sݳ ު]5āpUUWYd+w/#پ6.l\{jǽmεWgv.ڇ'.adr)Pl• f"W4V6rbo/DB` 7J(JjJb5:䧄ٶFh A%]IPmWIl^; *aUJʮ,B {&W*ʂ*Jb5*읺dZk9]INmW lϢ/1zRߠT 1f<C;8UZN&-$7\n@PɂŏQIk(t712##b)s&s%=ԎÝR$:6I'nsIGR Y 7${F%oMv|N `2RsZ]jl98KH݌H5!ѻII>9$fq<8V %zv&YDgf7s6hmz)n9`FȺ$tO :D-ږxLZc+03NG-WKRT)Ybd}^@sJ+2qͲv:J O!_$5$aj!o@[1'kC ;9ZoGzJ>@#ylÑ( )!pShY3^rhW\z|yK& }--xq9𠼄K?۪C9?[f~u~Xu~XuۼCT}ތ`U©z!YG#:Bi.{3BX2NąuXG?BuXGuXGb.>B:r-v|*ƥ|P?=3xçN[/HD\Ah⯼F%U9s aL/Gǥ\<ԅb'kLǷf?ħH1 )Cax1ǐd*II̠GY%[ cqdôu? endstream endobj 17 0 obj 1439 endobj 15 0 obj << /ExtGState << /a0 << /CA 1 /ca 1 >> >> /Font << /f-0-0 6 0 R /f-2-0 9 0 R /f-3-0 11 0 R /f-4-1 12 0 R /f-3-1 13 0 R >> >> endobj 14 0 obj << /Type /Page % 2 /Parent 1 0 R /MediaBox [ 0 0 595 841 ] /Contents 16 0 R /Group << /Type /Group /S /Transparency /I true /CS /DeviceRGB >> /Resources 15 0 R >> endobj 20 0 obj << /Length 21 0 R /Filter /FlateDecode >> stream xX[sݶ~ǯ`:%ҐƕLfi3MZ<}%)Α#  Ē=C v߷эGt؋bJ6}04W7A׬ͳs\?j0r}msS.mXySR ?x8=zgŽ*[AZގcS7N~/W1 T6< PLuzh%(6UM*y7<PLah%(6UM*<Ndx7T/W1 T5eP\cu0Mpz`3t Y`ViDlhPP ʡbڳ\3rnΡC!ՊfKH5*[j/ػ1CcB*ݐu/UNz"ZO~\5!Z˭0MU oJgn9m(q71юH^Y9tD!}p=b{/&4GS#g{T=Ffo#8IW(M@ؚ[Xy;I x n&CW|==Jtuř+&zEkN ;2ԑ8ڂ:3~PJd+J(mOd!yOYE/hTI+jw)|ڈ)@W&Bwf(IQ%3\\Hx\r"8h4{,gyz\8$AI3a%lzOi q${N:Mй QሌdL:PEMĘ>

            }rWl%7Bu'iÂ1EE;~C9@PPb%#,Xfy>tLq]$^wj OO.t=z~0ISR2v;d'>]+w3C [=%)5?Mlg|g>mt><ʼn.R \إw^B δԧQs RdPC4ʦ1!{#%"tj>%IF)\iVʶOjY7*l6q>Z畆댎Ǒ"rO{uwv ڝzmt#Sc DIGV,#S١@OXlVfOnh7 a|:~~#@ԭ1ygNF|m 3GOx:|TMŋE*ozѿQc%G Wr)?ĐI,y"fOe:Lg:ED_a`:o^N5:*ǧ7}BpO.wvϩ q-:en^LPV&"T_*8\mU1pP}h[ISQL;ij/x 7]EZ Ot^O:TCX֑~z(yTgzj\DLAidʻiSO;5P69 >|㦈WaWP~]@jL Wѕ8yyt(.e>;|)96`8uO.9='4瀝=%xOr.0,9\2m2~C!(|z`>HPc `CoiF E9 [mͮx8+co賤oYOBD endstream endobj 21 0 obj 2023 endobj 19 0 obj << /ExtGState << /a0 << /CA 1 /ca 1 >> >> /Font << /f-0-0 6 0 R /f-2-0 9 0 R /f-2-1 10 0 R /f-5-0 22 0 R /f-3-0 11 0 R /f-4-1 12 0 R /f-3-1 13 0 R >> >> endobj 18 0 obj << /Type /Page % 3 /Parent 1 0 R /MediaBox [ 0 0 595 841 ] /Contents 20 0 R /Group << /Type /Group /S /Transparency /I true /CS /DeviceRGB >> /Resources 19 0 R >> endobj 24 0 obj << /Length 25 0 R /Filter /FlateDecode /Subtype /Type1C >> stream x|w\620Vd]{(FA:".VP" *EcA&E"5j,1{!߼gy>|~ٝ3w:E,җbKd|̊ %1CVŇLj"HL& H *&Z牕AʼnDCjQ&8Ա/9f g$r0( xrБ}zu_\vY"2&*2?.(P:B^ZwG#Eƨ#'1q1bΈZzUpzCtุÆ%&&6tEdWbbm"#ԋÃ}C*:c"R􈵼D?rDDENbSx8\-N[zEEm;Wפת\J%xdjId$S')8+C7ׯoֿL5N-b3#zNot'\*JRT#* Kk҇ҿ (.> b NT4<21fp: ; 3bƒ82 Le3Lf" BF(G=D/F&FFVFj1FSe00j2btg#Ӎ6~ (,sˢekdked1cgx}}zMLLLL6Yhmhnl/+7oJ }}gjhjae`ZnZmz{?LErc|GM&?OoPa /~N;ׯ߽~O}_ bb_8RQ~}y&O_l_ן? 7NYMQ짶0naTcK(AR2+v6F;ePZʧJ;)i#4J<* ` NpxHLcéx#{< ʎ啗准ŇK(B Ab!NP@m@W<< c8#;w&uöMl?g(O~>8CM`݂WtzS &13+ñzvLãx?AyGjgoTZS:DXZ/\Zb;~y犟?onIޞD x^ 2l: I@5W!E>CXnnY2N 8$4)ne7?Rl,ZIMt Ntݬ 5m(A.RTFx qC /$v,LlEBIPzu @?D:Km4z ymu[թlaW ri[gy)BPi|$HCmg;8B Qӛ  4g'%?YДڂ5gFVT\'>>k4)xn_#}-~꫆$,uvv&I̔Δ)4z81eZ]sm,>ć,~-!ЅuI8[\:=$́ޥ R{()Hy2?&SۉJ+ONݥ N g=k&|Z;,?{gϔ*,orbL|cEjXnfUZN"d L.WŬ<\VLb$lx:STZls>;g銘ժb(f9| w\m~oΔٖFp͜ .REy ~6Ҟr*vך}GsPm>KM更BPa pL!hc$$8-0rH?i[O]o⽛( xyxe)n^a>᪊`,D9[[d"lrXWUTT80#}+b_-YSğܷnJwP>[}n'躝 :!Itݢ;_M''ț>;]=tSyսtqmaU1k´sݒ_V{4'mQћ@xcH\N^ yC#_{%_* 5OeÉx1f.Wh<&0d3vQͧ%NXɻ79}zd]p^ ׵q:+NX r rh# 8]}+_hVU3EѰ/dVs$ 2Zau-9В{-%H`K^F2$)bĭ ZJ!m]9qLgەt pMU 8]ܒvu,W|4=Y:t,cJBo Df'\$.LN\hKht̺< [0 n5j?8?$b#1cx h.ˏ޾/nIJd--E[genhPaSJ9'  .11:&7GU*+N$!I~͆-*$ vgP؁IQ!ܐ KarN/h=+T23ӰGgzz aPF>+Hܽ_M$\ɜӅkmj97 TvϤѺB$v2|J %%~s2t: >JќyWp0u0:OA>%f/xHy9,:oѐ#?cG74v0S>jMAĊu%#a0f; o?BRdtQ{#rsLg$ڪ#q.ő(;L.2` s`o +=rĚ#qkcb&e<9q#@5=k* Vx0]׊tI\J8)UkD$MeU{7FSY4]j:;k_R?s f9x4Rx{E{}/+)_+/_yp߷qʐuёݵuW¼9|A u8>;h#\P6C#`43FGRKo9Gq); rb4<%E)G)NyM.*fI\&3 ƕI";=GEnc8ۖC8gU/gͳŢ;_2ŵ='O^`,% Ѵqu##յ`pFWxQ͠3Ou>_̤d8*Ώ2F+1dZdS8pWޑ4‰PHbg8W[~)<=`.iL/BN$R]򟺱۞S55 59p_^yiJ_Ny˔l{ Q䑰?FK-qW` !7=dсpZPφR|׎[x½u`keFPY58MɎp2@0oDqfO 39 $L?5}ZʓFDTġjx',nZ\uI$ ypHA `(J۬n%lI)ߎ0OOGRlR; ݒw}G L C{v݆;M ϵ*]plF -8H^"y?A|2V{b d,mm6KY"mH Q0 +{`A*y^.<t?R~t K^^FUTr|9>˰@W7O= )Uu7V RxO1W0ӏf&ԉ3Գ/'65kHCa[b/<o\@*kAnNaAbNttRRtTN)G)z&#GcSlk% ؉ZUUJބf&r˧n~\}!'t +iiޯKڃw)`|W(AsO„%Ec 0h4 *iP@UA0 X Ã)¬hP<A7d4roθu}f z= %]HW+`>]0B0~t'N0ܒջ\\v酪kRiWUK+W]OG:y,xؤ(2|AC8?bjŠ#s~$Au3$88bѰD(#|"&sh!Qs뷥o9P;vl8_ѓh6- hkecKyy_̥##W.UBnD399+H8Ӡ AUq5E3~ K4:˟_8$NJDB*SHE6G*(0i*< dHuRʤC0 LsdOiwfaR5^买D- P )> m"8KabKGocYmiCS=~}9 Q'fJ#UA˕K=IKaP`\1n]#+G UT ('b<YiTi֑c$C*eő׺A "(e >aw=^?|dk_t=)xݐup˫ok3o7Aڵir6%Etc:J@չ*. Ra3/ k|~顨=atŋ<ި:QY;FN7HnUwqhRතNw] xcchA=pN*{!7iNIX g†C.k}V9*?w#J ,g6Dz OB &M Gn-!K'*I2ZTCmԮߊN! =Ԭw(慤A_<䀹 W )kOZ>733Lն5q+ d%(tC]`ġfhA$Gރ'G S|,WQ-RL*?<|$tN} nph-Eunh!'[afD< @3lVۮJx3H"ϢҒƱGN/d.74LV=nR$itO(ĐL$@q""]rƪ(MaXDy q SCR5nOjOK6MY&fLSfPgv{Vm!89D` Y8s͠eF8(:uk u p'2tf`$00|U 10j-$H@~#A<(W/lNvRG aEgܚ 5)OM.q'EElݴ>EKjWפ:+S NQھhs@Yt"NզJz˒H'OIT:75Q/TGۼC)uB ?`-R|8}#H]M0 +f0 "xثs.$4?}% lيUoNP{Cƣd#Z =eo{[}Y[yُ,z##@SoOWMo-M+<f(Z<( %Tlwt npOwtAmlH`@?ɶ! {?T+d |i5bga ~,!BfbdVAӍ[s$6vMr\>6d7u#´hF" 9X~ $KnCz;LW!wss   fH񶭔{;(bMsĠ 8J L+J_JVԍ4I9IhUCbS)Ġ"y0G ZEgE=}ȏ~P@rBxO_AeaaKIҿ8niK:^ΨE }9bcol܇q- `ccP__-ՠ,@$}S5g(Z OCP){ R 'rvglÓ>{"vמ}?ZN?qhx.)A R,"BQnƶBem!e;r$< hE<WFGK 06p!O۠m ebXFhٷX\NsIXlO?U"8M 5CՓb8*9d}LaAkOփW칳Ia:cSܡ'Dp+ojvE KzܪtJ ̪I-?ukF*+`7 eeeQ"bD G9 ظGQ!QC 捠m0'o0 ¸vhZI>A H<2=swO|JN#܇w"f;LdDk'|[ىͦܤ`v{p? KQ Ym"ck&$]Odt)-kn8r߁0)AtH`zz0·OaQ'PPi)Z؂p_ko aLkɩ)¯Pq1?z؉mq/utoUTDUXR5Ca9B]={ a)v.[q[sB;|R6I`{ZH{C8sôpY$b,6r-!uadN^[Rw'tZh5kF9GA=?lFXpօY/%3¸D:/ ,&dj"= LY[z6~݅aPl1oH4=5Q~sܷ)5(%>v?fK88*c {ImnwI,˃Hݷf$Aܜ R9ܲbnHTӓ /ףDƯZYXms ǿZKp 6ɀ%6k jN$7r=ƿ> "NyȚyad?~r$M\v<{M , o`nυDCSE6>}.?w{ͧV˫)1 nJaAo?qr_ƾ`=Mȥ;/E|}UbV^͇ #;k2RS ZՒ4J~ʯU{5Xnʆܷq# ( OΖʻ ߯ Ս$+/7TFQ2E!GO8NLcN18Eh;J$R!3<4f1 g_ ɓ#a7/N=uWӦ'; &VX&MRnֈԊFr*P2; 4 ģ%O$hurE-&Mw-XZS"gY 7;ܯߙ~(Cc݊|EmLi6d6lD9fffkͮ3vc'{6/0/605jieBbd Wh8rj/-Yj)44k9reZˍ-wZ|eW/iJg_R+)7(7+)w+**Re^yU٢lW>P>U`e#XO6nc36a+M}>g߱JcxiVVVV.Y]zh{OVXqjΎ̍&r,n>\q\.W'% Tz*T5D5MX UE֨RU[U;T{U٪<e Uꑪ['/弒ct~`>O7,~_6oT-SkCc3Knj/ u:B^NSTSg&um=cuԽYg`=zRk_hMiֻZ[nlbn}6z6}m6JFc3flE666a6166Z46{m)9ms%k6wmۼ^[Va`;v$i m=lllmm7`m Slo޷Wۿ4bhkkFhj&ifk\4PMFi5UF MO;;3;Nmgg7nLvRRe˳+;iwήή]]=.gv?#{Sg;{ڇ''ۯh~>4Ȣ8}i A)8wl8C?J֚CpA8j+6T<-w8&r,ַ'QGn]@: uZ68ͮZ{!&LRD*ɆFqF6a&WA8p[`no0IIT}Ӧ+2'G'Uwu7+6Ϣ\X(a%T-lq}UI3۞o/<$?me>̿+#rQ( T5RS#*^H%^xZ1?aԫ%҉kʇPm]J]N/\]|VnYyq,4ilZ/|[p7h;D Z\ID**}.Γ-%\`1 (4r ͩT֞)X~JUu*r*88qߊ.$IPz^ZpWLN|6ŽNAN Q=ÒisԲ|7ܻq!6蜪ukVjP-u s̞[^p\yEל^J 6wTKF9,Q  my>ATkڴheԿL'p꾋ľ3U=uE+cbU"dN!3bCs+ ^#\Pm[D=5K޲Lhk:VxDŭ \MFXͱ5|~񔻭m;nC#=Amn;Ln2V?wU){mk=I炓?p*1bAf׹9#|xybnsVCEInsO07kFAc <}|X8yt~0jve#Y/C^|%jcw28Q8p`Y{}xtÄΙB'7xo7LjMjvD;Z >ucu37)6C Hh3:UiPSh$l7/?Tl(bngҠei0|oLf/bis`ǜ-Ү8K4S~7W]0Yyt0R@bjcCeKu; bTW~I1eIcՊƓO'({OW02ũ z[sc)l3vnﱉB4hC,JwarVfr ly$?+S9yyO4u9GqT?G.9=X,.̾H O_t{uJ6+iJ:ߜNe*+aѾ=g%'pOྀ' MM]T׍.\\wy3 z:>e2NmN+i'Fm-Ӯ&T3Mm|Wʥ9m?b9gW Rj֥lџFkOy[[;:Bj|| \UѤ26WmJ` 1=bJʉnMw[Ք(SKKX<)1ќ>LN+ܺi]"sW(Ɩ'XSCsc=BTnͩAA#p@r2Lgnn]c%P*Tg=8S ^H{b. ã!Cd[n^|&.&B.poq})yUܹq66]jSh3(AHN|ʁ;Le/I5֤`0ɕ}[!řyjc0G4o{Ӗޓ WG}އwo72OR|+fMFW=-]r/63#$#88A *̀8z$nGn0HeE;I߶;:5U%xy0!'F9s/dw&ܸFHT9始~$ld|o=O]~ͩfЍ#Ug[e?. fv$(}"nPe˅xt vQǖ;[Htb6^|LohT9f uncP&f@FqdCs/ÈfGOP_ȅKj&Ĵ O%OVE Z*!rl1l'#+),HʉNLM*eqTI{( )y mO1pk^e3wP\FG%Fk}7?SE2(,C0V8h4LܚUfN'Wb?Xi=u Oy"ORciLohP`P#iݾ.Z&6ŏMf{AWq J7ܺy/TgNVr s @ %pɤCCC*&Xhi~Q'N5bRVtt:zDOO j c¤o{iCC H;}(!K3=@j iiWr9~.UИgN/+;2(&6-/Qt=8gi€'u&趜 /[t'w9d3DؗۄGuYutN|lF^?s{*`n[5UEa8 8q(F[~ Ņǯ)/M!*O?Nht⺨̈́'qB?]?unl.z #q Ɠ,:joJt+U؟FGzſ{*{068 \[: Co¬ڽ&vg;`kRhܧw4~l ǜ}xHP#[/ocg #ePL:ړoávmn5q߶>3m}y Fx)'W ftHJ~lh˷SbOu*5  9(l6UMin;n4:& n4*& *"D]Ѩр .@UH1FcbLn1s|d%3;Cs8U_\DEPv fH_!/RMv I"mFl.}r:N'y(E|{47};MT( ۅaP:~JoO)o1w5l~[ J=ͻ6'2A{WnNUdT 4ORs] 8'cjFXg,d枺n}g`Sdt,]Y X!b>wSڏ.Ѱ6*5]זfg@~5ɽLmչey?q[=@>ڶiN9ZV[Cѷ-:=$(s)ꮸ653|Qgғ(F \f5^VL|#NyXж.k>]A/^0%0,8@u]*8SFʹKjJܕ-鄿AFKۏj*a?8~"ƻ>/< y䂓BO:]$i䵅2@'գPC*op] qwEz(s͌dRɻIcL5n(Hs4fq ] dhϋ8u 7Ѐ߀ pBî2ڬŐ_Ш4}:҆L'_ġnuG$|V7іWcQH̄vW]5֘IdnRJ+ff}4#k,ۉ?W}u1{e$i,YCzLVrԺSWE1k֭?wX6"`Q&6|>D䙍,t"a쩚bWkz$hJGF:kZ;J~(ꘪ0rUKsW:%=FTYK8O%@0Nc;ꧪU&.NS87@ jđe^c#H"k7)~{I{'Kv;X6]_8'Ë" xT:+kۋ7/]gX!=ҡ~C7'>Iyا*4W";! 0-3RrゲJ jkI3p*: *(8΄EEj哜0,Y *W]ݲK>%Hv~iwHhƩrm+b#QڅVO lǾOB ID|y;%hFdFFg `ytG ꅂ0X["v' |]Fo nY(B}h<,U^"*.Jn_bv'b1iaTj鲢raE%YsB=\$5=WP}.L'jkC4X`M|m@@枨γ 2Y~(^ ;M襯Zo GROHy7ai+O.1$&0r,Ցi2#N%:rѲ3Lx gbEBw!H2S<3f6);w~ N%Eh b6Eg rh'?yųW.bpR3 e> ~Z0j)ӣ #{\I+>㗏^ŇV艏l>4ތM\>ԩ}zaU/= "55㛏^xHa$]5v&rQ6qH0%3T$:*TP.*B aNђ?Yi@2g5"~&% WMo&KTq5<8&oFq\LDOl Bl^R/"JvGߏ gk &ӯIrmjdHlŒ9WowPGG%]pg3iga`ݼϝ*'&SQ7d2G,u[k*ߓ_dQsqc`ʦe䎒3Ԇ j~2Q`$~9'+ōEqErpLj`=u$ۭ`o :?&L_ʶZ'H%vq!e2;x@&>f ˝Z%,f]rrj4l GE-VrAIks63mFvlG* LSA- $Pr"LcL,U!շx "\\]B{B8`|Ϲr:)u">aW5dXAD+`TYآ"?M=v:mcJ̐V#65]sYKYXĕAV*q,8}<3/R (bE 2:$Y+ٶh*L(Pˢ}R@#i|(Fk1FOeW|)ةu*&֑Wzy 35{s{:K;I14q?ChTZF:.Uln$d$=v}q?9G;QUMT,̪ǚG x9޵c}t0#LPU4-@ۓjN')tM]3yq endstream endobj 25 0 obj 21794 endobj 26 0 obj << /Length 27 0 R /Filter /FlateDecode >> stream x]T0,/A2 .<' KO@, \9\6Gr8~swʛƩ_m]|'sIk$-_0V>tO_O '_C)]l+M~>=K?Ưǒ%usoK嵝.4u}0Lޥ){kWsuYLⲘ&4V\p29qr"8 ąqA~P\ 9P@#Quv0+F-u'C>qzx7bĞ$uFQĚI?QӻȞDijG:uq<; q`x{ѫ~3uz=G +]ݢ#:'t6+Q^%*rn#V?HpF< = > endobj 6 0 obj << /Type /Font /Subtype /Type1 /BaseFont /UCDDZJ+SourceSansPro-Regular /FirstChar 32 /LastChar 252 /FontDescriptor 28 0 R /Encoding /WinAnsiEncoding /Widths [ 200 0 0 0 0 824 0 249 0 0 0 497 249 311 249 350 497 497 497 497 497 497 497 497 497 497 249 249 0 0 0 0 0 544 588 571 615 527 0 617 652 263 0 579 486 727 647 664 566 0 569 534 536 645 0 786 0 0 539 0 0 0 0 0 0 504 553 456 555 496 292 504 544 246 247 495 255 829 547 542 555 555 347 419 338 544 467 718 446 467 425 0 0 0 0 0 497 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 576 0 0 0 0 504 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 544 ] /ToUnicode 26 0 R >> endobj 29 0 obj << /Length 30 0 R /Filter /FlateDecode /Subtype /CIDFontType0C >> stream xP J[ E, *lؐ *,.b7 k111b$FAShovMf̛77ϗ杙sgGTqIC1Q1h֏Ϝo xY5X|i9x?mwUYUTvR>zִiU5vu3?dHG&L)РА֥du&c|>58Qg{橋0S3tico'RtdnHZ}cmiI`[1 i&c>#!=ۘ2>٤ i < dJoow J47iJ1%z[!ͤ5 :[!e'p@# pꨁpԆ'. :C}4?#MB0BhVh6C[C{t@GtBgtAWtC8z"胾( D#1 #2`B& SLC.%afbfcbcbKPXX(*kQ ؈RlflVl.>!x587&-x'pN=Y.Œ>%|˸ >u|rM|/%-|opp-;|#~P`+*+7PE5YNt 5FWVg ֤Ewz6=YuEoЗ~Աؘِl¦l d eKbka۲۳;; ٝ=ؓތde?F?p 11!aXHha2 LdLf 'p"'1i40idMfq*its ˸+\"s5p-q=KYM-m]=}/[=UVw=nRԑV*kw.wK檔TkMQܔ\R|xmT[SVfs2;tyt-M8k\E"M5K_>C1 Q1X#H0HL$" I۱;K/^,[|O$Sy*ե ah#q2I+*!bb7OerwZj99^1Y)OS~^dQϴzҮ۟$u @? endstream endobj 30 0 obj 1879 endobj 31 0 obj << /Length 32 0 R /Filter /FlateDecode >> stream x]j0 ~ CFw ]r6[N m琷&A_.L`7KXI#89Ϛ_*2a[νu\mpx1a#N ߗa/ k?8O `qWԌ+|M2"B[f%*9$t6dͿ~S7EUdOVyL2WަZ+bt? endstream endobj 32 0 obj 237 endobj 33 0 obj << /Type /FontDescriptor /FontName /HKBHAD+SourceSansPro-Regular /FontFamily (Source Sans Pro) /Flags 4 /FontBBox [ -454 -293 2159 968 ] /ItalicAngle 0 /Ascent 984 /Descent -273 /CapHeight 968 /StemV 80 /StemH 80 /FontFile3 29 0 R >> endobj 34 0 obj << /Type /Font /Subtype /CIDFontType0 /BaseFont /HKBHAD+SourceSansPro-Regular /CIDSystemInfo << /Registry (Adobe) /Ordering (Identity) /Supplement 0 >> /FontDescriptor 33 0 R /W [0 [ 653 594 577 ]] >> endobj 7 0 obj << /Type /Font /Subtype /Type0 /BaseFont /HKBHAD+SourceSansPro-Regular /Encoding /Identity-H /DescendantFonts [ 34 0 R] /ToUnicode 31 0 R >> endobj 35 0 obj << /Length 36 0 R /Filter /FlateDecode /Length1 2336 >> stream xV{LgvECVä" w">X,h+(m1mC 5A[5ڦ+TH|=45iw3~3n0LI_mϿn:x@s-xD)3߳;p۬#:e@wpQc\(&X;\crįCN @&j@- =%Pѣ 5ɕ;[EϛL ,UNK5:V$T X́% 4D4HIk1`J>А00V~'ao+,.,F^5}u]n_>Ɇr蘴cǿv\o`H\c&$-`V;4+UMM@1.70RP@HAhgCM͢ov}9,Ho]}ݺ z-FpgoIb[0&e+ =,$0le#o_*82eQ(m3E)Ní6UIeӫy[r dԜ,x8j99kwDL۾ uE%k#bx̎Ȑnkp`KgPQ4JPB*FO,T'v8aj_$+dw6摍XM3Bѓ¬nbS;07c_rVVJyn/]]-b=Wv3}I9KgLt@:Ij_N~Nl{Zv=-KX"1VzTTVU)ҨHz5Lc HײM}qYΝ_XALtRU &'Fɕw}G9-;_9.#k^%+25|2G_j`~__kY(_ "[@k#HA\v,Mԟ-gwL2 jL4oҏ"/$N*y. Eψ܉)ݨ.>7|K^ 3$^`*n-8mԩ+R:AgϹrU~RW]!Z (xجkz O\g`.rfcI^҈jCtO$XFD<$*"rU Q NDؤQ[`Dc ] endstream endobj 36 0 obj 1533 endobj 37 0 obj << /Length 38 0 R /Filter /FlateDecode >> stream x]j0 ~ CMZ0]r1{OJ;YKXRv^:kd~Q='cEQ6*_VDF;;.BJ>)Y/|{ v]͹EۂƑʽm4MBI-W7(P> endobj 8 0 obj << /Type /Font /Subtype /TrueType /BaseFont /PSSRRH+Roboto-Bold /FirstChar 32 /LastChar 117 /FontDescriptor 39 0 R /Encoding /WinAnsiEncoding /Widths [ 249 0 0 0 0 0 0 0 0 0 0 0 0 388 0 0 573 573 573 0 0 0 0 573 0 573 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 614 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 521 0 540 0 571 560 0 0 0 265 0 560 0 0 0 365 514 0 560 ] /ToUnicode 37 0 R >> endobj 40 0 obj << /Length 41 0 R /Filter /FlateDecode /Subtype /Type1C >> stream x|XW7 ,́Kaf{Ʈ+D:RwUDłbJE,Xb,ŮIT,1;>ogLy9.wsn\T*9Isg&PZ( %.q U) 5θZ.v5C E [yl'oKV1lMsxk(ksTRWzүW>C ֥$/N/ҍ LN _ OJO N ER"u9)$y<.w|.\I҅Ƨƥ$E'קo]Og˽"SR+XWh]{ңR"uÓÓmRtӃw럮OM7k3# Rd(N**)@QnV()ߘu1lhʬԬ옹 ̟Yt`kok¢bEP3Lj Jڨz>K_,U*wd *C]U:g9r,˹!-ZZ<`lnayG_h+JdLVVY]z`'ƜL`Tfe0!`AHO*kWZn>m}Fo3f4Y66q6y6%666m9cs͏6լUWUQOROU4ju`նmmmmEkwl;ͳ󳫰k`~X)AY+ߴ`!a֍ճ]14֏co_XŢG^]w%K\]6tY4.A/4h6kkji^h@/G+G{GgGqli&U9ON􅓷SS -N85;pqx3@~8?%[SU.ſ'A/ F Sق"D BLX)|#lꄋ-EHEEC 'bXL3ĥ6J<*7[Ft>񺅺P].Mۤ+Ujtq9e]m[[[jnwkp;ven޺;nŽ`Gǻܿv}{A#߹?p[w<==\=<=yL1###cGG]u=z\7yx.FϾCZOgM -}6w['7zI6  F& CaаPf62N.n~5|0ed88mnmo 6.2, 7-=rcaxxx8/`q^3y%xezzk1iε] gq|6N s~T;|k$- MwyC妳Mo!>@;5eqppz뵇 X8AQzƁ5t(8(]`؍GH>{IFD-[y^_\ #Pvw [[sasE?3H}ۅ􎯊lI ݋A`zSn9|ڤ{| Ca(|쁇` 1GwzsgȋIJzXĽ c@EK^o UQ?G?Z6(is>%Ra9I~ܲuKE9?JQ黝G`G4gH?rWB^L-/3 %ljl P2ٛKE논V6S;B\c/:_B(DŽ.^ ?#tAo0CLJ  -[HӇx/{7mb4j1\_w;0"DW~e x/SxSLᅊsa%iP}u)]"#_6)F_b|p6<}O~Q# ިm{DcF /kXğ#3 9!ڰͨli4m$֠센-8]麔Z/Ѡ{rx S< &=E \ٸ{je{N.?jgWa˩^ 6E<޾$Z.@Mߊ!XtQ&(e5\|zQгTpGAOlJ`R))`:_c]Cx6v$AP֚1G~-M͍3K?suЂ@xd@=[/d]LKZ%c*pXFݗ/5vӊIou0;i*BFH7oM׮4-xM`R1QR).♚ߞwب8SjxiSyشt=UQBf=ao)́syhھc.}^m0ˏ:l ^˹synD^%T[m <{ɇak7}}ţ|RLPOyX@TI* ;)1WB^Fgaj瞝q51 Q;j)騯YȫDz-1"NM/*۵pV1oo(<4wxr1t o9C7m ҆Ýa}Qx2kfd䲽nGFⲻCwD]a'w(ܗ ,^$Df-?lFkz'%_O@c <|njm\z >L(EWر5AEN>r$Fp.:v_۰aITMi9ɡ˅m7Eski$3A?fG헦oy֔KYZ-E7N X28HY4A2ʭR,>g[+*jGoѸI>t䜸H"%9UJ~tƃh[(G)gonA6wN{tQ-6[/̐qjy6kLBOwRق1^-i[d$e&4v*-ԄUv8t*qa":(=Jai i:*| 5tӠSiFL:~4vcTHtbia4#1[+tYYֲs?+MbܥV4J  &:s՟9it7Q0 |!`!`~#qZ0;;Kٗo+i+G`ZPڰ9~!QW6l^{x^ˍzљRxgDfps5tEmXM IPÓ$jѪELʕwzFй%_Bw%=]ߘq7)+0cmRiaCE>$pPx53Oo'.iZJPCkU I & ?yŵL]Ɂ"*߂nZi#g"B36*oSa %p$C2g{i!8Y&,\E:1E( B6plõ6NK1n⯿-S\E嵽<ת\Em޷U&kN>9P_ &0-|B;w2-"yfH]xBGhH:q xsߏ/jYG`ֵ#Ծ$}v0 ^aebZdq+  Dg⛗҃{67pGM5y^@e}݋mSW0_G$jLDj׏wOCGv<~4b`#B+r|f Oy)W <[ K{Q _Q,B &[i7kA߰4ĶTI݁woq;Ɣ OpVffҡ3"L'-_@w FU+Q ~s$G} 0X!Mc􇁷!N< )<՚3OjPO1T}Ci *wLUI>O>UkYI]ŎB(”`s>Cb4D\@*D2|Rr`/)g_HǍ70  G I2eߋ?ր }同X`(u5Պk\qܢВ4qD*񻈟8c"+ X]! <=D"5{EuhĉaÈ{Dn&a/N B! i<:H4 syhׇI`{{>x}DiŹƈDF_x<XL 9|$B=2SH O)pgqm*o⸙"Z$ y;`lГ,dA'iؽm$aB C[';|M*o9(:넰OT>!G^d1?,W%VD" &25/Sڐ5A(*!aVZ {"9P$ 'p`V3*G=A{Et5I< erCG(rp;Uquz!"vINp$%_G *`?\04;P3["q3LS0A!MŨ3W|0Mo*ٗ>H¨&&$siK $R+w̯$6'2i}QKt]#" ґo&A]^{6C:x\T'4a8e23 ,f@%w%b`+Υaty) D<+ե#wY^, nI1+ k7[~HyĮM Ƞoxl8&0ӵii}4(܍2`Tx`n!0Cx`!Sϳc~[ގ$~? &w{I,HoKriE"Z~ q?/83 &mrY-1 Q#z"iHͩ[B8ns2 S`/57g.?+w*o ;o.HK0{I"09p z13?Tļ PU]U}?w"h<ɯxbdFVR $, MzM]bff(_-\|ǖOZj4OHC?aw1(tï0شpА&Q]˼A0 C⿇RowlOFniL}" qF ͑|&q%rk +p¤@0AO$͉}o%p( ]!hzJlj*-^bO^A$bSP24I{"SccKSE5ћ7#%m.0@ 0ۉM<)y>] W21 ֔^^A_+)sm:wnm)kn+F%raZ_#A ʝF-N) r3D߷[&. 1%슈6gpJ) L#RNHځaDWoߘc?Di$mjfdrFƬ'a6RF(2FZR_YpadL@@}L qQ?U"I J w8+ )8K!gYso[?Dj[EJgsL峧Oa@*;),O&p&;|C(:Sb1$-8nZwb4GuP!R euVJrx[OM՞;&ME&gssa-o.)A s5 _cY|a" BP lv>F,\ Oh)ڋPq'.cDq/[bX/6m)xs={,t:ӹ&|u!EX]nnk]Pw@wXwZwWSfp6mt7n%nݎu/n޹}t/w wKwkw7w{wc}Ccܳݗt}vr&6ܟtGO_zL#cGGG#czlCxQd°ACxvAH,^?x5EyH^ը9䝮obj qn1=i[dC_rnq4aiŒxmbFF<} ~x/*ԃ?q( T5+-57rsPJ®t%(䩎JB cҾh q>!'5i[4 } _1.!|pRl:;U(/ΰ Eyveֽ2*(,.5{M؈ϫ]˗ YY߽Ei#/keQM⑶r]?jcЦҵin 8C g %%Yvd%,R&Z~TAw) wĺO&C$ȏo?M*Ukߏ-13hVL:m̈Y?R8 N䠙\XvBVSzXU_ؤ8\k5>,GJH&?*n a+DR yuKCD. vɧ6-9#ײA JH$dL/R%7oNTqQu;#u4 {wUU$dd-Ob2ԉF&݌n#bT[5UE{Cgs E3IT3Y)nv7u`'L}iI,᮪8&_LMѭGL5Tzӆ-ڢ s6O L'Zhw o'gB1&$7zEc2 Wk~mmǦY~7녙g,Ќݍ׸ƪͅ$JN Fz̙k e&PC׌oV&Fp>/ЪyPSF%sI [4& =Gl.o]mg}ΠVM kk\CcZ^pb[^7؊XCGJ-ԐK V4ysI2'–tʨÃJx6v͓޸{dnGY$g댔x_ewzy<9%Pu&ͩ;<  _InD rGk¦؛Ո/% j.\B i„Bk܂؉$ֈ BFoeGx9labRtVÐ _ly=3$~J͠Lq\ⲂP+:A|փz/W.Q<GbKpcAKΊ=* "4NDFE`TsʌpgjC*.C09w)gX!V1A.Z">t7+ q1^̙$[f RsZ+zk)Ar$ia8 }U~bE\i0;5Лf_Ouн#';[\6:7+Y8͔B@P;\A Octg㇜~*A[Ξ h0 `Y UU{KKe')`XOJw`qw;q鶊Q  V=65I#o |'2PhëWTJrj;Kӝ$wt?%1tȗoڳWSRgN- $3ÝN7@"{119!_Kn?ϳWL}^h(%#BR*PM yVBջ+h9jz҄d0y<$d܉d%וv5O+4ީeVb+*DZ4{%jC7(utr)C)ry2E?Q'&N.#|2͎@d(JjG8b$͔}b7vZalf7À=yao]H ]C3nyƒTEEŞ"5d6.ƼlBuvH?sӻ[=6,m.ŸbdPNȼ?2@kN`${bN?!k4qkoU?:q2>$O@)U$’\q pzI!LM IAOY#S90l>;v2ōEP#0V[W<jm~_ Ok]rg .*&bi#SKW&sAѻV0.MnA7'BiSiq5׸}q \SA/۷%G5}:Xy h1žTd\x]Bk2dǏ8m!Av-c$/J= tcxmXr`IBH^? R^@sA<91D`Tl\rGI w= j#,I6Ͽ!\؋'! :X|.FfvyLSѺ\+"$٭dr ̧\Eͦ`fr8ȼG%S $|Cx޸=Сwl8J暃ջKK KDQ?,w߶A_e& ]5*Ed>^˶;;&e}{K.o窲.O{h>9QUU 1u 6;tAchOʣj-,%tעµ;d.m"86Y.R!(_\SW)a/LMH%`<3-7JJk^>\,k.4rͅ皋/Qe\O>y^@\sGyǩ}׾ūW.SeAG";n2 ԋoCS,=ڤ}ݿ 8B.Ѽ:jۓxqz8 k@A5ޔ^'B<>g{'aN~̸sPK!V,TiUlADE"HfB iA !,=})bT.QR'}3}~>3s{ιپ9$Ȏnc#}3A8평Y:S pyl?iٶMBQk";POV6-p8.fC!#swЄK'lI[Y %ZTfVɧI)yv̞)(n0YЎB;T!к.Gʼn[MM2]{iL A`0B@.E JUw!  hܟr^CΘ]"zu߬~4(yYl 7dz4 偋Cf#慇YB-Z 예pr_t5Q~~f`Ô($=qEqyPW'D*(DO/bVoũ0A8tQv}7'C4&@!K"Bmk~»#_ >/Gݽs{FykmF&EXq!a-T]MO= o\j| ir -\ .,TeovWimJYm!f< tFXם7{ڶ߼ dׅ5:nRokȜHmzl]~o-5s ^IxfK-#|ZmH,~^5 d6$YPc™(Pfmc8@/H0؟"6տ[3%+x&Fqn8`G͘G#7j*PUfqNŃG ~X8zIX)^Xx-8_*QVk0‰4C3$IJxZI16YMhX\,fb7a"q6=Xyə;~mQ?N/NMff]S#fi}+`N 50M;*-"d^ВVoR Ȟ;TZB:[T@:1xxoe[dtXЗ/qfOtrd0HQZ0^s1*=|:n.߼'y%`EXI,Q^Kl%QYW]r'B 􃍲5iBzjB+5#m26|q%3a x]8XTˍ΍șJa n `On=(FQ 6HddsY5h-ڜվ eFkK=Ұ;./pjB#PdBlff>nCJGr9pO_[E0| E1*-G+dNj=?Ɔ3ĎW1iԿ8/`鹁G4_cHLqy$j['٣JZ0?lIpE^}LhddLR5FȨy鎿noQ&z^hO>_o(U@u`(_vڱWXuϋX[-|[t8XC>D;#F㍸?7wMud"4 ֩ };˹;v,c1<^PC`;ys-Z*:q\0^tK[KQ~W1ɂ;֯hh"AsMfBrɤe\F؄cn3BDyD((3w' P! UBdS/eKUpN4xף|ӝzI/|>(+;uEJ$bk`ŖRvs 7Xk֑ Rd4+Ջ| _DBrr}YaGxqVz,?#LHמQgsB ,E]"؅/⩚t#0BgN+n̋N;F< 1zV&s[%a >|!xb%6UjW.P{=JJ{7e?;c$ ks^xKzWrAK5&Qk9c҂E1[#AJZy˷s5ZB1Ov+Pe7Xʺ: _DQW ?)%oTWPP!7iBghJOsu%ZOل:"Մȏ` ҉"D|+ǘ3*fVbmx(Q@Ri(:q\k'sYDrv= =$ZVM t|ޑw "𹮃`:D!PC1JqFP΃SjOGc*eN TQ3DVtSHAUX rUeQn̗ndtg;N\.돊.QL@ 72̊qc`.2eHz]ҷ/c`0?lb>gapN Fw]xNc__NV~'F> stream x]Mn b"2&YҍQqj; ߾ Ra=\֏+q0E\kg^ h*v(ۖ0q'7Q@_qhp8$>BŻ֥qvI%_[0R6`ݿ=uGaԪdoaRIe3\^+fE,%##\?kf3IzȜ&a4kY[VYR%ɼ'ؿ"t{j#푵-󞿽ƘϏ3G"́T{e endstream endobj 43 0 obj 326 endobj 44 0 obj << /Type /FontDescriptor /FontName /WGAUVT+SourceSansPro-Bold /FontFamily (Source Sans Pro) /Flags 4 /FontBBox [ -457 -316 2157 1008 ] /ItalicAngle 0 /Ascent 984 /Descent -273 /CapHeight 1008 /StemV 80 /StemH 80 /FontFile3 40 0 R >> endobj 9 0 obj << /Type /Font /Subtype /Type1 /BaseFont /WGAUVT+SourceSansPro-Bold /FirstChar 32 /LastChar 128 /FontDescriptor 44 0 R /Encoding /WinAnsiEncoding /Widths [ 0 340 0 0 0 0 0 0 0 0 0 0 300 0 300 0 528 528 528 528 528 528 0 528 528 0 0 0 0 0 0 0 0 0 0 0 0 0 524 638 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 527 0 0 0 518 0 0 0 0 0 0 0 857 0 555 0 0 398 443 383 0 0 0 0 0 0 0 0 0 0 0 528 ] /ToUnicode 42 0 R >> endobj 45 0 obj << /Length 46 0 R /Filter /FlateDecode /Subtype /CIDFontType0C >> stream xwl9n^ L5棚SL1/ x׬齙f{キ$T$$@ @B.C<齿דH>ܫs盙+|?$'ٓ-Ifkfw-mL ]cp;n'yW <[lT7Uu^n+`BKIPGRl- )#͑FZa6vs%lmFfIٙKz`M3lvÒbY GmMg5%f7llgu,1ml#>vu SƱYYY1foZL-=FH5zZ2-6H4[ȿxhx O/B   #EP(0D)FE9<"P** "Q5Ph qxA]C}4@C4Bc4AS4Cs@KBkA[C{t@G$: "=I胾3#)`F"itXaCŽL80㑅 DLdLTLtLl\|,B`c br+NZzlFlflVlvNn^~AaQqIiYy\E\e\ x/*^5W^x;x}|q7q c'O>]|/=7xOS~/pgyoxY}G`X!,¢,Pg $K4˰,1 4XXUX(F3cmƳ [[ ۲۳;2ؙ]ؕLb/fe? 09L#8L(J38vfq,N`6'r's rsgrgsrs2K˘\\tr rs7r7s rswrwsrOO // _|W2:η6|~[6?~9 ~ɯ5>>||=ȟ3n>c>s.|+?+@ ˤ QU1J(L%UJUFeUN*UʪHUW T-E)Z1Uj+^uTWT_ PXMT\-RZmV^Q ꢮJT7uWTzhihɬJV,JUFi(]Vٔ+Sxei5Q4YS4U4]34S4[s4W4_ P9ZZZ\- *SkV^QY[U۴];S[{W_tPtXGtTt\'tRtZgtVt^ .Pbž]9cc'>Kw=9zر[':QXsa,x皙5,+/$?$俀g:yLPh4{ss?3艝v^vzltg+ a[5昂]M&WdpOOGtYWo?K endstream endobj 46 0 obj 1623 endobj 47 0 obj << /Length 48 0 R /Filter /FlateDecode >> stream x]Mj0 >`g!P,C0,r*0 l̳)܀B$ϸˆS$\GWnYim)8j[П".78=49p k7 u1s6AW{c΂9p}s%[liB )$O?1݈kG(2Kd )T=BTp endstream endobj 48 0 obj 223 endobj 49 0 obj << /Type /FontDescriptor /FontName /SPALKP+SourceSansPro-Bold /FontFamily (Source Sans Pro) /Flags 4 /FontBBox [ -457 -316 2157 1008 ] /ItalicAngle 0 /Ascent 984 /Descent -273 /CapHeight 1008 /StemV 80 /StemH 80 /FontFile3 45 0 R >> endobj 50 0 obj << /Type /Font /Subtype /CIDFontType0 /BaseFont /SPALKP+SourceSansPro-Bold /CIDSystemInfo << /Registry (Adobe) /Ordering (Identity) /Supplement 0 >> /FontDescriptor 49 0 R /W [0 [ 690 200 ]] >> endobj 10 0 obj << /Type /Font /Subtype /Type0 /BaseFont /SPALKP+SourceSansPro-Bold /Encoding /Identity-H /DescendantFonts [ 50 0 R] /ToUnicode 47 0 R >> endobj 51 0 obj << /Length 52 0 R /Filter /FlateDecode /Length1 12272 >> stream xzy[y߽o `{6f}0`̐CDRBmٕ:*N妉oȒ6v]u||Ҵur׵[Z3{fҜ?;{wB!CF|Sy!cg9 }*gch!+kɉG`uS8s~;#R}3С-07v̹G.8}C sb-\xԅcO|ah;Q/K4]Xa?3az]Fr<7r<{h(FP "buqk4yף #FuD ?NR=VA5tjv`hg8,Q6 ;vP4b *ڗez.3ӛ硋td!a8Fa\.y?՘0{N,;5nߢ?Ey4\0csl4x1]Eb8y(77cr <Ţh"zNneR^B9|[Ql>חr?vFZ+MF;ӔmR<:?>,>7udt"_g.m6yKHWF!:}zV괘a<5WU;AQMm: y-O'hXa xj޼hs^y7U X XO;EbLE{_jkxҎb7Qdܾ}Of1[.m0Ͷ`ޔ]k#Y6'#:/֞wf&Sɷ+Uu~pV}zX GՋF5KCj0ƧT4ǃ0,V [ꂜSX< DX(Tc*nDe.'q\dESƶɁ/kÊm^ݫ5{4V}YaE}Nt+N'~ z ǾvxowuEOӅ2HiDc,d8լ4S0ÀG~Yjaak}Ij!6PQ3MoQ?Pf1`zl6g'*tH M$bCCN|;_[t-lH )l?%?9{m+۷ehVZzDiy#4ܬl@T _<  1Ϭhń:۹ a#׮6a[Z!4? wFQz?T5R#ʺCx{̄]X%#@pqlkJC>):&4HGyzFF{e =bN'e#}_k߾(:D'^oEǢ}Շ>X~l׵]fACrrEPo(ρN0V~y"0FֽD9#Ȍomͯ?2xz|pr1dR`QfhaWˏOʫX?+J?DC@TZCaDǸB&Ff6`װu(a:aͲk-kY a Wkkwpc .W,FCd"Ll\+HDwo`|>1ZLnyo[i:5TOQJeqG:iZ6{eBճNunsؓuV2$hh ;Pۿo!F !R6LcP9zh J<e4^?CVX?ZAh<2AtIĒq-T"3ᛍ؂qRw#E@F&;'Od Gy}:tᘳM49i.kGQ:/?t\t<슔g>=04鈿s=:Da0_nͻo?mNz< xM#~y(T1;rCZ>̜;ct,Zbœ'8VI0xp)%I= Yz_:(P]!aOulKFǫ)v0Fa wAH`05p훈F](`O Bmn!.;u/Iџ~G.qS2ңNwӐpPQi^ Y!C'{6 k.S- K;#lrfe[fͥ`X=LcƻsG_)x抒,{ f;aFQrXT@ u|ɨ7v֎x p71koY۷h^B#h0چ9" G0%69ēXnPey^)P2#b8bdMvZ"9AzY,>qKǮvࡳCc^:SɸW`ء#Ns=\=mD78prO釆<44vكpjzkVGSXKdn} ]pBGYP"vr"GV*R`s&̈QoȞ=v鿺utt›X ٞX;9${f.f!1 Ϡ>rȻ 6]*@nF0ؔSPˤoc^vd{ş,;'x\. /܀(4vt63}@0z`t| tħ}3y% Q^r)f!o,]ZG⒩#GBTCp*Bh3 bh')[wNhTL8EE-u|l5=-nǮOzG'N_``J& L͖ X OwS{ /dq_柾8ћ?$P'^b[I9@KD6 prVG3 #*&AE XkO!NQTnިXuDhrirq 9щHLQ) {Wdhjݚ?y}kgk~xgivEzqwj?_6딭/ck˿q|`,Uݷ~9W_LŧE E == I1xR hW Ϊ4+Q1 V2n?- muv{-];6cuѦl}aos`˾ČRWo?G;]#iqkF+0`!Vy\ 'cTaT`=y{wjiQ H]IGyNM[JʷIrcǣF`+>0zqL@Uo vwŒV7~|AW{6O- 'zBEEoDzxoL*RK咉tp1# 0-% bp9m]7!, ~CFo^Q{.1kֺwӑ@t$`ob,.B}j?s Jp$U9Rm+sXbdq}$g<O?QOGgz ;U Yz+ضxa;% ?&uR{by$ z?EkA`$Ct( tQ"޼'jD~A#HS;H/y7Lt)OG#M[>T +3OD#"M/>596w|'5NSH>l+O_MV ci5 il {y8t}Vւ>_~Ū`ƙ[5h`:a;\-hX$Ƒk y.) ͖tV`3Pm٠/{[@Ԙ ]BbV❱ edȝ ܎b lM)9¥yLbXAY-8pqѐrY!GwjTs^!oxvaj46:wk\m}Kl/Ù{A<5X{>OQ8&d~PB4"xCG1g6>2?A} !*t 6-}/?@k`!BZв(j,R*Yn+Մ5And4{ɜe)rJI+NOmJ]DҡʦZq(%Iȁk7_2v-Re-^ k-ӚG'S IO$OA^cÄP[DWIF7+rHhQ*l3rvloPlX,Ma#0i$$=!Лu$SK'OIgɐGd+mr6yK HZLJb2WBZIҮAgk:5m > { Ld}C[ 'B y yA>|3g#N< y%aX. ^^ ZI-Dc8o$w%.)f"!6mi& dG"efzœ>e}Sw GSĨؿ"k}5Ү9#BZ"T;s8ɤ/~v $16jDa4/%O]O0|&d-5\ +7<7(Q--*3ɚ̕nI{jybJBuaT]#)9uql,&Iҕzwy&Wñw +MD&hnԝw# 7u!@/mB5d2>)N+THQ5-0 )}5L!sh1 kv4W En#M8)J%Wav=˞=Ż}%IL '$y(yF| '+4-wÌ"2Ϩ: *:qP' ?)m4#]zH!U`)HɦV5 4i`TP` D-- ݣvzN1OXu PZC@.J7b L , )ONwwNINBi NB"LкJGy &V}\i)3.$qE% NI$ O$axxG[]UZ 3*I7Tf6ޯ*I>c)-Y$RIa`xN% u$ n+s EiZLʚS2^,)Rb_7O!n7bw?b}8<ֵ5߷`gSw1mԁzw/,~/+9Ŀs_o?'zϢD^K5)S(Miψ/P u.h$Ro2PPSm3qo'8w࿪j^ֆu辨ɀ e6f: &bAnTCCߗM^!8@s8@\D| 4, tA7hD3FGU:ŷ- -dzŷQ!=_ 71a?\!q~KfaՃ_W2\u)޾&3虸ɞ?8MM endstream endobj 52 0 obj 7357 endobj 53 0 obj << /Length 54 0 R /Filter /FlateDecode >> stream x]Rn0 +rl+XA+EH¡ 1[j+$3Nvn[?m2{`]uF rűnmvPsE#<\ˇ' ([t'_玷k?0d.F:1K^dć1>H#[5b/ L7Ҍc#gʼn%h(L9 51S cFƀX3)S".1S c@~0kkj״1 .9OX[˞ ؏"?(ʣ.؃"%{(C͵TgS-S͵4ժ8g?zd7e|5FmzTi4݅%*} endstream endobj 54 0 obj 363 endobj 55 0 obj << /Type /FontDescriptor /FontName /ZRHPJE+Pacifico-Regular /FontFamily (Pacifico) /Flags 32 /FontBBox [ -593 -457 1660 1478 ] /ItalicAngle 0 /Ascent 1303 /Descent -453 /CapHeight 1478 /StemV 80 /StemH 80 /FontFile2 51 0 R >> endobj 11 0 obj << /Type /Font /Subtype /TrueType /BaseFont /ZRHPJE+Pacifico-Regular /FirstChar 32 /LastChar 124 /FontDescriptor 55 0 R /Encoding /WinAnsiEncoding /Widths [ 265 0 0 0 0 0 0 0 0 0 0 700 0 0 257 0 0 0 560 520 577 0 0 0 0 541 0 0 0 0 0 0 1014 0 0 0 0 686 0 0 0 0 0 937 0 0 0 0 0 0 0 693 0 0 0 1217 0 0 0 0 0 0 0 0 0 470 0 0 515 393 371 0 0 246 0 525 0 0 496 451 481 0 455 427 413 462 449 0 0 479 412 0 326 ] /ToUnicode 53 0 R >> endobj 56 0 obj << /Length 57 0 R /Filter /FlateDecode /Length1 5328 >> stream xX[l%I$%,R+R2im%%;JS2%NlGy;o5SAS(6?-QȬN P Eڟ()QEa4깻,'v Ι;gf=J3ƎRSc5Mxv&áKQIm >>6:z@!iJ(511>jCy/>[ۛ<=g16F|?X=mJ q&]Rv יc|,2/2  L^%t,vk,73y\ζaAgG2 &|WZBĊ%$ H!JW=iR};m#9v:Ba5:4dzNޓNi]@^Y,mp0i:yFTv|3Ώ()s)Ξ$Ԑ1 #lO"<$kfGE4ؑ7_ z$e2^5~o#xLp@q3j#;#^_`jxFl:v#hE,%b@vˎ dHrҺ^EV(͊e7;=bqZ)E\43BV;oܻ}ǺZNCw{  x%T55M3=O(Mu:iڕ+ln$Zfmk>_kK=/Jsh9#@Q_vF."6~b\א5,;+߶yU!msfo=ʫ˘`*RWoa^((.WVɒbwj5m) VߨRBkQ-._!3DV*9S 93&B2N\PE`Txa 2"%/s*+Va.Y:t!'E(~"l BD]V{E߱1یB B)*XyȑzI"LYrY5m6<S鮊 1+TQ[,U`QiKhD%R)%Z`%E^㊓-ղrAY]F`K* 2Q*y[1KD EZUx.XE-Ե*%E(!BɦHV[d,I> r[+ %E _7% 蠪ȐPov,DQA ;v+hl)ѡϟ(|yPhRS_((읖=8Y2;: 9ў#ʙ~ K0r!NJ%C:2miMi N2Yʈdz}nB:2;w+[!L*(-צPAX*ݜNH t't #P2%tS ݡ2=6Nj{uSvnM76kD55-tlRB6|Lmo >h:X߲c@E|*@|jAGm QG|!>j GmZWgݭcۏW +8i*ތ.v'n=8sMҪU4῔G6}JJOIN*p;RgLW-DZ)|qOBv~z<hSQTJL鮙OTTx H Դ:Gw=\isLJxţiP %]9p,rHӚԱ45m'[\ թF.;Ξr\/no͚Vb|VڳO 'vpϩ5<YX.Xh#N bEr"FNn\B!4PauZh3i֐ZmJY<%!c+-؉RZoU ;%N⍪-JmtUM!7S&gEQ*FpUgi3Ã8]p0t/eu171uS*(X!S}mxS١Sj8>i<{YӜ?)&f4\U%Zn9 x_!QK#- C}Zޙ8>xSL^4GIVgnuLGo7(p ,8qNI8_]henmwxx*,tYО'%ړ@8C`8VCW3_"t_Y~:oE~h<5' $ˡ 6$#78bUz֌ 6ېO`I)iplճڐ6$0AxΆDxކDx[ֻh,6$mHK dC"mHW( $r.)+B,^h>So?}SLuj>M*O9 $sv}6=7?e#5埰ktߔ| =~9CU+ 7_.G}̕{} tqkm ^%uZܻus3kş{e?dy;9/-ֹTr7 /~^gܑT;OkHJtWEx/e9, endstream endobj 57 0 obj 3058 endobj 58 0 obj << /Length 59 0 R /Filter /FlateDecode >> stream x]n <ݡ"I!U%}hɐ@9GYg/~{g#wj:Mˆun@[o5xqiE7HMp5!.Bń&{U> endobj 61 0 obj << /Type /Font /Subtype /CIDFontType2 /BaseFont /PMLOMO+Pacifico-Regular /CIDSystemInfo << /Registry (Adobe) /Ordering (Identity) /Supplement 0 >> /FontDescriptor 60 0 R /W [0 [ 265 393 394 495 411 484 ]] >> endobj 13 0 obj << /Type /Font /Subtype /Type0 /BaseFont /PMLOMO+Pacifico-Regular /Encoding /Identity-H /DescendantFonts [ 61 0 R] /ToUnicode 58 0 R >> endobj 62 0 obj << /Length 63 0 R /Filter /FlateDecode /Length1 3616 >> stream xV}p{'Y6DQȝ8dL$G`P_,!W ՞L0 v+4 9 L:%@2|t$N'$-N64dM$4;w{{}< @GVK};Ͼ2F@z o*LP2bkĘ[NI'p$"U1 Y~z#Fvc8[A8RsN`P`3'-U> L8q2l!ӛ>XǴCxGL.Yak#Zs'_ZٻyVr5-͋o&בZHn񦡻[:'mX 7"oq#wok{z5|StC,[~Ykz(|5;p7o=vfZ !L _x ^ xvi/|Ss! CpZѝ4q;e"0a X]pan#~od#RTC؅u} ?}g$mk"^ b^؞s?+GP9Cxq dvqYIhh{|#^UBuw׬^kqyg-8:춺5*IM!IM R8t)@ IEԹ>TLn\O=KUObh$7C'=1 !)!K|! e>*n7F3)I ULJ2|nAh4#!plX{T?4%rMZ'  JZRƦĜoZߟC_[/(XWt RW>@}RH^xugCj%Q sT/ULI=/f$.Z}D CTè|'\Tݟd-/V76ib:^mv$*>3&Ӂ9ueCfcZI5 ߛ\Y+EqfV,WÓfKө/)})zζBӺ]nI_' _gѿML F]Bt}\z\r 0ER & {K[߭Q9*[LjTh[>FB~i)ۺ4#F)$_ EEOJS`\RL;-řmL sb]5(?HOڠTN'$m 3bs#R.حEHGk-Od`t&rJ4Xr걊hG@TQSNJnTF\Pi2*1}S0\ab*.w]jM>by`+&ރo81 K'yQXz,a似Wski7+ K&UkKU5|buT&yVre~v%5Oq,fV:uK yԵ"$×Y{N"{c9ߊ[L'rЦ[aC)i#*VuNV<V0[\ W01S 5%gsoEgDZO&XC=f/B#VKFjgxJx-X4vvEl2>vG[2Qy&Ydʏ~gnuz69u`q2M ><8a?~wɯ z F2qR ?!‡jWR]l–Xa`7 zOl=^xwF\)LuFu bw񋄏[[ɿr?#LF_mH4?|qZ.4TTSSg'$o$zLۙ>KRiz 47}iwE2}Is|l6^7=s|=N׎cG -WqCdd,;#cƸ[翣o?Jʹ \C]-H/8>^kNm M=a74/1'f>>sX1ɱխ4(PE!PErY7/;-no(NͶŶfN۰mvVY+a zb&yr0F"~l-M쥞.˱ZBg#@mhrI"BQE$W^HIQ7aad3$͠2TF73J2QȌ"!(g2 ;-#"v%L3qb F+ endstream endobj 63 0 obj 2438 endobj 64 0 obj << /Length 65 0 R /Filter /FlateDecode >> stream x]j0 ~ CqXO!0KƲ=c˙aS6HAs=fTU 6]dqG4?D3pxvq9kKJ?8!e8^{5f&]SDy= \B]Fs2Ј9KxV!N ~.JT|O>BمYM [@XVi9qpr endstream endobj 65 0 obj 226 endobj 66 0 obj << /Type /FontDescriptor /FontName /ITGVDT+LiberationSans /FontFamily (Liberation Sans) /Flags 4 /FontBBox [ -203 -303 1050 910 ] /ItalicAngle 0 /Ascent 905 /Descent -211 /CapHeight 910 /StemV 80 /StemH 80 /FontFile2 62 0 R >> endobj 67 0 obj << /Type /Font /Subtype /CIDFontType2 /BaseFont /ITGVDT+LiberationSans /CIDSystemInfo << /Registry (Adobe) /Ordering (Identity) /Supplement 0 >> /FontDescriptor 66 0 R /W [0 [ 365 593 ]] >> endobj 12 0 obj << /Type /Font /Subtype /Type0 /BaseFont /ITGVDT+LiberationSans /Encoding /Identity-H /DescendantFonts [ 67 0 R] /ToUnicode 64 0 R >> endobj 68 0 obj << /Length 69 0 R /Filter /FlateDecode /Subtype /Type1C >> stream xzX`,à8;Xbc(Dzo{_l.U."{FSMg|gͽ{=ϥeL&@d%M2EOE?މŌxxx8@%Θ;a]'P5V2Q2M2WT$IH:$=>ɰdLB1_cFFu4j`)NP:@߀JF͢L( j'@RY=1=wM&h*5hjjjZVWkq\m m]mF{vv1CWj?~ACZ6}ct]LMt'}!uS.pv A/Cau:Bg·:sux׹.ԝ|kttfVѽToRXQ$HSiңVi_z_Jg7Koz6K;wB^^%o~i7ߨE[O?X?^Au?N g0`AAAAAA9^! dL&ddVLe2,RW_vT#˓*enٰG /eF(Bf)cX0$2t3%L%S˴3km yƨ ņ!c8P0 2\opanÃ' Ol-h}np6T%ܵ:A"o#:csz*N?{Bh? )Md\_3i؏@FAzIWO-U-gZM !XW"@[O2<`n!t:Xm}gXR%xKA#\5jb SݹH {+G@GUgBKK#Ks)JT!}+4_~@2LJ?!Wշ#B0怋s?w2P7h}ȐކK(90yƸ|m>"X{+Vᠸ6*ףiH ~Z T Kٯj$^x HH]GpDysՊe3n^u^TM ߵfX35q_ke.l)e7Z5ߺ>V/wZ|B(sySXW]!uW^'T׃~܌Ys9-nR?%?g%hx)S>TS(ueYe\}QW|! 9KyU#aqM!'SN):5[._uG c[d֡fa;pMQBO#hv7 fVeW# jeoH6"eHaaOQW^jcW*BV<^hvr*u`Ju~kMɃnݟeR/rt|N`o/E7(=[_5yݰ/txg4N\ȡ^z+*‘%1zJǹւh=yU2f<}4WQkS}-WDI Ar4ai3*F Ч xp{ Z mFj]K .UvS'v#gsӊR[AFbzRf-YTq_O,q-dGl4Ԡ=s̭=>ڵa x0G~,]&[t+fչ%F kp/ℾ4Б#Hj)Bd%E<Ǜ,ƿcW t}5SJV 0֗}d,7cU;Hhۨl>`cb>-aqw` ӿ\[Y(wrVHCbp`d=<㴋gjЍWX>07\ Q6 ,V4T P@’Tu uKN [Y:m -RHxL0k~k;x -)(.)ω@ EP?pE4a [BVQnph" R05ԝ:].D  nkI9V\*0b3aNu7q-wy'LNKKU,=HI= 4^hrse 0_K LY6j2 "g}U5P7?؛-8CHQ&)j\߃B-l{֣74{}*b;wʝ΅*Q;)r/Mc1Og x*1wtʺ "A#Lai[xu7%UD n$O,Dх˲M&Ib\yzoi˼#f6qFϮ3?E!چrlXb l5FRa{,pTP!0;3Ryъ-<.,dZ @KO =!^}@9y>` gvgow+696s.棓Bh&F&?|>Dpr97GÁn5͍; ^2?фC iLzNF i8 Cx6/H_bCs?p=O;bmu퇄)`]j,㡬'uIG]QdzWe]#_ɿ5dukTw7pWܭښ0eKy[[w`~o8)(`d->BP{D#^%Kk[3)9B)ngxkUWҫsNW@(SN#ZVR W$s+#w3|nFF=BRZ|@`GC`E*2ע;u;dq"iwJ07pr;ۈ>Z iD)8df|+T4ǘV#8i)|׭ں1M8\UV krf?ܥV41OF~;Yh~Vs9v[<ȪLicIIRd7.$ԅ <]LB;ջ(v?jvQQEI&Z_AUb5qxWH!&)dhQ t…?>TwƊ]ܽ*8#itd 9LSqypTU8BGԹva$ӡTE8kb ɠ-+=ߚӆ1x LB8 ?RD:,P|!.U 刘]娪e 28\`}8%@?!I^ެPxzЗV8X+6/ҰC^哗e$/F;dO~JIհiWr6;bcfW+4ke*g)dRJa% ) P/}Vv<%V)K>-l|+RӃHKe sm}KC蹼JbUD 2ߑTx3x0WhiA̋wHxi+/x3䕄J'@yA3^̳x*dɖ='#Nj_=1k+èְ7g:p,KG,]iI'^)8[X S&|9j ɒ3](9%i|1CcFn"'/4^lA&ȣ)_*:K]RQ47iZkzjjfkh^ռ7M5_kZvZ>Z!Z{ ~UC;P;Avo A'5PNp Ctlq щIѹLw"]J]kO=ݗ]R tDj#͔6KJH_Xo^^^^E7z}ͅ]=C#goѿPGd9K 0Xo`fj`mAA[ 2zlLf- )eYlDv[OFe ɌάdV353qIcҙM>yihhlÕ m C c G OV7l32e++eX~e+-%zldY)-gaa_oYl4HHjdh4Ha4h2#S#s#;#O@0hFe6*4*327j643l4btC#<Lԛ8y"?qę(Z^+.kA{'xs/brt:YW/.cOKltw TLEmOg@ xB d&0Ɵ?bqPߢr|IB ҉u~#|x_Օ!A7q{RrxjB#7*;WJwQ_4›@Dg=e!m- t=+5~֍+ SHw˖:ؗ{v{[{oW+@Ɔ-huE_# j|ͶKm.`E]9{n=PP9ڄ;NNRiD%Lq1>`~T)nRsn--|7Vg0/^D`'݃<㰋ghF8km Y{BFk:iw X%*i냁vU-k\] kk[B*΃^;Kr X,1#q-;8ZA?\wl+hB;ܳyY\SYν(p䥦3) Hp4pbcצTCo FtfJb}C*[Nrr BܗL2ZJ`9s˙deO>{oOl: ^P/Yo ;գE^unB<+rySoTsFN6J9S%/<]pbo|i?yKNź5&!AGXIp|f5`~öy[;6 3mFȍ|=A٪E~񺌗a- % į Yg@z+*/ϵ6JqKւĐJ ʊrydf">'0UgנB4m+&Ԥ;C o)4ez PtXPxh n$SV=(3Z3/D<]r>KIHD'h:O:OP|:vWbCaC!6k7AAQ°]'eƒ|%{s`]t:7t}p#A|ɎkHrF@,uw%A|Vo 'pc&bKD4%X&r.o݋'bczUQ6: LMZ ۷q(ų?_$!!-Nw+wy8{98Սݗ{ʷ5i dԲ eUUAA~G{˃h {s=AoŽ53E;m40S jZnCEPw   $JVd5ЖgjvFȁDwh>vk[Kb>;ZF^Nrs rR!8ZzX=+eiEp!4g%j%u䟯)60ΫR+LKN;s䑃38B}@[n(ھt-K#Y3ΑI8m 5t~Ղa42;.(o8XW>}m-+*)+N=)ќc\CYO яQ^~[^g~C9I)B{-*yXHwsWBRwiQd0F!NYS$ՃĤyhO&NEY9(h5_> hC~>[uʌ$Z&lH1I٬OCSh&; E `E!1)ʉd-5, ;7g&z!Y6Ӌha?T ji/)QTON!spTl5M 1>\7p\2({Ӓ;W\@p``l SstbeE%3m"1,ҽ1%A!4-xWRVN" Ae,v_-U0.!q$L^0X-)ے";R]#7ߡ B0:œ d7 _UGBL=]щ~EϼFkhˇHkge}cߘL5{߱8o *jmf;rc1s_XX4^}I%M*gTK$wۃJ#ۗAy_"áHN[ yFU,VZ_P>~GhF.e tH@KmZp-iFf]CiiIջtҮEWPZ3*F_ߓ/ s4˟(Å K}B@acqVwyUhjv O$hj"PP}.@ CSܼEteE!/}z_##|:weHOTFF&Ղ }mSE8^Zu\9N'y|}.$'h,Q `=wPΉ{\$Gޤ :9QU >J(ÂH2_]vg|Lv g#-O =_١!+nU"Mj|'@QM"ag޺wn& xb{c+#ɒsڑ 4|rDNS:'agϢKn 0RS)WYİn5 B!0Bgzv"G^KL…dPn{Q~QSqf#Q6.]B7l{I}wrޝAg` eJ(7XYi#KSc- ٽ#E *k۹ܔB)]ppdǑB}_7^y' %ǒ SR!DKI9< %JY鹒i)[N!֗hzXH%_o-n $!zחrA)Hxbw  eC|kuЇĩԿZ4&y l$ GEEnH|xEtm.޹C< tʹ)Kf=} (ntӹkR~x,ȥB[E<<4Ь.Zr]ȅ?{Z5>?D'}4zJɵ7]K_) NAyuS,rXܸ􃇎("I(s4 ~8+Ub쭪`ﷴT5wZu*KfB"Dc TqWY&ngyG`W/Pp<b<<,9 pwZ"2!Bf }C&H6P-hnV?${QhF34m8-:rW;.6!=cI%2Mf#2KeaJͦ&yO8)*@C!jՖOW O|uI~HtJp).1c&/'r xX4iZP]TFAp1ZDr^P9?J2__9$ꩊQMo)sHϜT:4iɣO_M_8EKqAUx2( u˺Ab])S޺ ڠd!zyHOXAU4x @Cէ49/uzj`$F,?]PS8L%{n ӒR!5sa- .5/5R&hLM=I)8~촐Rw+% Ȑ9dh)prb/hʋ!Jeˑx]ޝJ+ZlqRm,4:Ux([[ޅFܰpb<6҄p4O~$3~.fDrfξ>L+ m7(T>!]v Bƒhs=3M_4d)m_fac.^by;Ð &m7(H"hK߉PrŚ=LV *aH4-o ؓ1a1ytgOtq$l75V$H҂uBpha=2 <ވ&wg(E9穫gB?ZV.7݄r7LHJOQYQH6S0F`0?; N<-(&:.2P(xCt)Ted խ- ZS#Õ{j2(Smk\NcT% T*{)#gbn4 X(8>`=hv RۜKs(i1Vޥe?dPGuPY$Qq}yWhJe0@!vܹ2XyYDa``DDP`!?(=BMϩyװKdy$4BmEĭ(j}$oW\#YEN>E]³[xoACؙ+(ve# @ P'hT͓ ̬={T!XwYlCDʡ.L$1Re@K+s!Rj$wZ9rO]s;o+5Rw>mQ1dlF ͂C&Cb_$A* HoZF endstream endobj 69 0 obj 11788 endobj 70 0 obj << /Length 71 0 R /Filter /FlateDecode >> stream x]n wu8D Qd50)R!o_3>dCuk"T#hcUmA"L8].³F\:>ưI ToAa0vu,G.h#pPu/¿㠨n~$۟sMҒt W/$agd=tZ WyLZ~&)//0&>>7<3=֙)))ohحbzr M?'W^' endstream endobj 71 0 obj 270 endobj 72 0 obj << /Type /FontDescriptor /FontName /GDKIPY+SourceSansPro-It /FontFamily (Source Sans Pro) /Flags 4 /FontBBox [ -323 -291 2077 969 ] /ItalicAngle 0 /Ascent 984 /Descent -273 /CapHeight 969 /StemV 80 /StemH 80 /FontFile3 68 0 R >> endobj 22 0 obj << /Type /Font /Subtype /Type1 /BaseFont /GDKIPY+SourceSansPro-It /FirstChar 32 /LastChar 122 /FontDescriptor 72 0 R /Encoding /WinAnsiEncoding /Widths [ 200 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 510 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 536 0 0 481 0 531 523 0 0 0 0 0 525 515 0 0 0 0 0 528 0 0 0 0 410 ] /ToUnicode 70 0 R >> endobj 1 0 obj << /Type /Pages /Kids [ 2 0 R 14 0 R 18 0 R ] /Count 3 >> endobj 73 0 obj << /Type /Outlines /First 23 0 R /Last 23 0 R /Count -1 >> endobj 23 0 obj << /Title (Schlussrechnung 7-2019) /Parent 73 0 R /F 0 /Dest [2 0 R /XYZ 68 591 0] >> endobj 74 0 obj << /Names [ (information) [2 0 R /XYZ 380 731 0] (items) [2 0 R /XYZ 68 451 0] (letterheader) [2 0 R /XYZ 68 751 0] (to) [2 0 R /XYZ 68 730 0] (total) [18 0 R /XYZ 380 529 0] ] >> endobj 75 0 obj << /Dests 74 0 R >> endobj 76 0 obj << /Producer (cairo 1.16.0 (https://cairographics.org)) /Title (Invoice) /Author (Klaas Freitag) /Subject (Kraft Invoice document) /Keywords () /CreationDate (D:20200315114801+01'00) >> endobj 77 0 obj << /Type /Catalog /Pages 1 0 R /Outlines 73 0 R /Names 75 0 R >> endobj xref 0 78 0000000000 65535 f 0000092370 00000 n 0000003060 00000 n 0000002814 00000 n 0000000015 00000 n 0000002791 00000 n 0000030656 00000 n 0000034341 00000 n 0000036834 00000 n 0000059686 00000 n 0000062744 00000 n 0000071131 00000 n 0000079223 00000 n 0000075655 00000 n 0000005009 00000 n 0000004820 00000 n 0000003278 00000 n 0000004796 00000 n 0000007585 00000 n 0000007356 00000 n 0000005230 00000 n 0000007332 00000 n 0000091950 00000 n 0000092533 00000 n 0000007806 00000 n 0000029699 00000 n 0000029724 00000 n 0000030350 00000 n 0000030373 00000 n 0000031460 00000 n 0000033445 00000 n 0000033469 00000 n 0000033785 00000 n 0000033808 00000 n 0000034091 00000 n 0000034512 00000 n 0000036141 00000 n 0000036165 00000 n 0000036544 00000 n 0000036567 00000 n 0000037255 00000 n 0000058951 00000 n 0000058976 00000 n 0000059381 00000 n 0000059404 00000 n 0000060141 00000 n 0000061870 00000 n 0000061894 00000 n 0000062196 00000 n 0000062219 00000 n 0000062501 00000 n 0000062913 00000 n 0000070367 00000 n 0000070391 00000 n 0000070833 00000 n 0000070856 00000 n 0000071598 00000 n 0000074752 00000 n 0000074776 00000 n 0000075101 00000 n 0000075124 00000 n 0000075398 00000 n 0000075822 00000 n 0000078356 00000 n 0000078380 00000 n 0000078685 00000 n 0000078708 00000 n 0000078984 00000 n 0000079388 00000 n 0000091275 00000 n 0000091300 00000 n 0000091649 00000 n 0000091672 00000 n 0000092449 00000 n 0000092644 00000 n 0000092857 00000 n 0000092893 00000 n 0000093110 00000 n trailer << /Size 78 /Root 77 0 R /Info 76 0 R >> startxref 93200 %%EOF kraft-0.97/src/000077500000000000000000000000001410616450300133475ustar00rootroot00000000000000kraft-0.97/src/CMakeLists.txt000066400000000000000000000103121410616450300161040ustar00rootroot00000000000000# include_directories(${KDE4_INCLUDES} ${KDE4_INCLUDE_DIR} ${QT_INCLUDES} ${CMAKE_CURRENT_BINARY_DIR} ${KDEPIMLIBS_INCLUDE_DIRS} external) include_directories(${QT_INCLUDES} src) add_subdirectory(pics) ########### next target ############### qt5_add_resources(KRAFT_RC_SRC pics/kraft.qrc) set(kraft_SRCS attribute.cpp einheit.cpp doctype.cpp numbercycle.cpp katalogman.cpp stdsatzman.cpp katalog.cpp templkatalog.cpp docposition.cpp matkatalog.cpp calcdialogbase.cpp timecalcpart.cpp fixcalcpart.cpp floskeltemplate.cpp floskel.cpp calcpart.cpp kraftview.cpp kraftdoc.cpp catalogchapter.cpp addeditchapterdialog.cpp unitmanager.cpp stockmaterial.cpp materialcalcpart.cpp flostempldialog.cpp stdsatzman.cpp materialtempldialog.cpp timecalcdialog.cpp fixcalcdialog.cpp matcalcdialog.h matcalcdialog.cpp templatesaverbase.cpp templatesaverdb.cpp materialsaverbase.cpp materialsaverdb.cpp prefsdialog.cpp prefswages.cpp prefsunits.cpp katalogview.cpp filterheader.cpp kataloglistview.cpp templkatalogview.cpp templkataloglistview.cpp positionviewwidget.cpp documentsaverbase.cpp documentsaverdb.cpp documentman.cpp docdigest.cpp alldocsview.cpp doctypeedit.cpp doctext.cpp portal.cpp portalview.cpp archiveman.cpp reportgenerator.cpp htmlview.cpp docpostcard.cpp catalogtemplate.cpp catalogselection.cpp kraftdocheaderedit.cpp kraftdocfooteredit.cpp inserttempldialog.cpp archdocposition.cpp archdoc.cpp materialkataloglistview.cpp materialkatalogview.cpp materialselectdialog.cpp kraftdocedit.cpp kraftdocpositionsedit.cpp portalhtmlview.cpp templtopositiondialogbase.cpp textselection.cpp newdocassistant.cpp docassistant.cpp texteditdialog.cpp templateprovider.cpp headertemplateprovider.cpp footertemplateprovider.cpp catalogtemplateprovider.cpp texttemplate.cpp grantleetemplate.cpp tagman.cpp itemtagdialog.cpp tagtemplatesdialog.cpp importitemdialog.cpp importfilter.cpp kraftview_ro.cpp taxeditdialog.cpp numbercycledialog.cpp impviewwidgets.cpp setupassistant.cpp models/documentmodel.cpp models/documentproxymodels.cpp models/datemodel.cpp models/docbasemodel.cpp docdigestdetailview.cpp addressprovider.cpp addressselectorwidget.cpp addressprovider_akonadi.cpp addressselectordialog.cpp kraftdb.cpp geld.cpp defaultprovider.cpp metaxmlparser.cpp texttemplateinterface.cpp documenttemplate.cpp pdfconverter.cpp format.cpp ) kconfig_add_kcfg_files(kraft_SRCS databasesettings.kcfgc kraftsettings.kcfgc ) ki18n_wrap_ui(kraft_SRCS calctemplate.ui dbinit.ui createdb.ui dbselect.ui mysqldetails.ui sqlitedetails.ui identity.ui statuspage.ui upgradedb.ui timepart.ui fixpartui.ui matpartui.ui inserttmplbase.ui materialdialog.ui docheader.ui positionwidget.ui docfooter.ui texteditbase.ui positionwidget.ui doctypeeditbase.ui taxeditbase.ui wageseditbase.ui unitseditbase.ui importtodocbase.ui numbercycleseditbase.ui ) set(KRAFT_LINK_LIBS Qt5::Core Qt5::Widgets Qt5::Sql Qt5::Xml KF5::I18n KF5::Contacts KF5::ConfigCore KF5::ConfigGui ${CTEMPLATE_LIBRARIES} pthread Grantlee5::Templates ) if(KF5Akonadi_FOUND) list(APPEND KRAFT_LINK_LIBS KF5::AkonadiCore KF5::AkonadiContact KF5::AkonadiAgentBase KF5::AkonadiWidgets KF5::AkonadiXml ) endif() add_library(kraftlib STATIC ${KRAFT_RC_SRC} ${kraft_SRCS}) add_executable(kraft main.cpp) set_target_properties(kraft PROPERTIES OUTPUT_NAME kraft) target_link_libraries(kraftlib PUBLIC ${KRAFT_LINK_LIBS} ) target_link_libraries(kraft PUBLIC kraftlib ) install(TARGETS kraft ${INSTALL_TARGETS_DEFAULT_ARGS}) ########### install files ############### install(FILES kraftsettings.kcfg databasesettings.kcfg DESTINATION ${KCFG_INSTALL_DIR}) install(FILES kraftui.rc katalogview.rc DESTINATION ${KXMLGUI_INSTALL_DIR}/kraft) kraft-0.97/src/Kraft2.config000066400000000000000000000000371410616450300156670ustar00rootroot00000000000000// ADD PREDEFINED MACROS HERE! kraft-0.97/src/Kraft2.creator000066400000000000000000000000501410616450300160540ustar00rootroot00000000000000[General] [General] [General] [General] kraft-0.97/src/Kraft2.files000066400000000000000000000073561410616450300155370ustar00rootroot00000000000000addressselection.cpp addressselection.h addresstemplateprovider.cpp addresstemplateprovider.h archdoc.cpp archdoc.h archdocposition.cpp archdocposition.h archiveman.cpp archiveman.h attribute.cpp attribute.h brunskatalog.cpp brunskatalog.h brunskataloglistview.cpp brunskataloglistview.h brunskatalogview.cpp brunskatalogview.h brunsrecord.cpp brunsrecord.h brunsviewer.cpp brunsviewer.h brunsviewmain.cpp calcpart.cpp calcpart.h catalogchapteredit.cpp catalogchapteredit.h catalogselection.cpp catalogselection.h catalogtemplate.cpp catalogtemplate.h catalogtemplateprovider.cpp catalogtemplateprovider.h CMakeLists.txt dbids.h defaultprovider.cpp defaultprovider.h docassistant.cpp docassistant.h docdigest.cpp docdigest.h docdigestview.cpp docdigestview.h docfooter.ui docguardedptr.h docheader.ui doclocaledialog.cpp doclocaledialog.h docposition.cpp docposition.h docpostcard.cpp docpostcard.h doctext.cpp doctext.h doctextsview.ui doctype.cpp doctype.h doctypedetailseditbase.ui doctypeedit.cpp doctypeedit.h doctypeeditbase.ui documentman.cpp documentman.h documentsaverbase.cpp documentsaverbase.h documentsaverdb.cpp documentsaverdb.h einheit.cpp einheit.h extendedcombo.cpp extendedcombo.h filterheader.cpp filterheader.h fixcalcdialog.cpp fixcalcdialog.h fixcalcpart.cpp fixcalcpart.h fixpartui.ui floskel.cpp floskel.h floskeltemplate.cpp floskeltemplate.h flostab.ui flostempldialog.cpp flostempldialog.h footertemplateprovider.cpp footertemplateprovider.h geld.cpp geld.h headerselection.cpp headerselection.h headertemplateprovider.cpp headertemplateprovider.h htmlview.cpp htmlview.h imporst.cpp importfilter.cpp importfilter.h importitemdialog.cpp importitemdialog.h importtodocbase.ui insertplantbase.ui insertplantdialog.cpp insertplantdialog.h inserttempldialog.cpp inserttempldialog.h inserttmplbase.ui katalog.cpp katalog.h kataloglistview.cpp kataloglistview.h katalogman.cpp katalogman.h katalogview.cpp katalogview.h kraftdb.cpp kraftdb.h kraftdoc.cpp kraftdoc.h kraftdocedit.cpp kraftdocedit.h kraftdocfooteredit.cpp kraftdocfooteredit.h kraftdocheaderedit.cpp kraftdocheaderedit.h kraftdocpositionsedit.cpp kraftdocpositionsedit.h kraftglobals.h kraftview.cpp kraftview.h kraftview_ro.cpp kraftview_ro.h main.cpp matcalcdialog.cpp matcalcdialog.h materialcalcpart.cpp materialcalcpart.h materialdialog.ui materialkataloglistview.cpp materialkataloglistview.h materialkatalogview.cpp materialkatalogview.h materialsaverbase.cpp materialsaverbase.h materialsaverdb.cpp materialsaverdb.h materialselectdialog.cpp materialselectdialog.h materialtempldialog.cpp materialtempldialog.h matkatalog.cpp matkatalog.h matpartui.ui matpartui.ui.h newdocassistant.cpp newdocassistant.h numbercycle.cpp numbercycle.h numbercycledialog.cpp numbercycledialog.h numbercycleseditbase.ui pics/CMakeLists.txt portal.cpp portal.h portalhtmlview.cpp portalhtmlview.h portalview.cpp portalview.h portinglog.txt positiontagdialog.cpp positiontagdialog.h positionviewwidget.cpp positionviewwidget.h positionwidget.ui prefsdialog.cpp prefsdialog.h reportgenerator.cpp reportgenerator.h stdsatzman.cpp stdsatzman.h stockmaterial.cpp stockmaterial.h stockmaterialman.cpp stockmaterialman.h tagman.cpp tagman.h tagtemplatesdialog.cpp tagtemplatesdialog.h templateprovider.cpp templateprovider.h templatesaverbase.cpp templatesaverbase.h templatesaverdb.cpp templatesaverdb.h templkatalog.cpp templkatalog.h templkataloglistview.cpp templkataloglistview.h templkatalogview.cpp templkatalogview.h templtopositiondialogbase.cpp templtopositiondialogbase.h texteditbase.ui texteditdialog.cpp texteditdialog.h textselection.cpp textselection.h texttemplate.cpp texttemplate.h timecalcpart.cpp timecalcpart.h unitmanager.cpp unitmanager.h version.h zeitcalcdialog.cpp zeitcalcdialog.h zeitcalcpart.cpp zeitcalcpart.h zeitpartui.uikraft-0.97/src/Kraft2.includes000066400000000000000000000000001410616450300162160ustar00rootroot00000000000000kraft-0.97/src/Messages.sh000066400000000000000000000006241410616450300154540ustar00rootroot00000000000000#!bin/sh EXTRACTRC=$KRAFT_HOME/src/extractrc XGETTEXT=xgettext if [ -z "$KRAFT_HOME" ]; then echo "Need to set KRAFT_HOME" exit 1 fi podir=$KRAFT_HOME/po echo "PODir: " $podir pushd $KRAFT_HOME/src $EXTRACTRC `find . -name \*.rc -o -name \*.ui -o -name \*.kcfg` >> rc.cpp $XGETTEXT --kde --language=C++ --from-code=UTF-8 `find . -name \*.cc -o -name \*.cpp -o -name \*.h` -o $podir/kraft.pot kraft-0.97/src/addeditchapterdialog.cpp000066400000000000000000000055251410616450300202070ustar00rootroot00000000000000/*************************************************************************** addeditchapter - add and edit catalog chapters ------------------- begin : Sat Nov 6 2010 copyright : (C) 2010 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include #include #include #include #include #include "addeditchapterdialog.h" #include "catalogchapter.h" AddEditChapterDialog::AddEditChapterDialog( QWidget *parent ) :QDialog( parent ) { setObjectName( "CHAPTER_EDIT_DIALOG" ); setModal( true ); setWindowTitle( i18n( "Add/Edit Catalog Chapter" ) ); QVBoxLayout *vbox = new QVBoxLayout; this->setLayout( vbox ); mTopLabel = new QLabel(); mTopLabel->setText( i18n("Create a new Catalog Chapter")); vbox->addWidget( mTopLabel ); vbox->addWidget( new QLabel( i18n("Chapter Name:"))); mNameEdit = new QLineEdit; vbox->addWidget( mNameEdit ); vbox->addWidget( new QLabel( i18n("Chapter Description:"))); mDescEdit = new QLineEdit; vbox->addWidget( mDescEdit ); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); vbox->addWidget(buttonBox); } QString AddEditChapterDialog::name() const { return mNameEdit->text(); } QString AddEditChapterDialog::description() const { return mDescEdit->text(); } void AddEditChapterDialog::setParentChapter( const CatalogChapter& chapter ) { mParentChapter = chapter; mTopLabel->setText( i18n("Create new Catalog Chapter below chapter %1", chapter.name() )); } void AddEditChapterDialog::setEditChapter( const CatalogChapter& chapter ) { mChapter = chapter; mTopLabel->setText( i18n("Edit name and description of chapter %1", chapter.name() )); mNameEdit->setText( chapter.name() ); mDescEdit->setText( chapter.description() ); } kraft-0.97/src/addeditchapterdialog.h000066400000000000000000000031511410616450300176450ustar00rootroot00000000000000/*************************************************************************** addeditchapter - add and edit catalog chapters ------------------- begin : Sat Nov 6 2010 copyright : (C) 2010 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef ADDEDITCHAPTERDIALOG_H #define ADDEDITCHAPTERDIALOG_H #include #include #include #include "catalogchapter.h" class AddEditChapterDialog : public QDialog { Q_OBJECT public: AddEditChapterDialog(QWidget *parent = 0); void setEditChapter( const CatalogChapter& ); void setParentChapter( const CatalogChapter& ); QString name() const; QString description() const; private: CatalogChapter mChapter; CatalogChapter mParentChapter; QLineEdit *mNameEdit; QLineEdit *mDescEdit; QLabel *mTopLabel; }; #endif // ADDEDITCHAPTERDIALOG_H kraft-0.97/src/addressprovider.cpp000066400000000000000000000112371410616450300172570ustar00rootroot00000000000000/*************************************************************************** addressprovider.cpp - ------------------- begin : Fri Mar 4 2011 copyright : (C) 2011 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include "addressprovider.h" #include // FIXME this needs to change once there are more address book providers, ie. // on Mac. #include "addressprovider_akonadi.h" /* ==================================================================================== */ AddressProvider::AddressProvider( QObject *parent ) :QObject( parent ), _d( new AddressProviderPrivate(parent) ) { connect(_d, SIGNAL(addresseeFound(QString, KContacts::Addressee)), this, SLOT(slotAddresseeFound(QString, KContacts::Addressee))); connect(_d, SIGNAL(lookupError( QString, QString)), this, SLOT(slotErrorMsg(QString, QString))); connect(_d, SIGNAL(addresseeNotFound(QString)), SLOT(slotAddresseeNotFound(QString))); } bool AddressProvider::backendUp() { return _d->backendUp(); } QString AddressProvider::backendName() const { return _d->backendName(); } void AddressProvider::slotAddresseeFound( const QString& uid, const KContacts::Addressee contact) { // remove a potential error message in case an error happened before if( !( uid.isEmpty() || contact.isEmpty()) ) { _errMessages.remove(uid); } _addressCache[uid] = contact; _addressCache[uid].insertCustom(CUSTOM_ADDRESS_MARKER, "addressbook"); emit lookupResult(uid, _addressCache[uid]); } void AddressProvider::slotAddresseeNotFound( const QString& uid ) { KContacts::Addressee contact; // Empty for not found. _notFoundUids.insert(uid); emit lookupResult(uid, contact); } void AddressProvider::slotResetNotFoundCache() { _notFoundUids.clear(); } void AddressProvider::slotErrorMsg(const QString& uid, const QString& msg) { if( !uid.isEmpty() ) { _errMessages[uid] = msg; } } QString AddressProvider::errorMsg( const QString& uid ) { if( !_d->backendUp() ) { return "Backend down"; } if( _errMessages.contains(uid) ) { return _errMessages[uid]; } return QString(); } KContacts::Addressee AddressProvider::getAddresseeFromCache(const QString& uid) { KContacts::Addressee adr; if( _addressCache.contains(uid)) { adr = _addressCache[uid]; } return adr; } AddressProvider::LookupState AddressProvider::lookupAddressee( const QString& uid ) { // FIXME: Check for the size of the err messages. If it is big, // maybe do not bother the backend more if( !_d->backendUp() ) { return BackendError; } if( _notFoundUids.contains(uid)) { // qDebug() << uid << "was not found before"; return LookupNotFound; } if( _addressCache.contains(uid)) { return LookupFromCache; } if( _d->isSearchOngoing(uid) ) { return LookupOngoing; } if( _d->lookupAddressee(uid) ) { return LookupStarted; } return ItemError; } KContacts::Addressee AddressProvider::getAddressee(const QModelIndex& indx) { return _d->getAddressee(indx); } KContacts::Addressee AddressProvider::getAddressee( int row, const QModelIndex &parent) { return _d->getAddressee(row, parent); } QAbstractItemModel *AddressProvider::model() { return _d->model(); } QString AddressProvider::formattedAddress( const KContacts::Addressee& contact ) const { QString re; KContacts::Address address; address = contact.address( KContacts::Address::Pref ); if( address.isEmpty() ) address = contact.address(KContacts::Address::Work ); if( address.isEmpty() ) address = contact.address(KContacts::Address::Home ); if( address.isEmpty() ) address = contact.address(KContacts::Address::Postal ); if( address.isEmpty() ) { re = contact.realName(); } else { re = address.formattedAddress( contact.realName(), contact.organization() ); } return re; } kraft-0.97/src/addressprovider.h000066400000000000000000000101661410616450300167240ustar00rootroot00000000000000/*************************************************************************** addressprovider.h ------------------- begin : Fri Mar 4 2011 copyright : (C) 2011 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef ADDRESSPROVIDER_H #define ADDRESSPROVIDER_H #include #include #include // use define CUSTOM_ADDRESS_MARKER to mark the origin of addresses with .insertCustom #define CUSTOM_ADDRESS_MARKER "kraft", "identity_source" class AddressProviderPrivate; class AddressProvider : public QObject { Q_OBJECT public: AddressProvider( QObject* parent = 0 ); enum LookupState { LookupOngoing, LookupStarted, LookupFromCache, LookupNotFound, ItemError, BackendError }; bool backendUp(); QString backendName() const; /** * @brief lookupAddressee - look up an addressee by it's uid. * @param uid - A unique string identifying the contact * * This is asynchron an asynchronous method that returns immediately. * Connect to the signal lookupResult() for the result. * * Make sure to always use a non empty uid. */ LookupState lookupAddressee( const QString& uid ); QString formattedAddress( const KContacts::Addressee& ) const; /** * @brief return an address from cache * @param uid - the unique address uid * * Use this method to get the address if the lookupAddressee method * returned LookupFromCache. In this case, the address is already * known and can be fetched synchronously */ KContacts::Addressee getAddresseeFromCache(const QString& uid); /** * @brief model - returns an Qt model for a tree view. * @return a QAbstractItemModel */ QAbstractItemModel *model(); /** * @brief getAddressee - get the contact from an index * @param indx * * Depending on the underlying model in the private implementation * advantage can be taken from this functions. * Used by the addressselectorwidget * @return the found addressee */ KContacts::Addressee getAddressee(const QModelIndex& indx); KContacts::Addressee getAddressee( int row, const QModelIndex &parent = QModelIndex()); /** * @brief errorMsg - returns the error string for retrieval by uid * @param uid - the UID of the lookup job * * If the lookup job failed for whatever reason, this returns an error * message why, if that was available from the private implementation. * @return QString error message */ QString errorMsg( const QString& uid ); public slots: void slotResetNotFoundCache(); protected slots: void slotErrorMsg(const QString& uid, const QString& msg); void slotAddresseeFound( const QString& uid, const KContacts::Addressee contact); void slotAddresseeNotFound( const QString& uid ); signals: /** * @brief lookupResult - deliver lookup result * @param uid - the uid of the lookup, and the contact * * If the contact is empty, it was simply not found. It can be checked * if there was an error using the errorMsg method for the uid. If there * was no error (errorMsg returns empty string) the addressbook just * did not contain the address. */ void lookupResult( const QString&, const KContacts::Addressee& ); private: QHash _addressCache; AddressProviderPrivate *_d; QHash _errMessages; QSet _notFoundUids; }; #endif // ADDRESSPROVIDER_H kraft-0.97/src/addressprovider_akonadi.cpp000066400000000000000000000160351410616450300207460ustar00rootroot00000000000000/*************************************************************************** addressprovider.cpp - ------------------- begin : Fri Mar 4 2011 copyright : (C) 2011 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include "addressprovider_akonadi.h" #include #include #ifdef HAVE_AKONADI #include #include #include "akonadi/contact/contactsearchjob.h" #include #include #include #include #include using namespace Akonadi; #endif AddressProviderPrivate::AddressProviderPrivate( QObject *parent ) :QObject( parent ), _akonadiUp(false), mSession(0), mMonitor(0), _model(0) { init(); } bool AddressProviderPrivate::init() { _akonadiUp = false; #ifdef HAVE_AKONADI if ( !Akonadi::Control::start( ) ) { qDebug() << "Failed to start Akonadi!"; } else { mSession = new Akonadi::Session( "KraftSession" ); _akonadiUp = true; } #endif return _akonadiUp; } QString AddressProviderPrivate::backendName() const { #ifdef HAVE_AKONADI return QStringLiteral("Akonadi"); #else return QStringLiteral("Akonadi disabled by compile option!"); #endif } bool AddressProviderPrivate::backendUp() { return _akonadiUp; } bool AddressProviderPrivate::isSearchOngoing(const QString& uid) { return mUidSearches.contains(uid); } bool AddressProviderPrivate::lookupAddressee( const QString& uid ) { if( uid.isEmpty() ) { qDebug() << "Invalid: UID to lookup is empty."; return false; } if( mUidSearches.contains( uid ) ) { // search is already running // qDebug () << "Search already underways!";^ return false; } #ifdef HAVE_AKONADI Akonadi::ContactSearchJob *csjob = new Akonadi::ContactSearchJob( this ); csjob->setLimit( 1 ); csjob->setQuery(ContactSearchJob::ContactUid , uid, ContactSearchJob::ExactMatch); csjob->setProperty("UID", uid); connect( csjob, SIGNAL( result( KJob* ) ), this, SLOT( searchResult( KJob* ) ) ); csjob->start(); mUidSearches.insert( uid ); model(); return true; #endif return false; } #ifdef HAVE_AKONADI void AddressProviderPrivate::searchResult( KJob* job ) { if( !job ) return; QString uid; KContacts::Addressee contact; uid = job->property("UID").toString(); if( job->error() ) { // both uid and err message can be empty const QString errMsg = job->errorString(); emit lookupError(uid, errMsg ); // qDebug () << "Address Search job failed: " << job->errorString(); } else { Akonadi::ContactSearchJob *searchJob = qobject_cast( job ); const KContacts::Addressee::List contacts = searchJob->contacts(); KContacts::Addressee::List(); // qDebug () << "Found list of " << contacts.size() << " addresses as search result"; if( contacts.size() > 0 ) { contact = contacts[0]; // qDebug() << "Found uid search job for UID " << uid << " = " << contact.realName(); emit addresseeFound(uid, contact); } else { // qDebug() << "No search result for UID" << uid; emit addresseeNotFound(uid); } } // cleanup if(!uid.isEmpty()) { mUidSearches.remove( uid ); } job->deleteLater(); } #endif QAbstractItemModel* AddressProviderPrivate::model() { if( !_akonadiUp ) { return 0; } #ifdef HAVE_AKONADI Akonadi::ItemFetchScope scope; // fetch all content of the contacts, including images scope.fetchFullPayload( true ); // fetch the EntityDisplayAttribute, which contains custom names and icons scope.fetchAttribute(); if( ! mMonitor ) { mMonitor = new Akonadi::ChangeRecorder; mMonitor->setSession( mSession ); // include fetching the collection tree mMonitor->fetchCollection( true ); // set the fetch scope that shall be used mMonitor->setItemFetchScope( scope ); // monitor all collections below the root collection for changes mMonitor->setCollectionMonitored( Akonadi::Collection::root() ); // list only contacts and contact groups mMonitor->setMimeTypeMonitored( KContacts::Addressee::mimeType(), true ); mMonitor->setMimeTypeMonitored( KContacts::ContactGroup::mimeType(), true ); } if( !_model ) { _model = new Akonadi::ContactsTreeModel( mMonitor ); Akonadi::ContactsTreeModel::Columns columns; columns << Akonadi::ContactsTreeModel::FullName; columns << Akonadi::ContactsTreeModel::HomeAddress; // columns << Akonadi::ContactsTreeModel::FamilyName; // columns << Akonadi::ContactsTreeModel::GivenName; _model->setColumns( columns ); } return _model; #endif return 0; } KContacts::Addressee AddressProviderPrivate::getAddressee(const QModelIndex& indx) { KContacts::Addressee contact; #ifdef HAVE_AKONADI if( indx.isValid() ) { const Akonadi::Item item = indx.data(Akonadi::EntityTreeModel::ItemRole).value(); if (item.hasPayload()) { contact = item.payload(); } } #endif return contact; } KContacts::Addressee AddressProviderPrivate::getAddressee(int row, const QModelIndex &parent) { #ifdef HAVE_AKONADI const QModelIndex index = _model->index(row, 0, parent); return getAddressee(index); #endif return KContacts::Addressee(); } QString AddressProviderPrivate::formattedAddress( const KContacts::Addressee& contact ) const { QString re; KContacts::Address address; address = contact.address( KContacts::Address::Pref ); if( address.isEmpty() ) address = contact.address(KContacts::Address::Work ); if( address.isEmpty() ) address = contact.address(KContacts::Address::Home ); if( address.isEmpty() ) address = contact.address(KContacts::Address::Postal ); if( address.isEmpty() ) { re = contact.realName(); } else { re = address.formattedAddress( contact.realName(), contact.organization() ); } return re; } kraft-0.97/src/addressprovider_akonadi.h000066400000000000000000000054111410616450300204070ustar00rootroot00000000000000/*************************************************************************** addressprovider.h ------------------- begin : Fri Mar 4 2011 copyright : (C) 2011 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef ADDRESSPROVIDER_AKONADI_H #define ADDRESSPROVIDER_AKONADI_H #include #include #include #ifdef HAVE_AKONADI #include #include #include #include #endif class QAbstractItemModel; class AddressItemModel; // An akonadi based provider. class AddressProviderPrivate : public QObject { Q_OBJECT public: AddressProviderPrivate( QObject* parent = 0 ); // initialize the backend and return true if that worked. bool init(); // returns the result of the init process later on bool backendUp(); QString backendName() const; bool lookupAddressee( const QString& uid ); QString formattedAddress( const KContacts::Addressee& ) const; QAbstractItemModel *model(); KContacts::Addressee getAddressee(int row, const QModelIndex &parent); KContacts::Addressee getAddressee(const QModelIndex& indx); bool isSearchOngoing(const QString& uid); #ifdef HAVE_AKONADI public slots: void searchResult( KJob* ); #endif signals: // void addresseeFound( const QString&, const KContacts::Addressee& ); void addresseeNotFound( const QString& ); // error message when looking up the address for a UID void lookupError( const QString&, const QString&); // emitted when the search is finished, even if there was no result. void finished( int ); private: QSet mUidSearches; bool _akonadiUp; #ifdef HAVE_AKONADI Akonadi::Session *mSession; Akonadi::ChangeRecorder* mMonitor; // FIXME: Must static somehow Akonadi::ContactsTreeModel *_model; #else void *mSession; void *mMonitor; void *_model; #endif }; #endif // ADDRESSPROVIDER_AKONADI_H kraft-0.97/src/addressselectordialog.cpp000066400000000000000000000052741410616450300204310ustar00rootroot00000000000000/*************************************************************************** addressselectordialog.cpp - select addressee from address book. ------------------- begin : Sept. 2016 copyright : (C) 2016 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include #include #include #include #include "kraftsettings.h" #include "addressselectorwidget.h" #include "addressselectordialog.h" AddressSelectorDialog::AddressSelectorDialog( QWidget *parent ) :QDialog(parent) { setModal( true ); const QByteArray geo = QByteArray::fromBase64( KraftSettings::self()->addressSelectDialogSize().toAscii() ); restoreGeometry(geo); _addressSelectorWidget = new AddressSelectorWidget(this, false); connect(_addressSelectorWidget, SIGNAL(addressSelected(KContacts::Addressee)), SLOT(slotAddresseeSelected(KContacts::Addressee))); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel, this); QVBoxLayout *mainLayout = new QVBoxLayout; setLayout(mainLayout); mainLayout->addWidget(_addressSelectorWidget); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); mainLayout->addWidget(buttonBox); } void AddressSelectorDialog::slotAddresseeSelected( const KContacts::Addressee& addressee ) { _addressee = addressee; _addressee.insertCustom(CUSTOM_ADDRESS_MARKER, "addressbook"); } KContacts::Addressee AddressSelectorDialog::addressee() { return _addressee; } void AddressSelectorDialog::done(int r) { const QByteArray geo = saveGeometry().toBase64(); KraftSettings::self()->setAddressSelectDialogSize(geo); _addressSelectorWidget->saveState(); QDialog::done(r); } kraft-0.97/src/addressselectordialog.h000066400000000000000000000030601410616450300200650ustar00rootroot00000000000000/*************************************************************************** addressselectordialog.h - select addressee from address book. ------------------- begin : Sept. 2012 copyright : (C) 2012 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef ADDRESSSELECTORDIALOG_H #define ADDRESSSELECTORDIALOG_H #include #include class AddressSelectorWidget; using namespace KContacts; class AddressSelectorDialog : public QDialog { Q_OBJECT public: AddressSelectorDialog( QWidget *parent = 0 ); KContacts::Addressee addressee(); void done(int r); private slots: void slotAddresseeSelected(const KContacts::Addressee&); private: AddressSelectorWidget *_addressSelectorWidget; Addressee _addressee; }; #endif // ADDRESSSELECTORDIALOG_H kraft-0.97/src/addressselectorwidget.cpp000066400000000000000000000304531410616450300204520ustar00rootroot00000000000000/*************************************************************************** AddressSelectorWidget - Address Selection Widget based on Akonadi ------------------- begin : Jul 2011 copyright : (C) 2011 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include "kraftsettings.h" #include "addressselectorwidget.h" #include "addressprovider.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_AKONADI #include #include #endif /* ==================================================================== */ AddressSortProxyModel::AddressSortProxyModel(AddressProvider *provider, QObject *parent) : QSortFilterProxyModel(parent), _provider(provider) { // setFilterRole(ActivityItemDelegate::ActionTextRole); setFilterKeyColumn(0); } #if 0 bool AddressSortProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const { QVariant leftData = sourceModel()->data(left); QVariant rightData = sourceModel()->data(right); if (leftData.type() == QVariant::DateTime) { return leftData.toDateTime() < rightData.toDateTime(); } else { qDebug() << "OOOOO " << endl; } return true; } #endif static bool addressMatchesFilter(const KContacts::Address &address, const QString &filterString) { if (address.street().contains(filterString, Qt::CaseInsensitive)) { return true; } if (address.locality().contains(filterString, Qt::CaseInsensitive)) { return true; } if (address.region().contains(filterString, Qt::CaseInsensitive)) { return true; } if (address.postalCode().contains(filterString, Qt::CaseInsensitive)) { return true; } if (address.country().contains(filterString, Qt::CaseInsensitive)) { return true; } if (address.label().contains(filterString, Qt::CaseInsensitive)) { return true; } if (address.postOfficeBox().contains(filterString, Qt::CaseInsensitive)) { return true; } return false; } static bool contactMatchesFilter(const KContacts::Addressee &contact, const QString &filterString) { if (contact.assembledName().contains(filterString, Qt::CaseInsensitive)) { return true; } if (contact.formattedName().contains(filterString, Qt::CaseInsensitive)) { return true; } if (contact.nickName().contains(filterString, Qt::CaseInsensitive)) { return true; } if (contact.birthday().toString().contains(filterString, Qt::CaseInsensitive)) { return true; } const KContacts::Address::List addresses = contact.addresses(); int count = addresses.count(); for (int i = 0; i < count; ++i) { if (addressMatchesFilter(addresses.at(i), filterString)) { return true; } } const KContacts::PhoneNumber::List phoneNumbers = contact.phoneNumbers(); count = phoneNumbers.count(); for (int i = 0; i < count; ++i) { if (phoneNumbers.at(i).number().contains(filterString, Qt::CaseInsensitive)) { return true; } } const QStringList emails = contact.emails(); count = emails.count(); for (int i = 0; i < count; ++i) { if (emails.at(i).contains(filterString, Qt::CaseInsensitive)) { return true; } } const QStringList categories = contact.categories(); count = categories.count(); for (int i = 0; i < count; ++i) { if (categories.at(i).contains(filterString, Qt::CaseInsensitive)) { return true; } } if (contact.mailer().contains(filterString, Qt::CaseInsensitive)) { return true; } if (contact.title().contains(filterString, Qt::CaseInsensitive)) { return true; } if (contact.role().contains(filterString, Qt::CaseInsensitive)) { return true; } if (contact.organization().contains(filterString, Qt::CaseInsensitive)) { return true; } if (contact.department().contains(filterString, Qt::CaseInsensitive)) { return true; } if (contact.note().contains(filterString, Qt::CaseInsensitive)) { return true; } if (contact.url().url().url().contains(filterString, Qt::CaseInsensitive)) { return true; } const QStringList customs = contact.customs(); count = customs.count(); for (int i = 0; i < count; ++i) { if (customs.at(i).contains(filterString, Qt::CaseInsensitive)) { return true; } } return false; } bool contactGroupMatchesFilter(const KContacts::ContactGroup &group, const QString &filterString) { if (group.name().contains(filterString, Qt::CaseInsensitive)) { return true; } const uint count = group.dataCount(); for (uint i = 0; i < count; ++i) { if (group.data(i).name().contains(filterString, Qt::CaseInsensitive)) { return true; } if (group.data(i).email().contains(filterString, Qt::CaseInsensitive)) { return true; } } return false; } bool AddressSortProxyModel::filterAcceptsRow(int row, const QModelIndex &parent) const { KContacts::Addressee contact = _provider->getAddressee(row, parent); if( contact.isEmpty() ) { return true; } else { const QString p = filterRegExp().pattern(); return contactMatchesFilter(contact, p ); } return true; } QVariant AddressSortProxyModel::headerData(int section, Qt::Orientation orientation, int role) const { if ( orientation == Qt::Horizontal && role == Qt::DisplayRole) { if( section == 0 ) { return i18n("Name"); } else if ( section == 1 ) { return i18n("Address"); } } return QVariant(); } /* ------------------------------------------------------------------------------ */ KraftContactViewer::KraftContactViewer(QWidget *parent) :QWidget(parent) #ifdef HAVE_AKONADI , _contactViewer(0) #endif { QVBoxLayout *lay = new QVBoxLayout; lay->setMargin(0); setLayout(lay); #ifdef HAVE_AKONADI _contactViewer = new Akonadi::ContactViewer; _contactViewer->setShowQRCode(false); lay->addWidget(_contactViewer); #endif } void KraftContactViewer::setContact( const KContacts::Addressee& contact) { #ifdef HAVE_AKONADI _contactViewer->setRawContact(contact); #else Q_UNUSED(contact); #endif } /* ------------------------------------------------------------------------------ */ AddressSelectorWidget::AddressSelectorWidget(QWidget *parent, bool /* showText */) : QSplitter(parent), _provider(0) { setupUi(); restoreState(); } AddressSelectorWidget::~AddressSelectorWidget() { } void AddressSelectorWidget::setupUi() { _provider = new AddressProvider(this); setChildrenCollapsible(false); // Left page of the splitter QWidget *leftW = new QWidget; QVBoxLayout *leftLay = new QVBoxLayout; leftW->setLayout(leftLay); this->addWidget(leftW); QHBoxLayout *searchLay = new QHBoxLayout; leftLay->addLayout(searchLay); QLabel *searchLabel = new QLabel(i18n("&Search:")); searchLay->addWidget( searchLabel ); QLineEdit *edit = new QLineEdit; searchLabel->setBuddy(edit); edit->setClearButtonEnabled(true); searchLay->addWidget( edit ); connect(edit, SIGNAL(textChanged(QString)), SLOT(slotFilterTextChanged(QString))); #ifdef HAVE_AKONADI _addressTreeView = new Akonadi::EntityTreeView( this ); #else _addressTreeView = new QTreeView; #endif _addressTreeView->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); leftLay->addWidget(_addressTreeView); mProxyModel = new AddressSortProxyModel(_provider, this); mProxyModel->setSourceModel(_provider->model()); _addressTreeView->setModel(mProxyModel); connect(_addressTreeView, SIGNAL(activated(QModelIndex)), this, SLOT(slotAddresseeSelected(QModelIndex))); mProxyModel->sort(0); // the right side QWidget *rightW = new QWidget; QVBoxLayout *rightLay = new QVBoxLayout; rightW->setLayout(rightLay); this->addWidget(rightW); _contactViewer = new KraftContactViewer; _contactViewer->setMinimumWidth(200); rightLay->addWidget(_contactViewer); // Buttons to create and edit QHBoxLayout *hboxBot = new QHBoxLayout; hboxBot->addStretch(4); rightLay->addLayout( hboxBot ); mButEditContact = new QPushButton(i18n("Edit Contact...")); mButEditContact->setToolTip( i18n("Edit the currently selected contact" )); mButEditContact->setEnabled( false ); hboxBot->addWidget( mButEditContact ); QPushButton *butCreateContact = new QPushButton(i18n("New Contact...")); butCreateContact->setToolTip( i18n("Create a new Contact" ) ); hboxBot->addWidget( butCreateContact ); connect(butCreateContact,SIGNAL(clicked()),SLOT(slotCreateNewContact())); connect(mButEditContact, SIGNAL(clicked()),SLOT(slotEditContact())); setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); } void AddressSelectorWidget::slotFilterTextChanged( const QString& filter) { // qDebug() << "Filter: " << filter; mProxyModel->setFilterRegExp(QRegExp(filter, Qt::CaseInsensitive, QRegExp::RegExp)); // mProxyModel.setFilterFixedString(filter); } void AddressSelectorWidget::restoreState() { const QList sizes = KraftSettings::self()->addressPickerSplitterSize(); setSizes(sizes); const QByteArray state = QByteArray::fromBase64( KraftSettings::self()->addressPickerTreeviewState().toAscii() ); _addressTreeView->header()->restoreState(state); } void AddressSelectorWidget::saveState() { const QList s = sizes(); KraftSettings::self()->setAddressPickerSplitterSize(s); const QByteArray state = _addressTreeView->header()->saveState().toBase64(); KraftSettings::self()->setAddressPickerTreeviewState(state); } bool AddressSelectorWidget::backendUp() const { bool re = false; if( _provider ) { re = _provider->backendUp(); } return re; } void AddressSelectorWidget::slotCreateNewContact() { #ifdef HAVE_AKONADI // FIXME _addressEditor.reset(new Akonadi::ContactEditorDialog( Akonadi::ContactEditorDialog::CreateMode, this )); _addressEditor->show(); #endif } void AddressSelectorWidget::slotAddresseeSelected(QModelIndex index) { if ( index.isValid() ) { QModelIndex sourceIdx = mProxyModel->mapToSource(index); KContacts::Addressee contact = _provider->getAddressee( sourceIdx ); qDebug() << "----------- " << contact.formattedName() << contact.uid(); _contactViewer->setContact(contact); emit addressSelected(contact); mButEditContact->setEnabled( true ); } else { // qDebug () << "No address was selected!"; mButEditContact->setEnabled( false ); } } void AddressSelectorWidget::slotEditContact() { #ifdef HAVE_AKONADI if( _addressTreeView->selectionModel()->hasSelection() ) { QModelIndex index = _addressTreeView->selectionModel()->currentIndex(); if ( index.isValid() ) { const Akonadi::Item item = index.data( Akonadi::EntityTreeModel::ItemRole ).value(); if ( item.isValid() && item.hasPayload() ) { _addressEditor.reset(new Akonadi::ContactEditorDialog( Akonadi::ContactEditorDialog::EditMode, this )); _addressEditor->setContact( item ); _addressEditor->show(); } } } #endif } kraft-0.97/src/addressselectorwidget.h000066400000000000000000000062341410616450300201170ustar00rootroot00000000000000/*************************************************************************** addressselectorwidget - Address Selection Widget ------------------- begin : Jul 2011 copyright : (C) 2011- by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef ADDRESSSELECTORWIDGET_H #define ADDRESSSELECTORWIDGET_H #include #include #include #ifdef HAVE_AKONADI #include #include #endif #include #include "addressprovider.h" using namespace KContacts; class QLabel; class QTreeView; class QuickSearchWidget; class QItemSelectionModel; class QuickSearchWidget; class QPushButton; /* =============================================================== */ class KraftContactViewer : public QWidget { Q_OBJECT public: explicit KraftContactViewer(QWidget *parent = 0); void setContact( const KContacts::Addressee& contact); private: #ifdef HAVE_AKONADI Akonadi::ContactViewer *_contactViewer; #endif }; class AddressSortProxyModel : public QSortFilterProxyModel { Q_OBJECT public: AddressSortProxyModel(AddressProvider *provider, QObject *parent = 0); QVariant headerData(int section, Qt::Orientation orientation, int role) const; protected: bool filterAcceptsRow(int row, const QModelIndex &parent) const; // bool lessThan(const QModelIndex &left, const QModelIndex &right) const Q_DECL_OVERRIDE; public: QString mFilter; AddressProvider *_provider; }; /* =============================================================== */ class AddressSelectorWidget : public QSplitter { Q_OBJECT public: explicit AddressSelectorWidget(QWidget *parent = 0, bool showText = true ); ~AddressSelectorWidget(); bool backendUp() const; signals: void addressSelected(KContacts::Addressee); public slots: void saveState(); protected slots: void slotCreateNewContact(); void slotEditContact(); void restoreState(); void slotFilterTextChanged( const QString& filter); private slots: void slotAddresseeSelected(QModelIndex index); private: void setupUi(); QPushButton *mButEditContact; AddressProvider *_provider; AddressSortProxyModel *mProxyModel; QTreeView *_addressTreeView; KraftContactViewer *_contactViewer; #ifdef HAVE_AKONADI QScopedPointer _addressEditor; #endif }; #endif // AKONADIADDRESSSELECTOR_H kraft-0.97/src/addresstemplateprovider.cpp000066400000000000000000000046501410616450300210140ustar00rootroot00000000000000/*************************************************************************** addresstemplateprovider - template provider class for addresses ------------------- begin : Jun 2007 copyright : (C) 2007 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include #include #include #include "addresstemplateprovider.h" #include "texteditdialog.h" #include "doctext.h" #include "defaultprovider.h" AddressTemplateProvider::AddressTemplateProvider( QWidget *parent ) :TemplateProvider( parent ), mParent( parent ) { } void AddressTemplateProvider::slotNewTemplate() { // qDebug () << "SlotNewTemplate for addresses called!" << endl; KRun::runCommand( QString::fromLatin1( "kaddressbook --new-contact" ), QString::fromLatin1("kaddressbook" ), "address", mParent, "" ); } void AddressTemplateProvider::slotEditTemplate() { // qDebug () << "SlotEditTemplate called!" << endl; KRun::runCommand( QString::fromLatin1( "kaddressbook --uid %1" ).arg( mCurrentAddress.uid() ), QString::fromLatin1("kaddressbook" ), "address", mParent, "" ); } void AddressTemplateProvider::slotDeleteTemplate() { } void AddressTemplateProvider::slotSetCurrentAddress( const Addressee& adr ) { // qDebug () << "Current Address was set to " << adr.realName(); mCurrentAddress = adr; } void AddressTemplateProvider::slotTemplateToDocument() { if( mCurrentAddress.isEmpty() ) { // qDebug () << "Current address is empty, that should not happen"; return; } // qDebug () << "Moving address of " << mCurrentAddress.realName() << " to document" << endl; emit addressToDocument( mCurrentAddress ); } kraft-0.97/src/addresstemplateprovider.h000066400000000000000000000032271410616450300204600ustar00rootroot00000000000000/*************************************************************************** addresstemplateprovider - template provider class for address data ------------------- begin : Jun 2007 copyright : (C) 2007 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef ADDRESSTEMPLATEPROVIDER_H #define ADDRESSTEMPLATEPROVIDER_H #include #include "templateprovider.h" #include "doctext.h" #include using namespace KContacts; class AddressTemplateProvider : public TemplateProvider { Q_OBJECT public: AddressTemplateProvider( QWidget* ); signals: void newAddress( Addressee ); void addressToDocument( const Addressee& ); public slots: void slotNewTemplate(); void slotEditTemplate(); void slotDeleteTemplate(); void slotTemplateToDocument(); void slotSetCurrentAddress( const Addressee& ); private: Addressee mCurrentAddress; QWidget *mParent; }; #endif kraft-0.97/src/alldocsview.cpp000066400000000000000000000342031410616450300163710ustar00rootroot00000000000000/*************************************************************************** alldocsview.cpp ------------------- begin : Wed Mar 15 2006 copyright : (C) 2006 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "models/documentmodel.h" #include "models/documentproxymodels.h" #include "filterheader.h" #include "alldocsview.h" #include "documentman.h" #include "docguardedptr.h" #include "kraftdoc.h" #include "kraftdb.h" #include "defaultprovider.h" #include "docdigestdetailview.h" #include "kraftsettings.h" AllDocsView::AllDocsView( QWidget *parent ) : QWidget( parent ), mTableModel(0), mDateModel(0) { QVBoxLayout *box = new QVBoxLayout; setLayout( box ); box->setMargin( 0 ); box->setSpacing( 0 ); _searchLine = new QLineEdit(this); _searchLine->setClearButtonEnabled(true); _searchLine->setMinimumWidth(200); connect( _searchLine, SIGNAL(textChanged(QString)), this, SLOT(slotSearchTextChanged(QString)) ); QComboBox *filterCombo = new QComboBox; filterCombo->addItem(i18n("All documents")); filterCombo->addItem(i18n("Documents of last week")); filterCombo->addItem(i18n("Documents of last month")); // filterCombo->addItem("Document Type"); connect( filterCombo, SIGNAL(activated(int)), this, SLOT(slotAmountFilterChanged(int))); QHBoxLayout *hbox = new QHBoxLayout; QLabel *l1 = new QLabel(i18n("&Show: ")); l1->setBuddy(filterCombo); hbox->addWidget(l1); hbox->addWidget(filterCombo); hbox->addStretch( 2 ); QLabel *lab = new QLabel; lab->setText( i18n("&Search: ") ); lab->setBuddy( _searchLine); hbox->addWidget( lab ); hbox->addWidget( _searchLine); box->addLayout( hbox ); QFrame *f = new QFrame; f->setLineWidth( 2 ); f->setMidLineWidth( 3 ); f->setFrameStyle( QFrame::HLine | QFrame::Raised ); f->setFixedHeight( 10 ); box->addWidget( f ); box->addWidget(initializeTreeWidget()); } AllDocsView::~AllDocsView() { const QByteArray state = _tableView->horizontalHeader()->saveState().toBase64(); KraftSettings::self()->setDigestListColumnsAll( state ); const QByteArray state1 = _dateView->header()->saveState().toBase64(); KraftSettings::self()->setDigestListColumnsTime(state1); KraftSettings::self()->save(); } void AllDocsView::slotAmountFilterChanged(int entryNo) { int num = -1; if( entryNo == 1 ) { num = 7; } else if( entryNo == 2 ) { num = 31; } mTableModel->setMaxRows(num); mDateModel->setMaxRows(num); } void AllDocsView::slotSearchTextChanged(const QString& newStr ) { mTableModel->setFilterRegExp(newStr); mDateModel->setFilterRegExp(newStr); } QWidget* AllDocsView::initializeTreeWidget() { //Note: Currently building the views is done in slotBuildView() that is called from the portal // because otherwise we'd access the database before it is initialized _tableView = new QTableView; _dateView = new QTreeView; //Initialise mAllMenu = new QMenu( _tableView ); mAllMenu->setTitle( i18n("Document Actions")); //Add treewidgets to the toolbox: All docs view QVBoxLayout *vb1 = new QVBoxLayout; vb1->setMargin(0); _stack = new QStackedWidget(this); _stack->addWidget(_tableView); _stack->addWidget(_dateView); vb1->addWidget( _stack ); mAllViewDetails = new DocDigestDetailView; connect( mAllViewDetails, SIGNAL( showLastPrint( const dbID& ) ), this, SLOT( slotOpenLastPrinted() ) ); vb1->addWidget( mAllViewDetails ); QWidget *w = new QWidget; w->setLayout(vb1); // return w; } void AllDocsView::setView( ViewType type ) { // change the document listing widget QModelIndex current; if( type == FlatList) { _stack->setCurrentIndex(0); current = _tableView->currentIndex(); } else { _stack->setCurrentIndex(1); current = _dateView->currentIndex(); } // clear the details view mAllViewDetails->slotClearView(); if( current.isValid() > 0 ) { slotCurrentChanged(current, QModelIndex()); } else { // workaround, not cool. mCurrentlySelected = QModelIndex(); } } void AllDocsView::slotBuildView() { const QByteArray headerStateTable = QByteArray::fromBase64( KraftSettings::self()->digestListColumnsAll().toAscii() ); const QByteArray headerStateDate = QByteArray::fromBase64( KraftSettings::self()->digestListColumnsTime().toAscii() ); mDateModel = new DocumentFilterModel(-1, this); mDateModel->setEnableTreeview(true); mTableModel = new DocumentFilterModel(-1, this); mTableModel->setEnableTreeview(false); _tableView->setModel(mTableModel); _dateView->setModel(mDateModel); _tableView->sortByColumn(DocumentModel::Document_CreationDateRaw, Qt::DescendingOrder); _tableView->verticalHeader()->hide(); _tableView->setSortingEnabled(true); _tableView->horizontalHeader()->setMovable( true ); _tableView->horizontalHeader()->setSortIndicatorShown( true ); _tableView->horizontalHeader()->restoreState( headerStateTable ); _tableView->setSelectionBehavior( QAbstractItemView::SelectRows ); _tableView->setShowGrid( false ); _tableView->hideColumn( DocumentModel::Document_Id ); _tableView->hideColumn( DocumentModel::Document_ClientId ); _tableView->hideColumn( DocumentModel::Document_ClientAddress ); _tableView->showColumn( DocumentModel::Document_ClientName ); _tableView->showColumn( DocumentModel::Document_CreationDateRaw); _tableView->hideColumn( DocumentModel::Document_CreationDate); _tableView->hideColumn( DocumentModel::Document_Id_Raw); _tableView->hideColumn( DocumentModel::Treestruct_Type); _tableView->hideColumn( DocumentModel::Treestruct_Month); _tableView->hideColumn( DocumentModel::Treestruct_Year); _dateView->header()->restoreState( headerStateDate ); _dateView->hideColumn( DocumentModel::Document_ClientId ); _dateView->hideColumn( DocumentModel::Document_ClientAddress ); _dateView->showColumn( DocumentModel::Document_ClientName ); _dateView->showColumn( DocumentModel::Document_CreationDateRaw); _dateView->hideColumn( DocumentModel::Document_CreationDate); _dateView->hideColumn( DocumentModel::Document_Id_Raw); _dateView->hideColumn( DocumentModel::Treestruct_Type); _dateView->hideColumn( DocumentModel::Treestruct_Month); _dateView->hideColumn( DocumentModel::Treestruct_Year); _dateView->setSelectionBehavior( QAbstractItemView::SelectRows ); //Initialize common style options connect( _tableView->selectionModel(), SIGNAL( currentRowChanged(QModelIndex,QModelIndex) ), this, SLOT(slotCurrentChanged(QModelIndex,QModelIndex))); connect( _dateView->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SLOT(slotCurrentChanged(QModelIndex,QModelIndex))); connect( _tableView, SIGNAL( doubleClicked(QModelIndex) ), this, SLOT( slotDocOpenRequest(QModelIndex) ) ); connect( _dateView, SIGNAL( doubleClicked(QModelIndex) ), this, SLOT( slotDocOpenRequest(QModelIndex) ) ); _tableView->setAlternatingRowColors( true ); _dateView->setAlternatingRowColors(false); _tableView->setSelectionMode( QAbstractItemView::SingleSelection ); _tableView->setEditTriggers( QAbstractItemView::NoEditTriggers ); _dateView->setEditTriggers( QAbstractItemView::NoEditTriggers ); _dateView->setExpandsOnDoubleClick( false ); // expand the current year and month QModelIndex startIdx = mDateModel->index(0,DocBaseModel::Treestruct_Year, QModelIndex()); QModelIndexList yearIndexes = mDateModel->match(startIdx, Qt::DisplayRole, QVariant(QDate::currentDate().year()) ); if( yearIndexes.size() > 0 ) { QModelIndex yearIndx = mDateModel->index(yearIndexes.first().row(), 0, yearIndexes.first().parent()); _dateView->setExpanded(yearIndx,true); QModelIndex startIdxM = mDateModel->index(0, DocBaseModel::Treestruct_Month, yearIndx); QModelIndexList monthIndexes = mDateModel->match(startIdxM, Qt::DisplayRole, QVariant(QDate::currentDate().month()) ); if( monthIndexes.size() > 0 ) { QModelIndex mIdx = monthIndexes.first(); QModelIndex rIdx = mDateModel->index(mIdx.row(), 0, mIdx.parent()); _dateView->setExpanded(rIdx, true); } } connect(KraftDB::self(), &KraftDB::docDatabaseChanged, this, &AllDocsView::slotUpdateView); } void AllDocsView::slotUpdateView() { QApplication::setOverrideCursor( QCursor( Qt::WaitCursor ) ); static_cast(mDateModel->sourceModel())->resetData(); static_cast(mTableModel->sourceModel())->resetData(); mCurrentlySelected = QModelIndex(); QApplication::restoreOverrideCursor(); } void AllDocsView::contextMenuEvent( QContextMenuEvent * event ) { mAllMenu->popup( event->globalPos() ); } void AllDocsView::slotOpenLastPrinted( ) { // qDebug () << "slotOpenLastPrinted hit! "; emit openArchivedDocument( mLatestArchivedDigest ); } ArchDocDigest AllDocsView::currentLatestArchivedDoc() const { return mLatestArchivedDigest; } void AllDocsView::slotDocOpenRequest( QModelIndex index ) { Q_UNUSED(index) const QString id = currentDocumentId(); emit openDocument( id ); } int AllDocsView::currentDocumentRow() const { return mCurrentlySelected.row(); } QString AllDocsView::currentDocumentId( ) const { bool isDoc = true; DocBaseModel *model; if( _stack->currentIndex() == 0 ) { model = static_cast( mTableModel->sourceModel() ); } else { model = static_cast( mDateModel->sourceModel() ); isDoc = model->isDocument(mCurrentlySelected); } QString id; if( isDoc ) { QModelIndex idIndx = model->index(mCurrentlySelected.row(), DocumentModel::Document_Id_Raw, mCurrentlySelected.parent()); id = idIndx.data( Qt::DisplayRole ).toString(); } return id; } void AllDocsView::slotCurrentChanged( QModelIndex index, QModelIndex previous ) { Q_UNUSED(previous); if(index.isValid()) { DocBaseModel *model; bool isDoc = true; bool isDateModel= false; if( _stack->currentIndex() == 0 ) { mCurrentlySelected = mTableModel->mapToSource(index); model = static_cast( mTableModel->sourceModel() ); } else { isDateModel = true; mCurrentlySelected = mDateModel->mapToSource(index); model = static_cast( mDateModel->sourceModel() ); isDoc = model->isDocument(mCurrentlySelected); } /* get the corresponding document id */ if( isDoc ) { DocDigest digest; QModelIndex idIndx = model->index(mCurrentlySelected.row(), DocumentModel::Document_Ident, mCurrentlySelected.parent()); const QString id = idIndx.data( Qt::DisplayRole ).toString(); emit docSelected( id ); digest = model->digest( mCurrentlySelected ); mAllViewDetails->slotShowDocDetails( digest ); if( digest.archDocDigestList().size() > 0 ) { mLatestArchivedDigest = digest.archDocDigestList()[0]; } else { mLatestArchivedDigest = ArchDocDigest(); } } else { const QModelIndex idIndx = model->index(mCurrentlySelected.row(), DocumentModel::Treestruct_Type, mCurrentlySelected.parent()); int type = idIndx.data( Qt::DisplayRole ).toInt(); if( isDateModel) { const QModelIndex yIndx = model->index(mCurrentlySelected.row(), DocumentModel::Treestruct_Year, mCurrentlySelected.parent()); int year = yIndx.data( Qt::DisplayRole ).toInt(); if( type == AbstractIndx::MonthType ) { const QModelIndex mIndx = model->index(mCurrentlySelected.row(), DocumentModel::Treestruct_Month, mCurrentlySelected.parent()); int month = mIndx.data( Qt::DisplayRole ).toInt(); mAllViewDetails->slotShowMonthDetails(year, month); } else if( type == AbstractIndx::YearType ) { mAllViewDetails->slotShowYearDetails(year); } } } } else { // qDebug () << "Got invalid index, clearing digest view."; emit docSelected( QString() ); mAllViewDetails->slotClearView(); } //// qDebug () << "Supposed row: " << sourceIndex.row() << " Supposed ID: " << DocumentModel::self()->data(sourceIndex, Qt::DisplayRole); } QVector AllDocsView::contextMenus() { QVector menus; menus.append( mAllMenu); return menus; } kraft-0.97/src/alldocsview.h000066400000000000000000000055331410616450300160420ustar00rootroot00000000000000/*************************************************************************** alldocsview.h - digest view of all docs with filter ------------------- begin : Sat Mar 11 2017 copyright : (C) 2017 by Klaas Freitag email : kraft@freisturz.de ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef ALLDOCSVIEW_H #define ALLDOCSVIEW_H #include #include #include #include #include #include #include "models/datemodel.h" #include "docdigest.h" #include "docguardedptr.h" #include "models/documentproxymodels.h" class QPushButton; class dbID; class ArchDocDigest; class QContextMenuEvent; class QToolBox; class DocDigestDetailView; class AllDocsView : public QWidget { Q_OBJECT public: typedef enum {FlatList, TreeView} ViewType; AllDocsView( QWidget *parent = 0 ); ~AllDocsView(); int currentDocumentRow() const; QString currentDocumentId( ) const; ArchDocDigest currentLatestArchivedDoc() const; QVector contextMenus(); public slots: void slotBuildView(); void slotUpdateView(); void setView(ViewType type); protected: void contextMenuEvent( QContextMenuEvent* ); QWidget *initializeTreeWidget(); protected slots: void slotDocOpenRequest( QModelIndex ); void slotCurrentChanged( QModelIndex, QModelIndex ); void slotOpenLastPrinted(); void slotSearchTextChanged(const QString& newStr ); void slotAmountFilterChanged(int entryNo); signals: void createDocument(); void openDocument( const QString& ); void viewDocument( const QString& ); void copyDocument( const QString& ); void docSelected( const QString& ); void openArchivedDocument( const ArchDocDigest& ); private: QTableView *_tableView; QTreeView *_dateView; QStackedWidget *_stack; DocDigestDetailView *mAllViewDetails; QModelIndex mCurrentlySelected; DocumentFilterModel *mTableModel; DocumentFilterModel *mDateModel; QMenu *mAllMenu; QPushButton *mNewDocButton; ArchDocDigest mLatestArchivedDigest; QLineEdit *_searchLine; }; #endif kraft-0.97/src/archdoc.cpp000066400000000000000000000172111410616450300154600ustar00rootroot00000000000000/*************************************************************************** ArchDoc.cpp - an archived document. ------------------- begin : Mit Dez 31 19:24:05 CET 2003 copyright : (C) 2003 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include #include #include #include #include #include #include // application specific includes #include "archiveman.h" #include "archdoc.h" #include "documentman.h" #include "kraftdb.h" #include "defaultprovider.h" #include "kraftsettings.h" #include "format.h" namespace { QString multilineHtml( const QString& str ) { QString re {str.toHtmlEscaped()}; re.replace( '\n', "
            "); return re; } } // end namespace ArchDoc::ArchDoc() : mAttributes( QLatin1String("ArchDoc")) { } ArchDoc::ArchDoc( const dbID& id ) : mAttributes( QLatin1String("ArchDoc")) { /* load archive from database */ loadFromDb( id ); } ArchDoc::~ArchDoc() { } QString ArchDoc::docIdentifier() const { QString re = docType(); return i18n("%1 for %2 (Id %3)", re, ident() ); } QString ArchDoc::dateStr() const { return Format::toDateString(mDate, KraftSettings::self()->dateFormat()); } QString ArchDoc::preText() const { return mPreText; } QString ArchDoc::preTextHtml() const { return multilineHtml(mPreText); } QString ArchDoc::postText() const { return mPostText; } QString ArchDoc::postTextHtml() const { return multilineHtml(mPostText); } Geld ArchDoc::nettoSum() const { const Geld g = positions().sumPrice(); return g; } Geld ArchDoc::bruttoSum() const { Geld g = nettoSum(); const Geld ts = taxSum(); g += ts; return g; } Geld ArchDoc::taxSum() const { const Geld g = positions().taxSum(); return g; } Geld ArchDoc::fullTaxSum() const { return positions().fullTaxSum(); } Geld ArchDoc::reducedTaxSum() const { return positions().reducedTaxSum(); } QString ArchDoc::fullTaxPercentStr() const { return Format::localeDoubleToString(mTax, *DefaultProvider::self()->locale()); } QString ArchDoc::reducedTaxPercentStr() const { return Format::localeDoubleToString(mReducedTax, *DefaultProvider::self()->locale()); } QString ArchDoc::taxPercentStr() const { DocPositionBase::TaxType tt = mPositions.listTaxation(); if (tt == DocPositionBase::TaxType::TaxFull) { return fullTaxPercentStr(); } else if (tt == DocPositionBase::TaxType::TaxReduced) { return reducedTaxSumStr(); } return QString(); } double ArchDoc::tax() const { return mTax; } double ArchDoc::reducedTax() const { return mReducedTax; } void ArchDoc::loadFromDb( dbID id ) { mArchDocID = id; QSqlQuery q; q.prepare("SELECT archDocID, ident, docType, clientAddress, clientUid, " // pos 0..4 "salut, goodbye, printDate, date, pretext, posttext, country, language, " // pos 5..12 "projectLabel,tax, reducedTax, state from archdoc WHERE archDocID=:id" ); // pos 13..16 q.bindValue(":id", id.toInt()); q.exec(); // qDebug () << "Loading document id " << id.toString() << endl; if( q.next()) { QString docID; QString country; QString lang; docID = q.value( 0 ).toString(); mIdent = q.value( 1 ).toString(); mDocType = q.value( 2 ).toString(); mAddress = q.value( 3 ).toString(); mClientUid = q.value( 4 ).toString(); mSalut = q.value( 5 ).toString(); mGoodbye = q.value( 6 ).toString(); QVariant v = q.value( 7 ); mPrintDate = v.toDateTime(); mDate = q.value( 8 ).toDate(); mPreText = KraftDB::self()->mysqlEuroDecode( q.value( 9 ).toString() ); mPostText = KraftDB::self()->mysqlEuroDecode( q.value( 10 ).toString() ); country = q.value( 11 ).toString(); lang = q.value( 12 ).toString(); mProjectLabel = q.value( 13 ).toString(); mTax = q.value( 14 ).toDouble(); mReducedTax = q.value( 15 ).toDouble(); mState = q.value( 16 ).toInt(); loadItems( docID ); mAttributes.load(id); } else { // qDebug () << "ERR: Could not load archived doc with id " << id.toString() << endl; } } void ArchDoc::loadItems( const QString& archDocId ) { mPositions.clear(); if ( archDocId.isEmpty() /* || ! archDocId.isNum() */ ) { // qDebug () << "ArchDocId is not crappy: " << archDocId << endl; return; } QSqlQuery q; q.prepare("SELECT archPosID, archDocID, ordNumber, kind, postype, text, amount, " // pos 0..6 "unit, price, overallPrice, taxType FROM archdocpos WHERE archDocID=:id ORDER BY ordNumber"); // pos 7..10 q.bindValue(":id", archDocId); if( !q.exec() ) { qDebug() << "Error: " << q.lastError().nativeErrorCode(); } mPositions.setTaxes(mTax, mReducedTax); while( q.next() ) { ArchDocPosition pos; pos.mPosNo = q.value( 2 ).toString(); pos.mKind = q.value( 3 ).toString(); pos.mText = q.value( 5 ).toString(); pos.mAmount = q.value( 6 ).toDouble(); pos.mUnit = q.value( 7 ).toString(); pos.mUnitPrice = Geld( q.value( 8 ).toDouble() ); pos.mOverallPrice = q.value( 9 ).toDouble(); int tt = q.value( 10 ).toInt(); if ( tt == 1 ) pos.mTaxType = DocPositionBase::TaxNone; else if ( tt == 2 ) pos.mTaxType = DocPositionBase::TaxReduced; else if ( tt == 3 ) pos.mTaxType = DocPositionBase::TaxFull; mPositions.append( pos ); } } QList ArchDoc::itemslist() const { return mPositions; } QDateTime ArchDoc::sentOutDate() { QDateTime re; if ( mAttributes.hasAttribute( SentOutDateC ) ) { re = mAttributes["sentOutDate"].value().toDateTime(); } return re; } void ArchDoc::setSentOutDate( const QDateTime& dt ) { QString attName(SentOutDateC); if( dt.isValid() ) { Attribute att(attName); att.setPersistant(true); att.setValue(dt); mAttributes[attName] = att; } else { mAttributes.markDelete(attName); } mAttributes.save(mArchDocID); } ArchDocDigest ArchDoc::toDigest() const { return ArchDocDigest(mPrintDate, mState, mIdent, mArchDocID); } /* ###################################################################### */ ArchDocDigest::ArchDocDigest() { } ArchDocDigest::ArchDocDigest( QDateTime dt, int s, const QString& ident, dbID id ) : mPrintDate( dt ), mState( s ), mArchDocId( id ), mIdent( ident ) { } ArchDocDigest::~ArchDocDigest() { } QString ArchDocDigest::pdfArchiveFileName() const { const QString outputDir = ArchiveMan::self()->pdfBaseDir(); const QString filename = ArchiveMan::self()->archiveFileName(archDocIdent(), archDocId().toString(), "pdf" ); const QString file = QString( "%1/%2" ).arg( outputDir ).arg( filename ); return file; } /* ###################################################################### */ kraft-0.97/src/archdoc.h000066400000000000000000000133361410616450300151310ustar00rootroot00000000000000/*************************************************************************** archdoc.h - ------------------- begin : Sep 2006 copyright : (C) 2006 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef ARCHDOC_H #define ARCHDOC_H // include files for QT #include #include #include #include #include "archdocposition.h" #include "geld.h" #include "dbids.h" class QLocale; class AttributeMap; class ArchDocDigest { public: /** Constructor for the fileclass of the application */ ArchDocDigest(); ArchDocDigest( QDateTime, int, const QString&, dbID ); /** Destructor for the fileclass of the application */ ~ArchDocDigest(); QDateTime printDate() const { return mPrintDate; } int archDocState() const { return mState; } dbID archDocId() const { return mArchDocId; } QString archDocIdent() const { return mIdent; } QString pdfArchiveFileName() const; private: QDateTime mPrintDate; int mState; dbID mArchDocId; QString mIdent; }; class ArchDoc : public QObject { Q_OBJECT Q_PROPERTY(QString docType READ docType) Q_PROPERTY(QString address READ address) Q_PROPERTY(QString clientUid READ clientUid) Q_PROPERTY(QString ident READ ident) Q_PROPERTY(QString salut READ salut) Q_PROPERTY(QString goodbye READ goodbye) Q_PROPERTY(QString preText READ preText) Q_PROPERTY(QString preTextHtml READ preTextHtml) Q_PROPERTY(QString postText READ postText) Q_PROPERTY(QString postTextHtml READ postTextHtml) Q_PROPERTY(QString projectLabel READ projectLabel) Q_PROPERTY(QString docIDStr READ docIdStr) Q_PROPERTY(QString docIdentifier READ docIdentifier) Q_PROPERTY(QString dateStr READ dateStr) Q_PROPERTY(QString nettoSumStr READ nettoSumStr) Q_PROPERTY(QString bruttoSumStr READ bruttoSumStr) Q_PROPERTY(QString taxSumStr READ taxSumStr) Q_PROPERTY(QString fullTaxSumStr READ fullTaxSumStr) Q_PROPERTY(QString reducedTaxSumStr READ reducedTaxSumStr) Q_PROPERTY(QString reducedTaxPercentStr READ reducedTaxPercentStr) Q_PROPERTY(QString fullTaxPercentStr READ fullTaxPercentStr) Q_PROPERTY(QString taxPercentStr READ taxPercentStr) Q_PROPERTY(QString taxMarkerFull READ taxMarkerFull) Q_PROPERTY(QString taxMarkerReduced READ taxMarkerReduced) Q_PROPERTY(QList items READ itemslist) Q_PROPERTY(bool hasIndividualTaxation READ hasIndividualTaxation) public: const QString SentOutDateC {"SentOutDate"}; /** Constructor for the fileclass of the application */ ArchDoc(); ArchDoc( const dbID& ); /** Destructor for the fileclass of the application */ ~ArchDoc(); ArchDocPositionList positions() const { return mPositions; } QList itemslist() const; QDate date() const { return mDate; } QString dateStr() const; QString docType() const { return mDocType; } QString address() const { return mAddress; } QString clientUid() const { return mClientUid; } QString ident() const { return mIdent; } QString salut() const { return mSalut; } QString goodbye() const { return mGoodbye; } QString preText() const; QString preTextHtml() const; QString postText() const; QString postTextHtml() const; QString projectLabel() const { return mProjectLabel; } dbID docID() const { return mDocID; } QString docIdStr() const { return docID().toString(); } QString docIdentifier() const; Geld nettoSum() const; QString nettoSumStr() const { return nettoSum().toString(); } Geld bruttoSum() const; QString bruttoSumStr() const { return bruttoSum().toString(); } Geld taxSum() const; QString taxSumStr() const { return taxSum().toString(); } Geld fullTaxSum() const; QString fullTaxSumStr() const { return fullTaxSum().toString(); } Geld reducedTaxSum() const; QString reducedTaxSumStr() const { return reducedTaxSum().toString(); } QString fullTaxPercentStr() const; QString reducedTaxPercentStr() const; QString taxPercentStr() const; static QString taxMarkerNoTax() { return QStringLiteral("1"); } static QString taxMarkerReduced() { return QStringLiteral("2"); } static QString taxMarkerFull() { return QStringLiteral(""); } bool hasIndividualTaxation() const { return mPositions.hasIndividualTaxes(); } double tax() const; double reducedTax() const; ArchDocDigest toDigest() const; // when the document was sent to the customer. QDateTime sentOutDate(); void setSentOutDate( const QDateTime& dt ); void loadFromDb( dbID ); private: void loadItems( const QString& ); dbID mArchDocID; QString mAddress; QString mClientUid; QString mPreText; QString mPostText; QString mDocType; QString mSalut; QString mGoodbye; QString mIdent; QString mProjectLabel; double mTax; double mReducedTax; QDate mDate; QDateTime mPrintDate; ArchDocPositionList mPositions; dbID mDocID; int mState; AttributeMap mAttributes; }; #endif // ARCHDOC_H kraft-0.97/src/archdocposition.cpp000066400000000000000000000117521410616450300172510ustar00rootroot00000000000000/*************************************************************************** archdocposition.cpp - a position in an archived document ------------------- begin : Sep. 2006 copyright : (C) 2006 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include #include #include #include // application specific includes #include "einheit.h" #include "geld.h" #include "archdocposition.h" #include "archdoc.h" /** @author Klaas Freitag */ ArchDocPosition::ArchDocPosition() : mAmount( 0 ) { } Geld ArchDocPosition::nettoPrice() const { return mOverallPrice; } Geld ArchDocPosition::fullTax( double fullTax ) const { Geld tax; if ( mTaxType == DocPositionBase::TaxFull ) { tax = mOverallPrice * fullTax; } return tax / 100.0; } Geld ArchDocPosition::reducedTax( double reducedTax ) const { Geld tax; if ( mTaxType == DocPositionBase::TaxReduced ) { tax = mOverallPrice * reducedTax; } return tax / 100.0; } Geld ArchDocPosition::tax( double fullTax, double reducedTax ) const { Geld tax; if ( mTaxType == DocPositionBase::TaxFull ) { tax = mOverallPrice * fullTax; } else if ( mTaxType == DocPositionBase::TaxReduced ) { tax = mOverallPrice * reducedTax; } return tax / 100.0; } QString ArchDocPosition::taxMarkerHelper() const { QString re; if ( mTaxType == DocPositionBase::TaxReduced ) { re = ArchDoc::taxMarkerReduced(); } else if ( mTaxType == DocPositionBase::TaxNone) { re = ArchDoc::taxMarkerNoTax(); } else if ( mTaxType == DocPositionBase::TaxFull) { re = ArchDoc::taxMarkerFull(); } return re; } QString ArchDocPosition::htmlText(const QString& paraStyle) const { QString re; QString style( paraStyle ); if ( style.isEmpty() ) style = QStringLiteral("text"); // Keep empty parts allows multiple newlines here const QStringList li = mText.toHtmlEscaped().split( "\n", QString::KeepEmptyParts ); re = li.join("
            "); return re; } // ================================================================== ArchDocPositionList::ArchDocPositionList() : QList() { } Geld ArchDocPositionList::sumPrice() const { Geld g; const_iterator it; for ( it = begin(); it != end(); ++it ) { g += ( *it ).nettoPrice(); } return g; } Geld ArchDocPositionList::taxSum() const { Geld allTaxSum = fullTaxSum(); allTaxSum += reducedTaxSum(); return allTaxSum; } Geld ArchDocPositionList::fullTaxSum() const { Geld g; const_iterator it; for ( it = begin(); it != end(); ++it ) { if( (*it).taxType() == DocPositionBase::TaxFull) { g += (*it).nettoPrice(); } } const Geld ftSum(g.percent(_fullTax).toLong()); return ftSum; } Geld ArchDocPositionList::reducedTaxSum() const { Geld g; const_iterator it; for ( it = begin(); it != end(); ++it ) { if( (*it).taxType() == DocPositionBase::TaxReduced) { g += (*it).nettoPrice(); } } const Geld rtSum(g.percent(_reducedTax).toLong()); return rtSum; } DocPositionBase::TaxType ArchDocPositionList::listTaxation() const { int fullTax = 0; int noTax = 0; int redTax = 0; DocPositionBase::TaxType ret = DocPositionBase::TaxType::TaxNone; const_iterator it; for ( it = begin(); it != end(); ++it ) { if( (*it).taxType() == DocPositionBase::TaxFull) { fullTax++; } else if( (*it).taxType() == DocPositionBase::TaxReduced ) { redTax++; } else if( (*it).taxType() == DocPositionBase::TaxNone ) { noTax++; } } int cnt = count(); if (noTax == cnt) { ret = DocPositionBase::TaxType::TaxNone; } else if (redTax == cnt) { ret = DocPositionBase::TaxType::TaxReduced; } else if (fullTax == cnt) { ret = DocPositionBase::TaxType::TaxFull; } else ret = DocPositionBase::TaxType::TaxIndividual; return ret; } bool ArchDocPositionList::hasIndividualTaxes() const { qDebug() << "Has INDIVIDUAL taxes."; return (listTaxation() == DocPositionBase::TaxType::TaxIndividual); } void ArchDocPositionList::setTaxes(double fullTax, double reducedTax) { _fullTax = fullTax; _reducedTax = reducedTax; } kraft-0.97/src/archdocposition.h000066400000000000000000000112151410616450300167100ustar00rootroot00000000000000/*************************************************************************** archdocposition.h - a position in an archived document ------------------- begin : Sep 2006 copyright : (C) 2006 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef ARCHDOCPOSITION_H #define ARCHDOCPOSITION_H // include files for Qt #include #include #include // include files for KDE // application specific includes #include "einheit.h" #include "geld.h" #include "dbids.h" #include "docposition.h" #include "defaultprovider.h" class ArchDoc; /** @author Klaas Freitag */ class ArchDocPosition { friend class ArchDoc; public: ArchDocPosition(); ~ArchDocPosition(){}; QString posNumber() const { return mPosNo; } QString text() const { return mText; } QString htmlText(const QString ¶Style = QString()) const; QString unit() const { return mUnit; } Geld unitPrice() const { return mUnitPrice; } Geld nettoPrice() const; double amount() const { return mAmount; } DocPositionBase::TaxType taxType() const { return mTaxType; } Geld tax( double fullTax, double reducedTax ) const; Geld fullTax( double fullTax ) const; Geld reducedTax( double reducedTax ) const; QString kind() const { return mKind; } QString taxMarkerHelper() const; private: QString mText; QString mPosNo; QString mUnit; QString mKind; Geld mUnitPrice; Geld mOverallPrice; double mAmount; DocPositionBase::TaxType mTaxType; // No calculation yet }; class ArchDocPositionList : public QList { public: ArchDocPositionList(); Geld sumPrice() const; Geld taxSum() const; Geld fullTaxSum() const; Geld reducedTaxSum() const; DocPositionBase::TaxType listTaxation() const; bool hasIndividualTaxes() const; void setTaxes(double fullTax, double reducedTax); private: double _fullTax; double _reducedTax; }; Q_DECLARE_METATYPE(ArchDocPosition) Q_DECLARE_METATYPE(ArchDocPositionList) // Read-only introspection of Person object. GRANTLEE_BEGIN_LOOKUP(ArchDocPosition) if ( property == "itemNumber" ) return object.posNumber(); else if ( property == "text" ) return object.text(); else if ( property == "htmlText" ) return object.htmlText(); else if ( property == "kind" ) return object.kind(); else if ( property == "unit" ) return object.unit(); else if ( property == "unitPrice" ) { return object.unitPrice().toString(); } else if ( property == "nettoPrice" ) { return object.nettoPrice().toString(); } else if ( property == "amount" ) { QLocale *loc = DefaultProvider::self()->locale(); return loc->toString(object.amount()); } else if ( property == "taxType" ) { if (object.taxType() == DocPositionBase::TaxType::TaxFull) { return QStringLiteral("fullTax"); } else if (object.taxType() == DocPositionBase::TaxType::TaxReduced) { return QStringLiteral("reducedTax"); } else if (object.taxType() == DocPositionBase::TaxType::TaxNone) { return QStringLiteral("noTax"); } return QStringLiteral("Invalid"); } else if ( property == "itemType" ) { return object.kind(); } else if ( property == "taxMarker") { return object.taxMarkerHelper(); } else { return QStringLiteral("undefined"); } GRANTLEE_END_LOOKUP GRANTLEE_BEGIN_LOOKUP(ArchDocPositionList) if (property == "sumPrice") return object.sumPrice().toString(); else if (property == "taxSum") return object.taxSum().toString(); else if (property == "fullTaxSum") return object.fullTaxSum().toString(); else if (property == "reducedTaxSum") return object.reducedTaxSum().toString(); else if (property == "reducedTaxSum") return object.reducedTaxSum().toString(); else if (property == "hasIndividualTaxes") return object.hasIndividualTaxes(); else return QStringLiteral("Undefined"); GRANTLEE_END_LOOKUP #endif kraft-0.97/src/archiveman.cpp000066400000000000000000000312241410616450300161720ustar00rootroot00000000000000/*************************************************************************** archiveman.cpp - Archive Manager ------------------- begin : July 2006 copyright : (C) 2006 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include #include #include #include #include #include #include #include "archiveman.h" #include "kraftdoc.h" #include "kraftdb.h" #include "unitmanager.h" #include "dbids.h" #include "documentman.h" #include "defaultprovider.h" #include "format.h" Q_GLOBAL_STATIC(ArchiveMan, mSelf) ArchiveMan *ArchiveMan::self() { return mSelf; } ArchiveMan::ArchiveMan() { } ArchiveMan::~ArchiveMan() { } dbID ArchiveMan::archiveDocument( KraftDoc *doc ) { if( ! doc ) return dbID(); dbID archId = archiveDocumentDb( doc ); if ( DefaultProvider::self()->writeXmlArchive() ) { archiveDocumentXml( doc, archId.toString()); } return archId; } QString ArchiveMan::documentID( dbID archID ) const { QString re; QSqlQuery q; q.prepare("SELECT ident FROM archdoc WHERE archDocID=:id"); q.bindValue(":id", archID.toInt()); q.exec(); if ( q.next() ) { re = q.value( 0 ).toString(); } return re; } QDomElement ArchiveMan::xmlTextElement( QDomDocument doc, const QString& name, const QString& value ) { QDomElement elem = doc.createElement( name ); QDomText t = doc.createTextNode( value ); elem.appendChild( t ); return elem; } QDomElement ArchiveMan::positionsDomElement( DocPositionList *positions, QDomDocument& doc ) { QDomElement topElem = doc.createElement( "positions" ); QDomElement posElem; int num = 1; DocPositionListIterator it(*positions); while( it.hasNext() ) { DocPosition *dpb = static_cast( it.next() ); if( dpb->type() == DocPositionBase::Position ) { DocPosition *dp = static_cast(dpb); posElem = doc.createElement( "position" ); posElem.setAttribute( "number", num++ ); topElem.appendChild( posElem ); posElem.appendChild( xmlTextElement( doc, "text", dp->text() ) ); double am = dp->amount(); QString h = QString::number(am, 'f', 2 ); posElem.appendChild( xmlTextElement( doc, "amount", h )); Einheit e = dp->unit(); posElem.appendChild( xmlTextElement( doc, "unit", e.einheit( am ) ) ); Geld g = dp->unitPrice(); posElem.appendChild( xmlTextElement( doc, "unitprice", QString::number(g.toDouble(), 'f', 2 ))); Geld sum(g * am); posElem.appendChild( xmlTextElement( doc, "sumprice", QString::number(sum.toDouble(), 'f', 2 ) ) ); } } return topElem; } QDomDocument ArchiveMan::archiveDocumentXml( KraftDoc *doc, const QString& archId ) { QDomDocument xmldoc( "kraftdocument" ); QDomElement root = xmldoc.createElement( "kraftdocument" ); // Fixme: xmldoc.appendChild( root ); QDomElement cust = xmldoc.createElement( "client" ); root.appendChild( cust ); cust.appendChild( xmlTextElement( xmldoc, "address", doc->address() ) ); cust.appendChild( xmlTextElement( xmldoc, "clientId", doc->addressUid() ) ); QDomElement docElem = xmldoc.createElement( "docframe" ); root.appendChild( docElem ); docElem.appendChild( xmlTextElement( xmldoc, "docType", doc->docType() ) ); docElem.appendChild( xmlTextElement( xmldoc, "docDesc", doc->whiteboard() ) ); docElem.appendChild( xmlTextElement( xmldoc, "ident", doc->ident() ) ); docElem.appendChild( xmlTextElement( xmldoc, "predecessor", doc->predecessor() ) ); docElem.appendChild( xmlTextElement( xmldoc, "preText", doc->preText() ) ); docElem.appendChild( xmlTextElement( xmldoc, "postText", doc->postText() ) ); docElem.appendChild( xmlTextElement( xmldoc, "projectLabel", doc->projectLabel() ) ); docElem.appendChild( xmlTextElement( xmldoc, "salut", doc->salut() ) ); docElem.appendChild( xmlTextElement( xmldoc, "goodbye", doc->goodbye() ) ); docElem.appendChild( xmlTextElement( xmldoc, "date", Format::toDateString(doc->date(), Format::DateFormatIso))); DocPositionList dpList = doc->positions(); root.appendChild( positionsDomElement(&dpList, xmldoc) ); QString xml = xmldoc.toString(); // qDebug() << "Resulting XML: " << xml << endl; const QString outputDir = xmlBaseDir(); const QString filename = archiveFileName( doc->ident(), archId, "xml" ); const QString xmlFile = QString( "%1/%2" ).arg( outputDir ).arg( filename ); // qDebug () << "Storing XML to " << xmlFile << endl; QFile file( xmlFile ); if ( file.open( QIODevice::WriteOnly ) ) { QTextStream stream( &file ); stream << xml << "\n"; file.close(); } else { // qDebug () << "Saving failed" << endl; } return xmldoc ; } dbID ArchiveMan::archiveDocumentDb( KraftDoc *doc ) { /* mysql> describe archdoc; +---------------+--------------+------+-----+-------------------+----------------+ | Field | Type | Null | Key | Default | Extra | +---------------+--------------+------+-----+-------------------+----------------+ | archDocID | int(11) | NO | PRI | NULL | auto_increment | | ident | varchar(32) | YES | MUL | NULL | | | docType | varchar(255) | YES | | NULL | | | clientAddress | text | YES | | NULL | | | clientUid | varchar(32) | YES | | NULL | | | salut | varchar(255) | YES | | NULL | | | goodbye | varchar(128) | YES | | NULL | | | printDate | timestamp | NO | | CURRENT_TIMESTAMP | | | date | date | YES | | NULL | | | pretext | text | YES | | NULL | | | posttext | text | YES | | NULL | | | country | varchar(32) | YES | | NULL | | | language | varchar(32) | YES | | NULL | | | projectLabel | varchar(255) | YES | | NULL | | | state | int(11) | YES | | NULL | | +---------------+--------------+------+-----+-------------------+----------------+ */ if( ! doc ) return dbID(); QSqlTableModel model; model.setTable("archdoc"); QSqlRecord record = model.record(); if( doc->isNew() ) { // qDebug () << "Strange: Document in archiving is new!" << endl; } record.setValue( "ident", doc->ident() ); record.setValue( "docType", doc->docType() ); record.setValue( "docDescription", KraftDB::self()->mysqlEuroEncode( doc->whiteboard() ) ); record.setValue( "clientAddress", doc->address() ); record.setValue( "clientUid", doc->addressUid() ); record.setValue( "salut", doc->salut() ); record.setValue( "goodbye", doc->goodbye() ); record.setValue( "printDate", KraftDB::self()->currentTimeStamp() ); record.setValue( "date", doc->date() ); record.setValue( "pretext", KraftDB::self()->mysqlEuroEncode(doc->preText() ) ); record.setValue( "posttext", KraftDB::self()->mysqlEuroEncode(doc->postText() ) ); record.setValue( "projectLabel", KraftDB::self()->mysqlEuroEncode(doc->projectLabel() ) ); record.setValue( "predecessor", doc->predecessor() ); QLocale *loc = DefaultProvider::self()->locale(); record.setValue( "country", loc->bcp47Name() ); record.setValue( "language", "" ); record.setValue( "tax", DocumentMan::self()->tax( doc->date() ) ); record.setValue( "reducedTax", DocumentMan::self()->reducedTax( doc->date() ) ); if(!model.insertRecord(-1, record)) { // qDebug () << model.lastError(); } dbID id = KraftDB::self()->getLastInsertID(); archivePos( id.toInt(), doc ); return id; } int ArchiveMan::archivePos( int archDocId, KraftDoc *doc ) { /* mysql> describe archdocpos; +-----------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-----------+--------------+------+-----+---------+----------------+ | archPosID | int(11) | NO | PRI | NULL | auto_increment | | archDocID | int(11) | NO | MUL | | | | ordNumber | int(11) | NO | | | | | text | text | YES | | NULL | | | amount | decimal(6,2) | YES | | NULL | | | unit | varchar(64) | YES | | NULL | | | price | decimal(6,2) | YES | | NULL | | | vat | decimal(3,1) | YES | | 0.0 | | +-----------+--------------+------+-----+---------+----------------+ */ if( ! doc ) return -1; QSqlTableModel model; model.setTable("archdocpos"); QSqlRecord record = model.record(); int cnt = 0; DocPositionList posList = doc->positions(); DocPositionListIterator it( posList ); // qDebug () << "Archiving pos for " << archDocId << endl; while ( it.hasNext() ) { DocPosition *dp = static_cast( it.next() ); record.setValue( "archDocID", archDocId ); record.setValue( "ordNumber", 1+cnt /* dp->position() */ ); record.setValue( "kind", dp->attribute( DocPosition::Kind ) ); record.setValue( "text", dp->text() ); // expandItemText( dp ) ); record.setValue( "amount", dp->amount() ); record.setValue( "unit", dp->unit().einheit( dp->amount() ) ); record.setValue( "price", dp->unitPrice().toDouble() ); record.setValue( "overallPrice", dp->overallPrice().toDouble() ); record.setValue( "taxType", dp->taxTypeNumeric() ); if(!model.insertRecord(-1, record)) { // qDebug () << model.lastError(); } dbID id = KraftDB::self()->getLastInsertID(); // qDebug() << "Inserted for id " << id.toString() << endl; cnt++; // save the attributes of the positions in the attributes // table but with a new host type which reflects the arch state AttributeMap attribs = dp->attributes(); attribs.setHost( "ArchPosition" ); attribs.save( id ); } return cnt; } void ArchiveMan::ensureDirIsExisting( const QString& dir ) const { if( ! QFile::exists(dir)) { qDebug() << "pdfBaseDir does not exist! Trying to create" << dir; QDir d; if( d.mkpath(dir) ) { qDebug() << "Successfully created" << dir; } else { qDebug() << "Failed to create" << dir; } } } QString ArchiveMan::xmlBaseDir() const { QString outputDir = DefaultProvider::self()->xmlArchivePath(); if ( outputDir.isEmpty() ) { // stay bug compatible: Before issue #80, this was the pdfOutputDir outputDir = DefaultProvider::self()->pdfOutputDir(); } if (outputDir.isEmpty()) { outputDir = QStandardPaths::writableLocation( QStandardPaths::AppDataLocation ); } if ( ! outputDir.endsWith('/') ) outputDir += QChar('/'); ensureDirIsExisting(outputDir); return outputDir; } QString ArchiveMan::pdfBaseDir() const { QString outputDir = DefaultProvider::self()->pdfOutputDir(); if ( outputDir.isEmpty() ) { outputDir = QStandardPaths::writableLocation( QStandardPaths::AppDataLocation ); } if ( ! outputDir.endsWith('/') ) outputDir += QChar('/'); ensureDirIsExisting(outputDir); return outputDir; } QString ArchiveMan::archiveFileName( const QString& docId, const QString& archId, const QString& ext ) const { QString re = QString( "%1_%2.%3" ).arg( docId ).arg( archId ).arg( ext ); re.replace(QLatin1Char('/'), QLatin1Char('_')); return re; } kraft-0.97/src/archiveman.h000066400000000000000000000042011410616450300156320ustar00rootroot00000000000000/*************************************************************************** archiveman.h - Archive Manager ------------------- begin : July 2006 copyright : (C) 2006 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef ARCHIVEMAN_H #define ARCHIVEMAN_H #include #include "dbids.h" class KraftDoc; class dbID; class QDomDocument; class DocPositionList; class ArchiveMan { friend class KraftDB; public: virtual ~ArchiveMan(); static ArchiveMan *self(); /** * query the document identifier id for a given database archive id */ QString documentID( dbID archID ) const; QString xmlBaseDir() const; QString pdfBaseDir() const; QString archiveFileName( const QString&, const QString&, const QString& ) const; ArchiveMan(); protected: /* do not use the archive function directly, but always via KraftDB, to let the DB * class update the counters of documents. */ dbID archiveDocument( KraftDoc* ); virtual QDomDocument archiveDocumentXml( KraftDoc*, const QString& ); virtual dbID archiveDocumentDb( KraftDoc* ); private: QDomElement xmlTextElement( QDomDocument, const QString&, const QString& ); QDomElement positionsDomElement( DocPositionList *positions, QDomDocument& doc ); int archivePos( int, KraftDoc* ); void ensureDirIsExisting( const QString& dir ) const; }; #endif kraft-0.97/src/attribute.cpp000066400000000000000000000345471410616450300160730ustar00rootroot00000000000000/*************************************************************************** attribute.cpp - generic attribute object ------------------- begin : Aug. 2007 copyright : (C) 2007 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include #include #include #include #include #include "attribute.h" #include "kraftdb.h" #include "dbids.h" Attribute::Attribute() :mPersist( true ), mListValue( false ), mDelete( false ) { } Attribute::Attribute( const QString& name ) :mName( name ), mPersist( true ), mListValue( false ), mDelete( false ) { } void Attribute::setRawValue( const QVariant& var ) { mValue = var; } void Attribute::setValue( const QVariant& var ) { if ( useRelationTable() ) { QSqlQuery q; QString query = "SELECT " + mIdCol +" FROM " + mTable + " WHERE " + mStringCol + "=:string"; q.prepare( query ); // qDebug() << "Column: " << mIdCol << " | table " << mTable << " | string: " << mStringCol << ": " << query; if ( listValue() ) { QStringList idList; QStringList list = var.toStringList(); for ( QStringList::Iterator valIt = list.begin(); valIt != list.end(); ++valIt ) { QString curValue = *valIt; // qDebug() << "Searching for " << curValue << " in relation table"; q.bindValue( ":string", curValue ); q.exec(); if ( q.next() ) { idList << q.value( 0 ).toString(); } } mValue = QVariant( idList ); } else { q.bindValue( ":string", var.toString() ); q.exec(); // qDebug() << "ERROR" << q.lastError().text(); if ( q.next() ) { mValue = q.value( 0 ); } } } else { mValue = var; } } bool Attribute::useRelationTable() { return !( mTable.isEmpty() || mIdCol.isEmpty() || mStringCol.isEmpty() ); } QVariant Attribute::value() { if ( useRelationTable() ) { // get the value from the relation table QSqlQuery q; QString query = "SELECT " + mStringCol +" FROM " + mTable + " WHERE " + mIdCol + "=:id"; q.prepare( query ); if ( listValue() ) { QStringList idList = mValue.toStringList(); QStringList::Iterator it = idList.begin(); QStringList list; while( it != idList.end() ) { q.bindValue( ":id", *it ); q.exec(); while ( q.next() ) { QString str = q.value( 0 ).toString(); list.append( str ); } ++it; } return QVariant( list ); } else { q.bindValue( ":id", mValue.toString() ); q.exec(); if ( q.next() ) { return QVariant( q.value( 0 ) ); } } } return mValue; } QString Attribute::name() const { return mName; } bool Attribute::persistant() { return mPersist; } void Attribute::setPersistant( bool p ) { mPersist = p; } void Attribute::setListValue( bool p ) { mListValue = p; } bool Attribute::listValue() { return mListValue; } void Attribute::setValueRelation( const QString& table, const QString& idColumn, const QString& stringColumn ) { mTable = table; mIdCol = idColumn; mStringCol = stringColumn; } QString Attribute::toString() { QString re; re = "+ Attribute name: " + mName + '\n'; if ( mListValue ) { re += "+ Attribute Value (List): " + mValue.toStringList().join( ", " )+ '\n'; } else { re += "+ Attribute Value (String): " + mValue.toString() + '\n'; } re += "+ Relation Table: " + mTable + '\n'; re += "+ Relation ID-Column: " + mIdCol + '\n'; re += "+ Relation StringCol: " + mStringCol + '\n'; re += "+ List: " + ( mListValue ? QString( "yes" ) : QString( "no" ) ) + '\n'; return re; } /* * Attribute Map ============================================================ */ AttributeMap::AttributeMap() :QMap() { } AttributeMap::AttributeMap( const QString& host) :QMap(), mHost( host ) { } void AttributeMap::setHost( const QString& host ) { mHost = host; } bool AttributeMap::hasAttribute( const QString& name ) { Iterator it = find( name ); if ( it != end() ) { // it is there, check the delete flag. if ( ! ( *it ).mDelete ) return true; } return false; } /* * this method saves the attribute together with the host string that * defines the type of object that this attribute is associated to (like * position or document) and the hosts database id. */ void AttributeMap::save( dbID id ) { checkHost(); QSqlQuery attribQuery; attribQuery.prepare( "SELECT id, valueIsList FROM attributes WHERE hostObject=:host AND hostId=:hostId AND name=:name" ); attribQuery.bindValue( ":host", mHost ); attribQuery.bindValue( ":hostId", id.toString() ); Iterator it; for ( it = begin(); it != end(); ++it ) { Attribute att = it.value(); // qDebug () << ">> oo- saving attribute with name " << it.key() << " for " << id.toString() << " att-name: " << att.name(); attribQuery.bindValue( ":name", att.name() ); attribQuery.exec(); QString attribId; if ( attribQuery.next() ) { // the attrib exists. Check the values attribId = attribQuery.value(0).toString(); // the id if ( att.value().isNull() || att.mDelete ) { // the value is empty. the existing entry needs to be dropped dbDeleteAttribute( attribId ); return; } } else { // the attrib does not yet exist. Create if att value is not null. if ( att.value().isNull() ) { // qDebug () << "oo- skip writing of attribute, value is empty"; } else { // qDebug () << "oo- writing of attribute name " << att.name(); QSqlQuery insQuery; insQuery.prepare( "INSERT INTO attributes (hostObject, hostId, name, valueIsList, relationTable, " "relationIDColumn, relationStringColumn) " "VALUES (:host, :hostId, :name, :valueIsList, :relTable, :relIDCol, :relStringCol )" ); insQuery.bindValue( ":host", mHost ); insQuery.bindValue( ":hostId", id.toString() ); insQuery.bindValue( ":name", att.name() ); insQuery.bindValue( ":valueIsList", att.listValue() ); // Write the relation table info. These remain empty for non related attributes. insQuery.bindValue( ":relTable", att.mTable ); insQuery.bindValue( ":relIDCol", att.mIdCol ); insQuery.bindValue( ":relStringCol", att.mStringCol ); insQuery.exec(); dbID attId = KraftDB::self()->getLastInsertID(); attribId = attId.toString(); } } // store the id to be able to drop not longer existent values // qDebug () << "adding attribute id " << attribId << " for attribute " << att.name(); // now there is a valid entry in the attribute table. Check the values. QSqlQuery valueQuery( "SELECT id, value FROM attributeValues WHERE attributeId=" + attribId ); typedef QMap ValueMap; ValueMap valueMap; while ( valueQuery.next() ) { QString idStr = valueQuery.value( 0 ).toString(); // id QString valStr = valueQuery.value( 1 ).toString(); // value valueMap[valStr] = idStr; } // create a stringlist with the current values of the attribute if ( att.listValue() ) { QStringList newValues; newValues = att.mValue.toStringList(); // qDebug () << "new values are: " << newValues.join( ", " ); if ( newValues.empty() ) { // delete the entire attribute. dbDeleteValue( attribId ); // deletes all values dbDeleteAttribute( attribId ); valueMap.clear(); } else { // we really have new values QSqlQuery insValue; insValue.prepare( "INSERT INTO attributeValues (attributeId, value) VALUES (:attribId, :val)" ); insValue.bindValue( ":attribId", attribId ); // loop over all existing new values of the attribute. for ( QStringList::Iterator valIt = newValues.begin(); valIt != newValues.end(); ++valIt ) { QString curValue = *valIt; if ( valueMap.contains( curValue ) ) { // the valueMap is already saved. remove it from the valueMap string // qDebug () << "Value " << curValue << " is already present with id " << valueMap[curValue]; valueMap.remove( curValue ); } else { // the value is not yet there, insert it. insValue.bindValue( ":val", curValue ); insValue.exec(); } } } } else { // only a single entry for the attribte, update if needed. QString newValue = att.mValue.toString(); // access the attribute object directly to get the numeric // qDebug () << "NEW value String: " << newValue; // value in case the attribute is bound to a relation table if ( newValue.isEmpty() ) { // delete the entire attribute dbDeleteValue( attribId ); // deletes all values dbDeleteAttribute( attribId ); valueMap.clear(); } else { if ( valueMap.empty() ) { // there is no entry yet that could be updated. QSqlQuery insertQuery; insertQuery.prepare( "INSERT INTO attributeValues (attributeId, value ) VALUES (:id, :val)" ); insertQuery.bindValue( ":id", attribId ); insertQuery.bindValue( ":val", newValue ); insertQuery.exec(); // qDebug () << "insert new attrib value for non list: " << newValue; } else { QString oldValue = valueMap.begin().key(); QString id = valueMap.begin().value(); if ( newValue != oldValue ) { // qDebug () << "Updating " << id << " from " << oldValue << " to " << newValue; QSqlQuery updateQuery; updateQuery.prepare( "UPDATE attributeValues SET value=:val WHERE id=:id" ); updateQuery.bindValue( ":val", newValue ); updateQuery.bindValue( ":id", id ); // qDebug () << "do the update!"; updateQuery.exec(); } valueMap.remove( oldValue ); } } } // remove all still existing entries in the valueMap because they point to values which are // in the db but were deleted from the attribute if ( ! valueMap.isEmpty() ) { ValueMap::Iterator mapIt; for ( mapIt = valueMap.begin(); mapIt != valueMap.end(); ++mapIt ) { QString valId = mapIt.value(); dbDeleteValue( attribId, valId ); } } } } void AttributeMap::markDelete( const QString& name ) { if ( name.isEmpty() || ! contains( name ) )return; Iterator it = find( name ); if ( it != end() ) { ( *it ).mDelete = true; // qDebug () << "Marking attrib " << name << " to delete!"; } } /* remove all Attributes from the database for the given host id * this method clears the entire map and should only be called if * the whole host is to delete anyway. */ void AttributeMap::dbDeleteAll( dbID id ) { // qDebug () << "This is the id for to delete: " << id.toString(); if ( !id.isOk() ) return; QSqlQuery listQuery; listQuery.prepare( "SELECT id FROM attributes WHERE hostObject=:hostObject AND hostId=:hostId" ); listQuery.bindValue( ":hostObject", mHost ); listQuery.bindValue( ":hostId", id.toString() ); listQuery.exec(); // qDebug () << "4-XXXXXXXXXXX " << listQuery.lastError().text(); while ( listQuery.next() ) { dbDeleteAttribute( listQuery.value( 0 ).toString() ); } clear(); } void AttributeMap::dbDeleteAttribute( const QString& attribId ) { if ( attribId.isEmpty() ) return; QSqlQuery delQuery; // qDebug () << "Deleting attribute id " << attribId; delQuery.prepare( "DELETE FROM attributes WHERE id=:id" ); delQuery.bindValue( ":id", attribId ); delQuery.exec(); // qDebug () << "5-XXXXXXXXXXX " << delQuery.lastError().text(); dbDeleteValue( attribId ); // delete all values } void AttributeMap::dbDeleteValue( const QString& attribId, const QString& id ) { QSqlQuery delQuery; if ( id.isEmpty() && ! attribId.isEmpty() ) { delQuery.prepare( "DELETE FROM attributeValues WHERE attributeId=" + attribId ); } else if ( !id.isEmpty() ) { delQuery.prepare( "DELETE FROM attributeValues WHERE id="+id ); } delQuery.exec(); // qDebug () << "6-XXXXXXXXXXX " << delQuery.lastError().text(); } void AttributeMap::load( dbID id ) { QSqlQuery q1; q1.prepare("SELECT id, name, valueIsList, relationTable, relationIDColumn, relationStringColumn FROM attributes WHERE hostObject=:hostObject AND hostId=:hostId"); q1.bindValue(":hostObject", mHost); q1.bindValue(":hostId", id.toInt()); q1.exec(); checkHost(); while ( q1.next() ) { QString h = q1.value( 1 ).toString(); bool isList = q1.value( 2 ).toBool(); QString relTable = q1.value( 3 ).toString(); QString relIDCol = q1.value( 4 ).toString(); QString relStrCol = q1.value( 5 ).toString(); Attribute attr( h ); attr.setListValue( isList ); attr.setValueRelation( relTable, relIDCol, relStrCol ); QSqlQuery q2; q2.prepare("SELECT value FROM attributeValues WHERE attributeId=:id"); q2.bindValue(":id", q1.value(0).toInt()); q2.exec(); QStringList values; QString str; while ( q2.next() ) { if ( isList ) { values << q2.value( 0 ).toString(); } else { str = q2.value( 0 ).toString(); // qDebug() << " attribute string " << h <<": " << str; } } // qDebug() << " attribute list " << h <<": " << values; if ( isList ) { attr.setRawValue( QVariant( values ) ); } else { attr.setRawValue( QVariant( str ) ); } attr.setPersistant( true ); insert( h, attr ); } } void AttributeMap::checkHost() { if ( mHost.isEmpty() ) { // qDebug () << "Host for attributes unset, assuming unknown"; mHost = "unknown"; } } kraft-0.97/src/attribute.h000066400000000000000000000050631410616450300155270ustar00rootroot00000000000000/*************************************************************************** attribute.h - generic attribute object ------------------- begin : Aug. 2007 copyright : (C) 2007 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef ATTRIBUTE_H #define ATTRIBUTE_H // include files for Qt #include // include files for KDE // application specific includes #include "kraftcat_export.h" /** @author Klaas Freitag */ class QString; class dbID; class KRAFTCAT_EXPORT Attribute { friend class AttributeMap; public: Attribute(); Attribute( const QString& name ); void setValue( const QVariant& var ); QVariant value(); QString name() const; bool persistant(); bool listValue(); void setListValue( bool ); void setPersistant( bool ); bool useRelationTable(); void setValueRelation( const QString&, const QString&, const QString& ); QString toString(); private: void setRawValue( const QVariant& var ); // sets the value without checking for relations QString mName; QVariant mValue; bool mPersist; bool mListValue; bool mDelete; // Delete the attribute on save. Written and read by the attributemap QString mTable; QString mIdCol; QString mStringCol; }; /* * Attribute Map */ class KRAFTCAT_EXPORT AttributeMap: public QMap { public: AttributeMap(); AttributeMap( const QString& ); bool hasAttribute( const QString& ); void setHost( const QString& ); void load( dbID ); void save( dbID ); void markDelete( const QString& ); void dbDeleteAll( dbID ); protected: void dbDeleteAttribute( const QString& ); void dbDeleteValue( const QString&, const QString& = QString() ); private: void checkHost(); QString mHost; }; #endif kraft-0.97/src/calcdialogbase.cpp000066400000000000000000000035201410616450300167700ustar00rootroot00000000000000/*************************************************************************** CalcDialogBase - base class for calculation detail dialogs ------------------- begin : 2017-01-31 copyright : (C) 2017 by Klaas Freitag email : kraft@freisturz.de ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ // include files for Qt #include #include #include #include #include "calcdialogbase.h" CalcDialogBase::CalcDialogBase(QWidget *parent) : QDialog( parent ) { _centralWidget = new QWidget(this); setModal( true ); QVBoxLayout *mainLayout = new QVBoxLayout; setLayout(mainLayout); mainLayout->addWidget(_centralWidget); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); mainLayout->addWidget(buttonBox); } /* END */ kraft-0.97/src/calcdialogbase.h000066400000000000000000000024161410616450300164400ustar00rootroot00000000000000/*************************************************************************** CalcDialogBase - base class for calculation detail dialogs ------------------- begin : 2017-01-31 copyright : (C) 2017 by Klaas Freitag email : kraft@freisturz.de ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef _CALCDIALOGBASE_H #define _CALCDIALOGBASE_H #include #include /** * */ class CalcDialogBase: public QDialog { Q_OBJECT public: CalcDialogBase(QWidget *parent); protected: QWidget *_centralWidget; }; #endif /* END */ kraft-0.97/src/calcpart.cpp000066400000000000000000000102351410616450300156450ustar00rootroot00000000000000/*************************************************************************** calcpart.cpp - ------------------- begin : Mit Dez 31 2003 copyright : (C) 2003 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include #include #include "calcpart.h" #include "fixcalcpart.h" #include "materialcalcpart.h" #include "timecalcpart.h" CalcPart::CalcPart( ): m_prozentPlus(0), m_dbId(-1), m_templId(-1), m_dirty(false), m_toDelete(false) { } CalcPart::CalcPart( int prozent ): m_prozentPlus( prozent ), m_dbId(-1), m_templId(-1), m_dirty(false), m_toDelete(false) { } CalcPart::CalcPart(const QString& name, int prozent ) : m_prozentPlus( prozent ), m_name( name ), m_dbId(-1), m_templId(-1), m_dirty(false), m_toDelete(false) { } CalcPart::~CalcPart() { } /** Read property of int m_prozentPlus. */ const double& CalcPart::getProzentPlus() { return m_prozentPlus; } /** Write property of int m_prozentPlus. */ void CalcPart::setProzentPlus( const double& _newVal) { if( _newVal != m_prozentPlus ) { m_prozentPlus = _newVal; setDirty(true); } } void CalcPart::setName( const QString& newName ) { if( newName != m_name ) { m_name = newName; setDirty(true); } } /** Wird immer reimplementiert */ Geld CalcPart::basisKosten() { Geld g; return g; } QString CalcPart::getType() const { return i18n("Base"); } void CalcPart::setToDelete(bool val) { m_toDelete = val; } bool CalcPart::isToDelete() { return m_toDelete; } /* * =========================================================================== */ CalcPartList::CalcPartList() :QList() { } Geld CalcPartList::calcPrice() { return costPerCalcPart( ALL_KALKPARTS ); } Geld CalcPartList::costPerCalcPart( const QString& calcPart ) { CalcPart *cp; Geld g; /* suche nach einer speziellen Kalkulationsart */ QListIterator i( *this ); while( i.hasNext()) { cp = i.next(); if( ( calcPart == ALL_KALKPARTS || calcPart == cp->getType() ) && ! cp->isToDelete() ) { g += cp->basisKosten(); } } return g; } /* * Attention: returning non deep copy here ! */ CalcPartList CalcPartList::getCalcPartsList( const QString& calcPart ) { CalcPartList parts; if( calcPart == ALL_KALKPARTS ) return *this; else { CalcPart *cp; /* suche nach einer speziellen Kalkulationsart */ QListIterator i( *this ); while( i.hasNext()) { cp = i.next(); if( calcPart == cp->getType() && ! cp->isToDelete() ) { parts.append(cp); } } } return( parts ); } /* * Attention: returning non deep copy here ! */ CalcPartList CalcPartList::decoupledCalcPartsList() { CalcPartList parts; CalcPart *newcp = 0; CalcPart *cp; QListIterator i( *this ); while( i.hasNext()) { cp = i.next(); if ( cp->getType() == KALKPART_FIX ) { newcp = new FixCalcPart( ); *newcp = *( static_cast( cp ) ); } else if ( cp->getType ()== KALKPART_TIME ) { newcp = new TimeCalcPart( ); *newcp = *( static_cast( cp ) ); } else if ( cp->getType() == KALKPART_MATERIAL ) { newcp = new MaterialCalcPart( ); *newcp = *( static_cast( cp ) ); } if ( newcp ) newcp->setDbID( -1 ); parts.append( newcp ); } return( parts ); } kraft-0.97/src/calcpart.h000066400000000000000000000050111410616450300153060ustar00rootroot00000000000000/*************************************************************************** calcpart.h - ------------------- begin : Mit Dez 31 2003 copyright : (C) 2003 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef CALCPART_H #define CALCPART_H #include #include #include "dbids.h" #include "kraftglobals.h" /**This file contains a part of a calculation. *@author Klaas Freitag */ class CalcPart { public: CalcPart(); CalcPart( int prozent ); CalcPart( const QString& name, int prozent = 0 ); virtual ~CalcPart(); /** Write property of int m_prozentPlus. */ virtual void setProzentPlus( const double& _newVal); /** Read property of int m_prozentPlus. */ virtual const double& getProzentPlus(); /** base costs for one unit */ virtual Geld basisKosten(); void setName( const QString& name ); QString getName() const { return m_name; } virtual QString getType() const; virtual bool isDirty() { return m_dirty; } virtual void setDirty( bool b ) { m_dirty = b; } virtual dbID getDbID() { return m_dbId; } virtual void setDbID( dbID id ) { m_dbId = id; } virtual dbID getTemplID() { return m_templId; } virtual void setTemplID( dbID id ) { m_templId = id; } virtual void setToDelete(bool ); virtual bool isToDelete(); private: double m_prozentPlus; QString m_name; dbID m_dbId; dbID m_templId; bool m_dirty; bool m_toDelete; }; class CalcPartList : public QList { public: CalcPartList(); Geld calcPrice(); Geld costPerCalcPart( const QString& ); CalcPartList getCalcPartsList( const QString& ); CalcPartList decoupledCalcPartsList(); }; typedef QListIterator CalcPartListIterator; #endif kraft-0.97/src/calctemplate.ui000066400000000000000000000625441410616450300163570ustar00rootroot00000000000000 d_calcTempl 0 0 657 801 Edit Template true 1 Template Text: Qt::Horizontal QSizePolicy::Expanding 118 20 &Store in Chapter false cbChapter false 0 160 false &Unit false m_unit Qt::Horizontal 40 20 &Count Time for Overalltime Qt::Horizontal 40 20 0 0 VAT false full half Time Calculation text true Time measureable effort for this template: false false false 4 true Label Duration Hourly Rate Glob. Rate Adds a new time calculation part to the template new... Edits the current time calculation part edit... Deletes the current time calculation part delete Qt::Vertical QSizePolicy::Expanding 20 191 Fix Costs text true Fix costs for this template per one unit: false false false 4 true Amount Label Single Price Overall Price adds a new fix calculation part new... edits the current fix calculation part edit... deletes the current fix calculation part delete Qt::Vertical QSizePolicy::Expanding 20 170 Material text true Needed materials for one unit of this template: false false false 4 true Label Amount Unit Price adds a new material calculation part new... edits the current material part edit... deletes the current material calculation part delete Qt::Vertical QSizePolicy::Expanding 20 160 Overall Price per Unit &Manual Price Calculated Price 99999.000000000000000 0.000000000000000 0 99999 Qt::Horizontal 40 20 textLabel2 Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter false Fix Costs Part: false textLabel2 Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter false Material Part: false textLabel2 Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter false 0 0 Profit: false spBenefit QAbstractSpinBox::UpDownArrows % -1000 1000 Time Calculation Part: false Qt::Vertical 20 40 QFrame::HLine QFrame::Raised 75 true Calculated Price: Qt::PlainText false 75 true 88.888,88 € Qt::PlainText Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter tabWidget cbChapter m_text m_unit m_addTime cbMwst m_rbManual m_rbCalculation m_timeParts m_butAddTime m_butEditTime m_butRemoveTime m_fixParts m_butAddFix m_butEditFix m_butRemoveFix m_matParts m_butAddMat m_butEditMat m_butRemoveMat kraft-0.97/src/catalogchapter.cpp000066400000000000000000000074311410616450300170410ustar00rootroot00000000000000/*************************************************************************** catalogchapter.h - a simle catalog chapter object ------------------- begin : Thu Nov 4 2010 copyright : (C) 2010 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include #include #include #include #include "catalogchapter.h" #include "kraftdb.h" CatalogChapter::CatalogChapter() : mSortKey(0) { } CatalogChapter::CatalogChapter( int id, int csId, const QString& name, int parent, const QString& desc ) :mName( name ), mId( dbID(id) ), mCatalogSetId( dbID(csId) ), mDescription( desc ), mParentId( parent ), mSortKey(0) { } QString CatalogChapter::name() const { return mName; } void CatalogChapter::setName( const QString& name ) { mName = name; } QString CatalogChapter::description() const { return mDescription; } void CatalogChapter::setDescription( const QString& d ) { mDescription = d; } dbID CatalogChapter::id() const { return mId; } dbID CatalogChapter::catalogSetId() const { return mCatalogSetId; } void CatalogChapter::setCatalogSetId( const dbID& id ) { mCatalogSetId = id; } dbID CatalogChapter::parentId() const { return mParentId; } void CatalogChapter::setParentId( const dbID &id ) { mParentId = id; } int CatalogChapter::sortKey() const { return mSortKey; } void CatalogChapter::setSortKey( int key ) { mSortKey = key; } void CatalogChapter::save() { // qDebug () << "Inserting new chapter " << name() << mCatalogSetId.toString() << endl; QSqlQuery q; q.prepare("INSERT INTO CatalogChapters (catalogSetID, chapter, description, sortKey, parentChapter)" "VALUES(:catalogSetID, :chapter, :desc, :sortKey, :parentChapter)"); q.bindValue( ":catalogSetID", mCatalogSetId.toString() ); q.bindValue( ":chapter", this->name() ); q.bindValue( ":desc", this->description() ); q.bindValue( ":sortKey", this->sortKey() ); q.bindValue( ":parentChapter", this->parentId().toInt() ); q.exec(); mId = KraftDB::self()->getLastInsertID(); } bool CatalogChapter::removeFromDB() { // qDebug () << "Removing chapter " << name() << " with id " << mId.toInt(); QSqlQuery q; q.prepare("DELETE FROM CatalogChapters WHERE chapterID=:chapId"); q.bindValue( ":chapId", mId.toInt() ); return q.exec(); } void CatalogChapter::saveNameAndDesc() { QSqlQuery q; q.prepare("UPDATE CatalogChapters SET chapter = :newchapter, description = :desc WHERE chapterID = :id"); q.bindValue(":id", mId.toInt() ); q.bindValue(":desc", this->description() ); q.bindValue(":newchapter", this->name() ); q.exec(); } void CatalogChapter::reparent( const dbID& pId ) { dbID parentId( pId ); setParentId( pId ); QSqlQuery q; q.prepare("UPDATE CatalogChapters SET parentChapter= :p WHERE chapterID = :id"); q.bindValue(":id", mId.toInt() ); q.bindValue(":p", parentId.toInt() ); q.exec(); // qDebug () << "Reparenting chapter " << mId.toInt() << ", reuslt: " << q.lastError().text(); } kraft-0.97/src/catalogchapter.h000066400000000000000000000035371410616450300165110ustar00rootroot00000000000000/*************************************************************************** catalogchapter.h - a simle catalog chapter object ------------------- begin : Thu Nov 4 2010 copyright : (C) 2010 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef CATALOGCHAPTER_H #define CATALOGCHAPTER_H #include #include "kraftcat_export.h" #include class KRAFTCAT_EXPORT CatalogChapter { public: CatalogChapter(); CatalogChapter( int, int, const QString&, int, const QString& = QString() ); QString name() const; void setName( const QString& ); QString description() const; void setDescription( const QString& ); dbID id() const; dbID parentId() const; void setParentId( const dbID& ); dbID catalogSetId() const; void setCatalogSetId( const dbID& ); bool removeFromDB(); int sortKey() const; void setSortKey( int ); void save( ); void saveNameAndDesc(); void reparent( const dbID& ); private: QString mName; dbID mId; dbID mCatalogSetId; QString mDescription; dbID mParentId; int mSortKey; }; #endif // CATALOGCHAPTER_H kraft-0.97/src/catalogselection.cpp000066400000000000000000000141671410616450300174040ustar00rootroot00000000000000/*************************************************************************** katalogselection - widget to select catalog entries from ------------------- begin : 2006-08-30 copyright : (C) 2006 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include "catalogselection.h" #include "catalogtemplate.h" #include "katalogman.h" #include "templkatalog.h" #include "templkataloglistview.h" #include "materialkataloglistview.h" #include "matkatalog.h" #include "docposition.h" #include "filterheader.h" #include #include #include #include #include #include #include #include #include #include #include #include CatalogSelection::CatalogSelection( QWidget *parent ) :QWidget( parent ), mCatalogSelector(nullptr), mWidgets(nullptr) { QVBoxLayout *layout = new QVBoxLayout; layout->setMargin(0); QHBoxLayout *hb = new QHBoxLayout; layout->addLayout(hb); QLabel *l = new QLabel( i18n( "Selected &Catalog: " ) ); hb->addWidget(l); mCatalogSelector = new QComboBox; hb->addWidget(mCatalogSelector); connect( mCatalogSelector, SIGNAL( activated( const QString& ) ), this, SLOT( slotSelectCatalog( const QString& ) ) ); l->setBuddy( mCatalogSelector ); hb->addStretch(); mListSearchLine = new FilterHeader; hb->addWidget(mListSearchLine); mWidgets = new QStackedWidget; mWidgets->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ) ); layout->addWidget(mWidgets); this->setLayout(layout); setupCatalogList(); } void CatalogSelection::setupCatalogList() { QStringList katalogNames = KatalogMan::self()->allKatalogNames(); mCatalogSelector->insertItems(-1, katalogNames ); slotSelectCatalog( katalogNames[0] ); } void CatalogSelection::slotCatalogDoubleClicked( QModelIndex ) { emit actionAppendPosition(); } CatalogTemplateList CatalogSelection::currentSelectedPositions() { CatalogTemplateList re; const QString currentCat = mCatalogSelector->currentText(); if( mWidgetMap.contains( currentCat ) ) { KatalogListView *lv = mWidgetMap[currentCat]; re = lv->selectedTemplates(); } return re; } Katalog* CatalogSelection::currentSelectedKat() { const QString currentCat = mCatalogSelector->currentText(); Katalog *kat = KatalogMan::self()->getKatalog( currentCat ); if ( ! kat ) { qCritical() << "Could not find catalog " << currentCat << endl; } return kat; } QString CatalogSelection::currentSelectedKatChapter() { QString chap; const QString currentCat = mCatalogSelector->currentText(); if( mWidgetMap.contains( currentCat ) ) { KatalogListView *lv = mWidgetMap[currentCat]; chap = lv->selectedCatalogChapter(); } return chap; } void CatalogSelection::slotSelectCatalog( const QString& katName ) { Katalog *kat = KatalogMan::self()->getKatalog( katName ); if ( !kat ) { const QString type = KatalogMan::self()->catalogTypeString( katName ); // qDebug () << "Catalog type for cat " << katName << " is " << type << endl; if ( type == QStringLiteral("TemplCatalog") ) { kat = new TemplKatalog( katName ); } else if ( type == QStringLiteral("MaterialCatalog") ) { kat = new MatKatalog( katName ); } if ( kat ) { KatalogMan::self()->registerKatalog( kat ); } else { qCritical() << "Could not find a valid catalog type for catalog named " << katName << endl; } } if ( kat ) { KatalogListView *katListView = nullptr; if ( ! mWidgetMap.contains( katName ) ) { if ( kat->type() == TemplateCatalog ) { TemplKatalogListView *tmpllistview = new TemplKatalogListView( this ); katListView = tmpllistview; tmpllistview->setShowCalcParts( false ); // qDebug () << "Creating a selection list for catalog " << katName << endl; } else if ( kat->type() == MaterialCatalog ) { MaterialKatalogListView *matListView = new MaterialKatalogListView( this ); katListView = matListView; } if ( katListView ) { katListView->setSelectFromMode(); // mode to only select from mWidgets->addWidget(katListView); mWidgetMap.insert( katName, katListView ); katListView->contextMenu()->addAction( i18n("Append to Document"), this, &CatalogSelection::actionAppendPosition); katListView->addCatalogDisplay( katName ); connect(katListView, &KatalogListView::doubleClicked, this, &CatalogSelection::slotCatalogDoubleClicked); connect(katListView, &KatalogListView::currentItemChanged, this, &CatalogSelection::selectionChanged); KatalogMan::self()->registerKatalogListView( katName, katListView ); } } else { katListView = mWidgetMap[katName]; } // Select the widget if ( katListView ) { mWidgets->setCurrentWidget(katListView); mListSearchLine->setListView(katListView); emit selectionChanged(katListView->currentItem(), nullptr); } } } kraft-0.97/src/catalogselection.h000066400000000000000000000046301410616450300170430ustar00rootroot00000000000000/*************************************************************************** katalogselection - widget to select catalog entries from ------------------- begin : 2006-08-30 copyright : (C) 2006 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef CATALOGSELECTION_H #define CATALOGSELECTION_H #include #include "kataloglistview.h" class QComboBox; class QStackedWidget; class QActionCollection; class QAction; class DocPosition; class FilterHeader; class CalcPartList; class Katalog; class CatalogSelection : public QWidget { Q_OBJECT public: CatalogSelection( QWidget *parent=0 ); ~CatalogSelection() { }; Katalog* currentSelectedKat(); QString currentSelectedKatChapter(); CatalogTemplateList currentSelectedPositions(); protected: void setupCatalogList(); signals: /* * a template was selected to be inserted into the document. This * transports a ptr to the katalog and the item in it. Since the * template type is dependent on the katalog type it is not known * what type of template is coming. It is up to the receiver to * decide (and cast) to the correct template on the katalog type. * * FIXME: Better approach: all catalog items inherit from a base * type. */ void selectionChanged(QTreeWidgetItem * current,QTreeWidgetItem * previous); void actionAppendPosition(); protected slots: void slotSelectCatalog( const QString& ); // void slotAppendToDoc( QListViewItem *item = 0 ); void slotCatalogDoubleClicked( QModelIndex ); private: QComboBox *mCatalogSelector; QStackedWidget *mWidgets; QMap mWidgetMap; FilterHeader *mListSearchLine; }; #endif kraft-0.97/src/catalogtemplate.cpp000066400000000000000000000073021410616450300172230ustar00rootroot00000000000000/*************************************************************************** catalogtemplate - template base class for catalog data ------------------- begin : Oct 2007 copyright : (C) 2007 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include #include #include "catalogtemplate.h" #include "unitmanager.h" CatalogTemplate::CatalogTemplate() : m_calcType( Calculation ), mUseCounter(0), mEntered( QDateTime::currentDateTime() ), mLastModified( QDateTime::currentDateTime() ), mUnitId(0) { } CatalogTemplate::~CatalogTemplate() { } CatalogTemplate::CalculationType CatalogTemplate::calcKind() { return m_calcType; } void CatalogTemplate::setCalculationType( CalculationType t ) { m_calcType = t; } QString CatalogTemplate::calcKindString() const { if( m_calcType == ManualPrice ) return i18n("Manual Price"); else if( m_calcType == Calculation ) return i18n("Calculated"); else if( m_calcType == AutoCalc ) return i18n("AutoCalc"); else return i18n( "Err: Unknown type %d", m_calcType); } void CatalogTemplate::setEnterDate( const QDateTime& d ) { mEntered = d; } QDateTime CatalogTemplate::enterDate() { return mEntered; } void CatalogTemplate::setModifyDate( const QDateTime& d ) { mLastModified = d; } QDateTime CatalogTemplate::modifyDate() { return mLastModified; } void CatalogTemplate::setLastUsedDate( const QDateTime &d ) { mLastUsed = d; } QDateTime CatalogTemplate::lastUsedDate() { return mLastUsed; } void CatalogTemplate::setUseCounter( int cnt ) { mUseCounter = cnt; } int CatalogTemplate::useCounter() { return mUseCounter; } QString CatalogTemplate::getText() const { return mText; } void CatalogTemplate::setText( const QString& str ) { mText = str; } void CatalogTemplate::setUnitId(int id) { mUnitId = id; } Einheit CatalogTemplate::unit() const { return UnitManager::self()->getUnit(mUnitId); } void CatalogTemplate::setChapterId( const dbID& id, bool persist ) { // qDebug () << "set chapterId to " << id.toString(); mChapterId = id; if( persist ) { saveChapterId(); } } void CatalogTemplate::saveChapterId() { // qDebug () << "WRN: Chapter ID saving for template not implemented!"; } dbID CatalogTemplate::chapterId() { return mChapterId; } // ================================================================================ CatalogTemplateList::CatalogTemplateList() :QList() { } CatalogTemplateList::~CatalogTemplateList() { } int CatalogTemplateList::compareItems( CatalogTemplate *ct1, CatalogTemplate *ct2 ) { // CatalogTemplate* ct1 = static_cast( i1 ); // CatalogTemplate* ct2 = static_cast( i2 ); // qDebug () << "********************************* In Sort!" << endl; if ( !( ct1 && ct2 ) ) return 0; int sortKey1 = ct1->sortKey(); int sortKey2 = ct2->sortKey(); if ( sortKey1 == sortKey2 ) return 0; if ( sortKey1 < sortKey2 ) return -1; return 1; } kraft-0.97/src/catalogtemplate.h000066400000000000000000000054151410616450300166730ustar00rootroot00000000000000/*************************************************************************** catalogtemplate - template base class for catalog data ------------------- begin : Oct 2007 copyright : (C) 2007 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef CATALOGTEMPLATE_H #define CATALOGTEMPLATE_H /** * base class that is the base for all templates in kraft catalogs. */ #include #include "kraftcat_export.h" #include "dbids.h" class QWidget; class CatalogSelection; class Katalog; class Geld; class Einheit; class KRAFTCAT_EXPORT CatalogTemplate { public: typedef enum { Unknown, ManualPrice, Calculation, AutoCalc } CalculationType; CatalogTemplate(); virtual ~CatalogTemplate(); virtual bool save() = 0; virtual Geld unitPrice() = 0; CalculationType calcKind(); void setCalculationType( CalculationType t ); QString calcKindString() const ; int sortKey() { return mSortKey; } void setSortKey( int k ) { mSortKey = k; } void setEnterDate( const QDateTime& ); QDateTime enterDate(); void setModifyDate( const QDateTime& ); QDateTime modifyDate(); void setLastUsedDate( const QDateTime& ); QDateTime lastUsedDate(); void setUseCounter( int ); int useCounter(); QString getText() const; void setText( const QString& ); void setChapterId( const dbID&, bool ); dbID chapterId(); Einheit unit() const; void setUnitId(int id); protected: virtual void saveChapterId(); CalculationType m_calcType; int mSortKey; int mUseCounter; dbID mChapterId; // the chapter (==parent) of the item QDateTime mEntered; QDateTime mLastModified; QDateTime mLastUsed; QString mText; private: int mUnitId; }; class KRAFTCAT_EXPORT CatalogTemplateList : public QList { public: CatalogTemplateList(); virtual ~CatalogTemplateList(); protected: // int compareItems( QPtrCollection::Item, QPtrCollection::Item ); virtual int compareItems( CatalogTemplate*, CatalogTemplate* ); }; typedef QListIterator CatalogTemplateListIterator; #endif kraft-0.97/src/catalogtemplateprovider.cpp000066400000000000000000000052471410616450300210040ustar00rootroot00000000000000/*************************************************************************** catalogtemplateprovider - template provider class for catalog data ------------------- begin : 2007-05-23 copyright : (C) 2007 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include #include "catalogtemplateprovider.h" #include "texteditdialog.h" #include "doctext.h" #include "defaultprovider.h" #include "katalog.h" #include "catalogselection.h" CatalogTemplateProvider::CatalogTemplateProvider( QWidget *parent ) :TemplateProvider( parent ), mCatalogSelection(nullptr) { } Katalog *CatalogTemplateProvider::currentCatalog() { Katalog *kat {nullptr}; if (mCatalogSelection) { kat = mCatalogSelection->currentSelectedKat(); } return kat; } void CatalogTemplateProvider::setCatalogSelection( CatalogSelection *cs ) { mCatalogSelection = cs; connect( mCatalogSelection, SIGNAL( actionAppendPosition() ), this, SLOT( slotTemplateToDocument() ) ); } void CatalogTemplateProvider::slotNewTemplate() { qDebug () << "SlotNewTemplate for Catalog called!" << endl; if ( mCatalogSelection ) { Katalog *catalog = mCatalogSelection->currentSelectedKat(); CatalogTemplateList list; const QString currKat = mCatalogSelection->currentSelectedKatChapter(); emit templatesToDocument(catalog, list, currKat); } } void CatalogTemplateProvider::slotEditTemplate() { // qDebug () << "SlotEditTemplate for Catalog called!" << endl; // mCatalogSelection->currentSelectedPositions } void CatalogTemplateProvider::slotDeleteTemplate() { } void CatalogTemplateProvider::slotTemplateToDocument() { // qDebug () << "Moving catalog entry to document" << endl; if ( mCatalogSelection ) { Katalog *catalog = mCatalogSelection->currentSelectedKat(); emit templatesToDocument(catalog, mCatalogSelection->currentSelectedPositions(), QString()); } } kraft-0.97/src/catalogtemplateprovider.h000066400000000000000000000033301410616450300204400ustar00rootroot00000000000000/*************************************************************************** catalogtemplateprovider - template provider classes for catalog data ------------------- begin : 2007-05-24 copyright : (C) 2007 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef CATALOGTEMPLATEPROVIDER_H #define CATALOGTEMPLATEPROVIDER_H #include "templateprovider.h" #include "doctext.h" #include "catalogtemplate.h" #include "katalog.h" class QWidget; class CatalogSelection; class CatalogTemplateProvider : public TemplateProvider { Q_OBJECT public: CatalogTemplateProvider( QWidget* ); void setCatalogSelection( CatalogSelection * ); Katalog *currentCatalog(); signals: void templatesToDocument( Katalog*, CatalogTemplateList, const QString& ); void catalogSelected(Katalog*); public slots: void slotNewTemplate(); void slotEditTemplate(); void slotDeleteTemplate(); void slotTemplateToDocument(); private: CatalogSelection *mCatalogSelection; }; #endif kraft-0.97/src/createdb.ui000066400000000000000000000074641410616450300154720ustar00rootroot00000000000000 createDbForm 0 0 395 232 Database creation and initial schema setup: true 0 0 / 129 30 0 X Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Filling the database with initial values: true 0 0 / 129 30 0 X Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 0 0 Status: QFrame::NoFrame Database setup status... true Qt::Vertical 20 149 kraft-0.97/src/databasesettings.kcfg000066400000000000000000000030141410616450300175260ustar00rootroot00000000000000 localhost -1 root kraft-0.97/src/databasesettings.kcfgc000066400000000000000000000001241410616450300176700ustar00rootroot00000000000000File=databasesettings.kcfg ClassName=DatabaseSettings Singleton=true Mutators=true kraft-0.97/src/dbids.h000066400000000000000000000044131410616450300146070ustar00rootroot00000000000000/*************************************************************************** dbids.h - database id class ------------------- begin : ? copyright : (C) 2006- by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef DBIDS_H #define DBIDS_H #include #include #include /** * utility class that provides a simple database id object. * It's useful to work with dicts which do not work on base * types like int */ class dbID { public: dbID(int id):m_id(id){} dbID():m_id(-1){} int intID() const { return m_id; } bool operator==( const int& _u ) const { return m_id == _u; } bool operator==( const long& _u ) const { return m_id == _u; } bool operator==( const dbID& _u ) const { return m_id == _u.m_id; } bool operator!=( const dbID& _u ) const { return m_id != _u.m_id; } dbID& operator=( const QString& _u ) { bool ok; int id = _u.toInt( &ok ); if( ok ) { m_id = id; } return *this; } bool operator<( dbID _id ) { if( m_id < _id.toInt() ) return true; return false; } dbID& operator=( const int _u ) { m_id = _u; return *this; } bool isOk() const { return m_id > -1; } int toInt() { return m_id; } QString toString() const { return QString::number(m_id); } private: int m_id; }; typedef QHash dbIdDict; typedef QHashIterator dbIdDictIterator; typedef QList DBIdList; #endif kraft-0.97/src/dbinit.ui000066400000000000000000000052011410616450300151550ustar00rootroot00000000000000 dbInitWidget 0 0 556 244 Database Update: true Overall Progress: 0 TextLabel Detail Progress: 0 TextLabel Status: TextLabel kraft-0.97/src/dbselect.ui000066400000000000000000000036361410616450300155030ustar00rootroot00000000000000 dbSelectForm 0 0 419 255 <html><head/><body><p>Kraft uses a database backend to store values. By default it uses a file based database with easy setup targeted to single user mode.</p><p><br/></p></body></html> true SQLite 3 - file based database (default) true buttonGroup MySQL Serverbased Database for advanced Setups buttonGroup Qt::Vertical 20 94 kraft-0.97/src/defaultprovider.cpp000066400000000000000000000237631410616450300172650ustar00rootroot00000000000000/*************************************************************************** defaultprovider.cpp - Default Providing Class ------------------- begin : November 2006 copyright : (C) 2006 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include #include #include #include #include #include #include "defaultprovider.h" #include "kraftdb.h" #include "doctext.h" #include "kraftsettings.h" #include "doctype.h" #include "kraftdoc.h" #include "dbids.h" #include Q_GLOBAL_STATIC(DefaultProvider, mSelf) DefaultProvider *DefaultProvider::self() { return mSelf; } DefaultProvider::DefaultProvider() { } QString DefaultProvider::docType() { QString type = KraftSettings::self()->doctype(); if ( type.isEmpty() ) { QStringList allTypes = DocType::allLocalised(); if( ! allTypes.isEmpty() ) { type = DocType::allLocalised()[0]; } else { type = i18n( "Unknown" ); } } return type; } DocTextList DefaultProvider::documentTexts( const QString& docType, KraftDoc::Part tt ) { DocTextList re; QString typeStr = DocText::textTypeToString( tt ); QString sql = QString( "SELECT texts.docTextID, texts.name, texts.text, texts.description, " "texts.textType, types.name as docTypeName FROM DocTexts texts, " "DocTypes types WHERE texts.docTypeId=types.docTypeID AND " "types.name=\'%1\' AND textType = \'%2\'").arg( docType ).arg( typeStr ); // qDebug() << "Reading texts from DB with: " << sql << endl; QSqlQuery query( sql ); if ( query.isActive() ) { while ( query.next() ) { DocText dt; dt.setDbId( query.value( 0 ) /* docTextID */ .toInt() ); dt.setName( query.value( 1 ) /* name */ .toString() ); dt.setText( KraftDB::self()->mysqlEuroDecode( query.value( 2 ) /* text */ .toString() ) ); dt.setDescription( query.value( 3 ) /* description */ .toString() ); dt.setTextType( DocText::stringToTextType( query.value( 4 ) /* textType */ .toString() ) ); dt.setDocType( query.value( 5 ) /* docType */ .toString() ); re.append( dt ); } } return re; } QString DefaultProvider::defaultText( const QString& docType, KraftDoc::Part p, DocGuardedPtr ) { QString re; DocTextList list = documentTexts( docType, p ); DocTextList::iterator it; for ( it = list.begin(); it != list.end(); ++it ) { if( (*it).isStandardText() ) { re = ( *it ).text(); break; } } return re; } dbID DefaultProvider::saveDocumentText( const DocText& t ) { dbID retVal; QSqlTableModel model; model.setTable( "DocTexts" ); if ( t.dbId().isOk() ) { // qDebug () << "Doing update!"; model.setFilter( "docTextID=" + t.dbId().toString() ); model.select(); if( model.rowCount() > 0 ) { QSqlRecord record = model.record(0); record.setValue( "docTextID", t.dbId().toString() ); record.setValue( "name", t.name() ); record.setValue( "description", t.description() ); record.setValue( "text", KraftDB::self()->mysqlEuroEncode( t.text() ) ); record.setValue( "docType", t.docType() ); record.setValue( "docTypeId", DocType::docTypeId( t.docType() ).toString() ); record.setValue( "textType", t.textTypeString() ); model.setRecord(0, record); model.submitAll(); } } else { // qDebug () << "Doing insert!"; QSqlRecord record = model.record(); record.setValue( "name", t.name() ); record.setValue( "description", t.description() ); record.setValue( "text", KraftDB::self()->mysqlEuroEncode( t.text() ) ); record.setValue( "docType", t.docType() ); record.setValue( "docTypeId", DocType::docTypeId( t.docType() ).toString() ); record.setValue( "textType", t.textTypeString() ); model.insertRecord(-1, record); model.submitAll(); } retVal = KraftDB::self()->getLastInsertID(); return retVal; } QLocale* DefaultProvider::locale() { return &_locale; } void DefaultProvider::deleteDocumentText( const DocText& dt ) { if ( dt.dbId().isOk() ) { QSqlQuery q; q.prepare("DELETE FROM DocTexts WHERE docTextID=" + dt.dbId().toString() ) ; q.exec(); } else { // qDebug () << "Delete document text not ok: " << dt.text(); } } QString DefaultProvider::currencySymbol() const { return self()->locale()->currencySymbol(); } QString DefaultProvider::iconvTool() const { return locateBinary( "iconv" ); } QString DefaultProvider::getStyleSheet( const QString& styleName ) const { QString style; if( styleName.isEmpty() ) return style; const QString findFile = QString("styles/%1.style").arg(styleName); const QString tmplFile = locateFile(findFile); QFile data( tmplFile ); if (data.open( QFile::ReadOnly )) { QTextStream readIn( &data ); style = readIn.readAll(); data.close(); } return style; } // this method uses QStandardPath::locate from the AppDataLocation to find // files, but if KRAFT_HOME is set, that one is preffered. QString DefaultProvider::locateFile(const QString& findFile) const { QString re; const QString prjPath = QString::fromUtf8(qgetenv( "KRAFT_HOME" )); if( prjPath.isEmpty()) { re = QStandardPaths::locate( QStandardPaths::AppDataLocation, findFile ); } else { re = prjPath; if( !re.endsWith(QChar('/')) ) { re.append( QChar('/')); } re.append(findFile); QFileInfo fi(re); if( !fi.exists() ) { if( findFile.startsWith("pics")) { // special handling: formerly the pics in KRAFT_HOME were in src. re = prjPath; if( !re.endsWith(QChar('/')) ) { re.append( QChar('/')); } re.append("src/"); re.append(findFile); } else { qDebug() << "WARN: locateFile could not find file " << findFile; } } } return re; } QString DefaultProvider::locateKraftTool(const QString& toolName) const { QString fullPath; fullPath = locateFile("tools/" + toolName); if (fullPath.isEmpty()) { fullPath = QStandardPaths::findExecutable(toolName); } QFileInfo fi(fullPath); if (!fi.exists()) { fullPath.clear(); } return fullPath; } QString DefaultProvider::locateBinary(const QString& name) const { QString bin; if (!name.isEmpty()) { bin = QStandardPaths::findExecutable( name ); } return bin; } QStringList DefaultProvider::findTrml2Pdf( ) const { // define the default value to compare against, to see if there is a custom // value in the settings file. const QString rmlbinDefault = QStringLiteral( "trml2pdf" ); // FIXME: how to get the default value? const QString rmlbin = KraftSettings::self()->trml2PdfBinary(); // qDebug () << "### Start searching rml2pdf bin: " << rmlbin; QStringList retList; // mHavePdfMerge = false; if ( rmlbin != rmlbinDefault ) { retList = rmlbin.split(' ', QString::SkipEmptyParts); } else { // The value in the config is not, as it is still the same as the default // read from either KRAFT_HOME or search in the system. QString p = QString::fromUtf8(qgetenv("KRAFT_HOME")); if( !p.isEmpty() ) { p += QLatin1String("/tools/erml2pdf.py"); // qDebug () << "Found erml2pdf from KRAFT_HOME: " << p; if( QFile::exists( p ) ) { retList << "python3"; retList << p; // mHavePdfMerge = true; } } else { const QString ermlpy = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "kraft/tools/erml2pdf.py" ); // qDebug () << "Ermlpy: " << ermlpy; if( ! ermlpy.isEmpty() ) { // need the python3 interpreter, check for it QString python = QStandardPaths::findExecutable(QLatin1String("python3")); if( python.isEmpty() ) { qCritical() << "ERR: Unable to find python3, thats a problem"; } else { // qDebug () << "Using python: " << python; retList << python; retList << ermlpy; // mHavePdfMerge = true; } } } if (retList.isEmpty() ){ // tool erml2pdf.py not found. Try trml2pdf_kraft.sh for legacy reasons QString trml2pdf = QStandardPaths::findExecutable(QLatin1String("trml2pdf_kraft.sh")); if( trml2pdf.isEmpty() ) { // qDebug () << "Could not find trml2pdf_kraft.sh"; } else { // qDebug () << "Found trml2pdf: " << trml2pdf; retList << trml2pdf; // mHavePdfMerge = true; } } } if ( retList.isEmpty() ) { qDebug () << "ReportLab based PDF conversion script not found!"; } return retList; } bool DefaultProvider::writeXmlArchive() { return KraftSettings::self()->doXmlArchive(); } QString DefaultProvider::xmlArchivePath() { return KraftSettings::self()->xmlArchivePath(); } QString DefaultProvider::pdfOutputDir() { return KraftSettings::self()->pdfOutputDir(); } DefaultProvider::~DefaultProvider() { } kraft-0.97/src/defaultprovider.h000066400000000000000000000043121410616450300167170ustar00rootroot00000000000000/*************************************************************************** defaultprovider.h - Defaults for this and that ------------------- begin : November 2006 copyright : (C) 2006 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef DEFAULTPROVIDER_H #define DEFAULTPROVIDER_H #include #include "kraftcat_export.h" #include "kraftdoc.h" #include "doctext.h" class QSqlRecord; class QStringList; class dbID; /** * encapsulates all relevant for default values for documents such as * texts etc. */ class KRAFTCAT_EXPORT DefaultProvider { public: ~DefaultProvider(); static DefaultProvider *self(); QString defaultText( const QString&, KraftDoc::Part, DocGuardedPtr = 0 ); dbID saveDocumentText( const DocText& ); void deleteDocumentText( const DocText& ); QString docType(); // the default document type for new docs DocTextList documentTexts( const QString&, KraftDoc::Part ); QString currencySymbol() const; QLocale* locale(); QString iconvTool() const; QStringList findTrml2Pdf() const; QString locateKraftTool(const QString& toolName) const; QString locateBinary(const QString& name) const; QString locateFile(const QString& findFile) const; QString getStyleSheet( const QString& ) const; DefaultProvider(); bool writeXmlArchive(); QString pdfOutputDir(); QString xmlArchivePath(); private: // static DefaultProvider *mSelf; QLocale _locale; const QString EuroTag; }; #endif kraft-0.97/src/docassistant.cpp000066400000000000000000000355541410616450300165660ustar00rootroot00000000000000/*************************************************************************** docassistant.cpp - Assistant widget ------------------- begin : April 2007 copyright : (C) 2007 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include #include #include #include #include #include #include "docassistant.h" #include "docpostcard.h" #include "catalogselection.h" #include "textselection.h" #include "kraftsettings.h" #include "kataloglistview.h" #include "doctext.h" #include "defaultprovider.h" #include "headertemplateprovider.h" #include "footertemplateprovider.h" #include "catalogtemplateprovider.h" #include "addresstemplateprovider.h" DocAssistant::DocAssistant( QWidget *parent ): QSplitter( parent ), mFullPreview( true ), mActivePage( KraftDoc::Header ) { setOrientation( Qt::Vertical ); QWidget *topWidget = new QWidget; QVBoxLayout *topVBox = new QVBoxLayout; topVBox->setMargin(0); topWidget->setLayout( topVBox ); QHBoxLayout *buttonLayout = new QHBoxLayout; topVBox->addLayout( buttonLayout ); QPushButton *pb = new QPushButton( i18n( "Show &Templates" ) ); buttonLayout->addWidget( pb ); connect( pb, SIGNAL( toggled( bool ) ), this, SLOT( slotToggleShowTemplates( bool ) ) ); pb->setCheckable( true ); pb->setToolTip( i18n( "Show mask to create or select templates to be used in the document" ) ); buttonLayout->addStretch(); topVBox->addLayout(buttonLayout); mPostCard = new DocPostCard; mPostCard->slotSetMode( DocPostCard::Full, KraftDoc::Header ); // setResizeMode( vb /* mPostCard->view() */, KeepSize ); topVBox->addWidget(mPostCard); addWidget(topWidget); mTemplatePane = new QWidget; QVBoxLayout *bottomVBox = new QVBoxLayout; bottomVBox->setMargin(0); mTemplatePane->setLayout( bottomVBox ); addWidget( mTemplatePane ); setStretchFactor(0, 0); setStretchFactor(1, 0); mWidgetStack = new QStackedWidget; bottomVBox->addWidget( mWidgetStack ); mWidgetStack->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); /* Selections are the gui reprenentations of the template providing catalogs * like header- and footer texts and catalogs. */ mCatalogSelection = new CatalogSelection; mWidgetStack->addWidget( mCatalogSelection ); connect( mCatalogSelection, SIGNAL( selectionChanged(QTreeWidgetItem*,QTreeWidgetItem*) ), this, SLOT( slotCatalogSelectionChanged(QTreeWidgetItem*,QTreeWidgetItem*) ) ); mHeaderSelector = new TextSelection( 0, KraftDoc::Header ); mWidgetStack->addWidget( mHeaderSelector ); connect( mHeaderSelector, SIGNAL(validTemplateSelected() ), this, SLOT( slotTemplateSelectionChanged() ) ); connect( mHeaderSelector, SIGNAL(editCurrentTemplate()), this, SLOT(slotEditTemplate())); mFooterSelection = new TextSelection( 0, KraftDoc::Footer ); mWidgetStack->addWidget( mFooterSelection ); connect( mFooterSelection, SIGNAL(validTemplateSelected()), this, SLOT(slotTemplateSelectionChanged())); connect( mFooterSelection, SIGNAL(editCurrentTemplate()), this, SLOT(slotEditTemplate())); connect( mFooterSelection, SIGNAL( actionCurrentTextToDoc() ), this, SLOT( slotAddToDocument() ) ); connect( mPostCard, SIGNAL( selectPage( int ) ), this, SLOT( slotSelectDocPart( int ) ) ); QHBoxLayout *butHBox2 = new QHBoxLayout; bottomVBox->addLayout( butHBox2 ); QIcon icons = QIcon::fromTheme( "go-previous" ); // KDE 4 icon name: go-previous mPbAdd = new QPushButton( icons, QString() ); mPbAdd->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum ); connect( mPbAdd, SIGNAL( clicked() ), this, SLOT( slotAddToDocument() ) ); butHBox2->addWidget( mPbAdd ); mPbAdd->setToolTip( i18n( "Add a template to the document" ) ); icons = QIcon::fromTheme( "document-new" ); mPbNew = new QPushButton( icons, QString() ); // KDE 4 icon name: document-new mPbNew->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum ); connect( mPbNew, SIGNAL( clicked() ), this, SLOT( slotNewTemplate() ) ); mPbNew->setToolTip( i18n( "Create a new template" ) ); butHBox2->addWidget( mPbNew ); icons = QIcon::fromTheme( "document-properties" ); mPbEdit = new QPushButton( icons, QString() ); // KDE 4 icon name: document-properties mPbEdit->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum ); connect( mPbEdit, SIGNAL( clicked() ), this, SLOT( slotEditTemplate() ) ); mPbEdit->setToolTip( i18n( "Edit the current template" ) ); butHBox2->addWidget( mPbEdit ); icons = QIcon::fromTheme( "edit-delete" ); mPbDel = new QPushButton( icons, QString() ); // KDE 4 icon name: edit-delete mPbDel->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum ); connect( mPbDel, SIGNAL( clicked() ), this, SLOT( slotDeleteTemplate() ) ); mPbDel->setToolTip( i18n( "Delete the current template" ) ); butHBox2->addWidget( mPbDel ); butHBox2->addStretch(); mPbAdd->setEnabled( false ); mPbNew->setEnabled( false ); mPbEdit->setEnabled( false ); mPbDel->setEnabled( false ); /* Template Provider initialisations */ mHeaderTemplateProvider = new HeaderTemplateProvider( parent ); /* get a new header text from the default provider */ connect( mHeaderTemplateProvider, SIGNAL( newHeaderText( const DocText& ) ), this, SLOT( slotNewHeaderDocText( const DocText& ) ) ); connect( mHeaderTemplateProvider, SIGNAL( updateHeaderText( const DocText& ) ), this, SLOT( slotUpdateHeaderDocText( const DocText& ) ) ); connect( mHeaderTemplateProvider, SIGNAL( headerTextToDocument( const DocText& ) ), this, SLOT( slotHeaderTextToDocument( const DocText& ) ) ); connect( mHeaderTemplateProvider, SIGNAL( deleteHeaderText( const DocText& ) ), this, SLOT( slotHeaderTextDeleted( const DocText& ) ) ); mHeaderTemplateProvider->setSelection( mHeaderSelector ); mFooterTemplateProvider = new FooterTemplateProvider( parent ); /* get a new Footer text from the default provider */ connect( mFooterTemplateProvider, SIGNAL( newFooterText( const DocText& ) ), this, SLOT( slotNewFooterDocText( const DocText& ) ) ); connect( mFooterTemplateProvider, SIGNAL( updateFooterText( const DocText& ) ), this, SLOT( slotUpdateFooterDocText( const DocText& ) ) ); connect( mFooterTemplateProvider, SIGNAL( footerTextToDocument( const DocText& ) ), this, SLOT( slotFooterTextToDocument( const DocText& ) ) ); connect( mFooterTemplateProvider, SIGNAL( deleteFooterText( const DocText& ) ), this, SLOT( slotFooterTextDeleted( const DocText& ) ) ); mFooterTemplateProvider->setSelection( mFooterSelection ); /* Catalog Template Provider */ mCatalogTemplateProvider = new CatalogTemplateProvider( parent ); mCatalogTemplateProvider->setCatalogSelection( mCatalogSelection ); connect(mCatalogTemplateProvider, &CatalogTemplateProvider::templatesToDocument, this, &DocAssistant::templatesToDocument); mCurrTemplateProvider = mHeaderTemplateProvider; const QList sizes = KraftSettings::self()->assistantSplitterSetting(); if( sizes.count() > 0 ) { setSizes( sizes ); } mTemplatePane->hide(); } void DocAssistant::slotAddToDocument() { // qDebug () << "SlotAddToDocument called!" << endl; if ( mCurrTemplateProvider ) { mCurrTemplateProvider->slotTemplateToDocument(); } } void DocAssistant::slotTemplateSelectionChanged( ) { if (!mCurrTemplateProvider) { mPbNew->setEnabled(false); mPbEdit->setEnabled(false); mPbDel->setEnabled(false); return; } if( mActivePage == KraftDoc::Positions ) { // no editing on the catalogs bool enableNew {false}; auto kat = static_cast(mCurrTemplateProvider)->currentCatalog(); if (kat->type() == KatalogType::TemplateCatalog) { enableNew = true; } mPbNew->setEnabled(enableNew); mPbEdit->setEnabled( false ); mPbDel->setEnabled( false ); } else { bool mv {false}; if( mActivePage == KraftDoc::Header ) { mv = mHeaderSelector->validSelection(); } else if( mActivePage == KraftDoc::Footer ) { mv = mFooterSelection->validSelection(); } mPbAdd->setEnabled( mv ); mPbNew->setEnabled( true ); mPbEdit->setEnabled( mv ); mPbDel->setEnabled( mv ); } } void DocAssistant::slotCatalogSelectionChanged(QTreeWidgetItem *current ,QTreeWidgetItem *) { // enable the move-to-document button. // qDebug () << "catalog position selection changed!" << endl; if ( current ) { mPbAdd->setEnabled( true ); } else { mPbAdd->setEnabled( false ); } slotTemplateSelectionChanged(); } void DocAssistant::slotNewTemplate() { /* always set the doc type in case the provider benefits from that */ mCurrTemplateProvider->slotSetDocType( mDocType ); mCurrTemplateProvider->slotNewTemplate(); } /* a new header doc text was created and should go to the document */ void DocAssistant::slotNewHeaderDocText( const DocText& dt ) { /* show in list of texts in the GUI */ mHeaderSelector->addNewDocText( dt ); } /* called with a changed text that needs to be updated in the view */ void DocAssistant::slotUpdateHeaderDocText( const DocText& dt ) { mHeaderSelector->updateDocText( dt ); } /* the user hit "add to document" to use a header text template */ void DocAssistant::slotHeaderTextToDocument( const DocText& dt ) { emit headerTextTemplate( dt.text() ); } /* a new header doc text was created and should go to the document */ void DocAssistant::slotNewFooterDocText( const DocText& dt ) { /* show in list of texts in the GUI */ mFooterSelection->addNewDocText( dt ); } /* called with a changed text that needs to be updated in the view */ void DocAssistant::slotUpdateFooterDocText( const DocText& dt ) { mFooterSelection->updateDocText( dt ); } /* the user hit "add to document" to use a header text template */ void DocAssistant::slotFooterTextToDocument( const DocText& dt ) { emit footerTextTemplate( dt.text() ); } /* Slot that initiates an edit */ void DocAssistant::slotEditTemplate() { // qDebug () << "Editing a template using the currentTemplProvider" << endl; if ( mCurrTemplateProvider ) { mCurrTemplateProvider->slotSetDocType( mDocType ); mCurrTemplateProvider->slotEditTemplate(); } } /* slot that initialises a delete, called from the delete button */ void DocAssistant::slotDeleteTemplate() { QMessageBox msgBox; msgBox.setText(i18n( "Do you really want to delete the template permanently?\n" "It can not be recovered.")); msgBox.setStandardButtons(QMessageBox::Yes| QMessageBox::No); msgBox.setDefaultButton(QMessageBox::Yes); int ret = msgBox.exec(); if ( ret == QMessageBox::No ) { return; } if ( mCurrTemplateProvider ) { mCurrTemplateProvider->slotDeleteTemplate(); } } void DocAssistant::slotHeaderTextDeleted( const DocText& /* dt */) { mHeaderSelector->deleteCurrentText(); slotTemplateSelectionChanged( ); // disable the edit buttons etc. } void DocAssistant::slotFooterTextDeleted( const DocText& /* dt */) { mFooterSelection->deleteCurrentText(); slotTemplateSelectionChanged( ); // disable the edit buttons etc. } /* slot that opens the template details in case on == true */ void DocAssistant::slotToggleShowTemplates( bool on ) { if ( on ) { // setFullPreview is set in the subslots called from here, that // makes mFullPreview truly reflecting the state of the toggle button if ( mActivePage == KraftDoc::Header ) { slotShowHeaderTemplates(); } else if ( mActivePage == KraftDoc::Positions ) { slotShowCatalog(); } else if ( mActivePage == KraftDoc::Footer ) { slotShowFooterTemplates(); } } else { // hide the details setFullPreview( true, mActivePage ); } emit toggleShowTemplates( on ); } DocPostCard *DocAssistant::postCard() { return mPostCard; } CatalogSelection* DocAssistant::catalogSelection() { return mCatalogSelection; } /* sets the Part of the doc, eg. Header, Footer */ void DocAssistant::slotSelectDocPart( int p ) { mActivePage = p; if( mActivePage == KraftDoc::Header ) { mCurrTemplateProvider = mHeaderTemplateProvider; } else if( mActivePage == KraftDoc::Positions ) { mCurrTemplateProvider = mCatalogTemplateProvider; } else if( mActivePage == KraftDoc::Footer ) { mCurrTemplateProvider = mFooterTemplateProvider; } else { // qDebug () << "Alert: Unknown document part id: " << p; } emit selectPage( p ); slotToggleShowTemplates( !mFullPreview ); slotTemplateSelectionChanged( ); // hide the add, edit- and del buttons } /* Doc Type like offer, invoice etc. */ void DocAssistant::slotSetDocType( const QString& type ) { mDocType = type; mHeaderSelector->slotSelectDocType( type ); mFooterSelection->slotSelectDocType( type ); } void DocAssistant::slotShowCatalog( ) { setFullPreview( false, KraftDoc::Positions ); mWidgetStack->setCurrentWidget( mCatalogSelection ); } void DocAssistant::slotShowHeaderTemplates() { setFullPreview( false, KraftDoc::Header ); mWidgetStack->setCurrentWidget( mHeaderSelector ); } void DocAssistant::slotShowFooterTemplates() { setFullPreview( false, KraftDoc::Footer ); mWidgetStack->setCurrentWidget( mFooterSelection ); } void DocAssistant::setFullPreview( bool setFull, int id ) { if ( setFull ) { /* remember the sizes used before */ saveSplitterSizes(); mTemplatePane->hide(); mPostCard->slotSetMode( DocPostCard::Full, id ); mFullPreview = true; } else { mTemplatePane->show(); mPostCard->slotSetMode( DocPostCard::Mini, id ); if ( KraftSettings::self()->assistantSplitterSetting().size() == 2 ) { QList sizes = KraftSettings::self()->assistantSplitterSetting(); if( sizes.contains(0)) { sizes[0] = 50; sizes[1] = 50; } setSizes( sizes ); } mFullPreview = false; } } void DocAssistant::saveSplitterSizes() { if( mTemplatePane->isVisible() ) { const QList s = sizes(); KraftSettings::self()->setAssistantSplitterSetting( s ); } } kraft-0.97/src/docassistant.h000066400000000000000000000071131410616450300162210ustar00rootroot00000000000000/*************************************************************************** docassistant.h - Assistant widget ------------------- begin : April 2007 copyright : (C) 2007 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef DOCASSISTANT_H #define DOCASSISTANT_H #include #include #include #include #include #include "kraftdoc.h" #include "catalogtemplate.h" #include "docpostcard.h" #include "catalogselection.h" class TextSelection; class QWidget; class QPushButton; class Katalog; class TemplateProvider; class HeaderTemplateProvider; class CatalogTemplateProvider; class FooterTemplateProvider; class AddressTemplateProvider; class DocText; class QSplitter; using namespace KContacts; class DocAssistant : public QSplitter { Q_OBJECT public: DocAssistant( QWidget* ); DocPostCard *postCard(); CatalogSelection *catalogSelection(); void saveSplitterSizes(); public slots: void slotShowCatalog(); void slotShowHeaderTemplates(); void slotShowFooterTemplates(); void setFullPreview( bool, int ); void slotSelectDocPart( int ); void slotToggleShowTemplates( bool ); void slotAddToDocument(); void slotNewTemplate(); void slotEditTemplate(); void slotDeleteTemplate(); void slotSetDocType( const QString& ); protected slots: void slotTemplateSelectionChanged(); void slotHeaderTextToDocument( const DocText& ); void slotFooterTextDeleted( const DocText& ); void slotHeaderTextDeleted( const DocText& ); void slotNewHeaderDocText( const DocText& ); void slotUpdateHeaderDocText( const DocText& ); void slotCatalogSelectionChanged( QTreeWidgetItem*,QTreeWidgetItem* ); void slotNewFooterDocText( const DocText& ); void slotUpdateFooterDocText( const DocText& ); void slotFooterTextToDocument( const DocText& ); signals: void selectPage( int ); void templatesToDocument( Katalog*, CatalogTemplateList, const QString&); void toggleShowTemplates( bool ); void addressTemplate( const Addressee& ); void headerTextTemplate( const QString& ); void footerTextTemplate( const QString& ); private: DocPostCard *mPostCard; CatalogSelection *mCatalogSelection; QStackedWidget *mWidgetStack; TextSelection *mFooterSelection; TextSelection *mHeaderSelector; bool mFullPreview; int mActivePage; QPushButton *mPbAdd; QPushButton *mPbNew; QPushButton *mPbEdit; QPushButton *mPbDel; QWidget *mTemplatePane; QString mDocType; // QSplitter *mMainSplit; TemplateProvider *mCurrTemplateProvider; HeaderTemplateProvider *mHeaderTemplateProvider; AddressTemplateProvider *mAddressTemplateProvider; CatalogTemplateProvider *mCatalogTemplateProvider; FooterTemplateProvider *mFooterTemplateProvider; }; #endif kraft-0.97/src/docdigest.cpp000066400000000000000000000062421410616450300160240ustar00rootroot00000000000000/*************************************************************************** docdigest.cpp ------------------- begin : Wed Mar 15 2006 copyright : (C) 2006 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include #include #include #include #include "docdigest.h" #include "defaultprovider.h" #include "format.h" #include "kraftsettings.h" DocDigest::DocDigest( dbID id, const QString& type, const QString& clientID ) :mID(id), mType( type ), mClientId( clientID ), mLocale( "kraft" ), _archDocLazyLoaded(false) { } DocDigest::DocDigest() :mLocale( "kraft" ), _archDocLazyLoaded(false) { } QString DocDigest::date() const { return Format::toDateString(mDate, KraftSettings::self()->dateFormat()); } QDate DocDigest::rawDate() const { return mDate; } QString DocDigest::lastModified() const { const QString re = QString( "%1 %2").arg( Format::toDateString(mLastModified.date(), KraftSettings::self()->dateFormat())) .arg(mLastModified.time().toString("hh:mm")); return re; } ArchDocDigestList DocDigest::archDocDigestList() { if( !_archDocLazyLoaded ) { const QString id(ident()); qDebug() << "Querying archdocs for document ident " << id; QSqlQuery query; query.prepare("SELECT archDocID, ident, printDate, state FROM archdoc WHERE" " ident=:id ORDER BY printDate DESC" ); query.bindValue(":id", id); query.exec(); while(query.next()) { int archDocID = query.value(0).toInt(); const QString dbIdent = query.value(1).toString(); QDateTime printDateTime = query.value(2).toDateTime(); int state = query.value(3).toInt(); mArchDocs.append( ArchDocDigest( printDateTime, state, dbIdent, dbID(archDocID) ) ); } _archDocLazyLoaded = true; } return mArchDocs; } KContacts::Addressee DocDigest::addressee() const { return mContact; } void DocDigest::setAddressee( const KContacts::Addressee& contact ) { mContact = contact; } /* *************************************************************************** */ DocDigestsTimeline::DocDigestsTimeline() :mMonth( 0 ), mYear( 0 ) { } DocDigestsTimeline::DocDigestsTimeline( int m, int y ) :mMonth( m ), mYear( y ) { } void DocDigestsTimeline::setDigestList( const DocDigestList& list ) { mDigests = list; } kraft-0.97/src/docdigest.h000066400000000000000000000065541410616450300154770ustar00rootroot00000000000000/*************************************************************************** docdigest.h - ------------------- begin : Wed Mar 15 2006 copyright : (C) 2006 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef DOCDIGEST_H #define DOCDIGEST_H #include #include #include #include "dbids.h" #include "archdoc.h" class QString; class QDate; typedef QList ArchDocDigestList; class DocDigest { public: DocDigest( dbID id, const QString& type, const QString& clientID ); DocDigest(); QString clientId() const { return mClientId; } void setClientId( const QString& id ) { mClientId = id; } QString clientAddress() const { return mClientAddress; } void setClientAddress( const QString& address ) { mClientAddress = address; } KContacts::Addressee addressee() const; void setAddressee( const KContacts::Addressee& ); QString type() const { return mType; } void setType( const QString& t ) { mType = t; } QString date() const; void setDate( const QDate& date ) { mDate = date; } QDate rawDate() const; QString lastModified() const; void setLastModified( const QDateTime& date ) { mLastModified = date; } QString id() const { return mID.toString(); } void setId( dbID id ) { mID = id; } QString ident() const { return mIdent; } void setIdent( const QString& ident ) { mIdent = ident; } QString whiteboard() const { return mWhiteboard; } void setWhiteboard( const QString& white ) { mWhiteboard = white; } void setProjectLabel( const QString& prjLabel ) { mProjectLabel = prjLabel; } QString projectLabel() const { return mProjectLabel; } ArchDocDigestList archDocDigestList(); protected: dbID mID; QString mType; QString mClientId; QString mIdent; QString mWhiteboard; QString mProjectLabel; QString mClientAddress ; QDateTime mLastModified; QDate mDate; QLocale mLocale; ArchDocDigestList mArchDocs; bool _archDocLazyLoaded; private: KContacts::Addressee mContact; }; typedef QList DocDigestList; typedef QList DocDigestListIterator; class DocDigestsTimeline { public: DocDigestsTimeline(); DocDigestsTimeline( int, int ); int month() { return mMonth; } void setMonth( int m ) { mMonth = m; } int year() { return mYear; } void setYear( int y ) { mYear = y; } DocDigestList digests() { return mDigests; } void setDigestList( const DocDigestList& ); void clearDigestList() { mDigests.clear (); } private: int mMonth, mYear; DocDigestList mDigests; }; typedef QList DocDigestsTimelineList; #endif kraft-0.97/src/docdigestdetailview.cpp000066400000000000000000000425641410616450300201110ustar00rootroot00000000000000/*************************************************************************** docdigestdetailview.cpp - Details of a doc digest ------------------- begin : februry 2011 copyright : (C) 2011 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include #include #include #include #include #include #include "docdigest.h" #include "docdigestdetailview.h" #include "defaultprovider.h" #include "htmlview.h" #include "texttemplate.h" #include "archdoc.h" DocDigestHtmlView::DocDigestHtmlView( QWidget *parent ) : HtmlView( parent ) { connect(this, SIGNAL(openUrl(QUrl)), this, SLOT(slotLinkClicked(QUrl))); } void DocDigestHtmlView::slotLinkClicked(const QUrl& url) { const QUrlQuery q(url); // Url is like "http://localhost/show_last_print?id=5" const QString idStr = q.queryItemValue(QLatin1String("id")); const QString path = url.path(); if( path.endsWith("show_last_print")) { bool ok; emit( showLastPrint( dbID(idStr.toInt(&ok)) ) ); } } // ######################################################################################################### DocDigestDetailView::DocDigestDetailView(QWidget *parent) : QFrame(parent) { setFrameStyle(QFrame::StyledPanel+QFrame::Raised); QHBoxLayout *hbox = new QHBoxLayout; hbox->setSpacing(0); const int detailMinWidth = 260; setFixedHeight(200); // --- The left details box _leftDetails = new QLabel; hbox->addWidget(_leftDetails); _leftDetails->setTextFormat(Qt::RichText); _leftDetails->setMinimumWidth(detailMinWidth); _leftDetails->setFrameStyle(0); // --- The middle HTML based view hbox->setMargin(0); setLayout( hbox ); mHtmlCanvas = new DocDigestHtmlView( this ); mHtmlCanvas->setFrameStyle(0); mHtmlCanvas->setStylesheetFile("docdigestview.css"); connect( mHtmlCanvas, SIGNAL(showLastPrint( const dbID& )), this, SIGNAL( showLastPrint( const dbID& ) ) ); hbox->addWidget( mHtmlCanvas); const QString bgColor = mHtmlCanvas->palette().base().color().name(); const QString style = QString("QLabel { " "background-color: %1; " "background-image: url(:/kraft/kraft_customer.png); background-repeat: repeat-none;" "background-position: top left; " "padding: 10px; " "}").arg(bgColor); _leftDetails->setStyleSheet(style); _leftDetails->setWordWrap(true); // --- The right details Box const QString styleR = QString("QLabel { " "background-color: %1;" "background-image: url(:/kraft/postit.png); background-repeat: repeat-none;" "background-position: top center;" "padding: 0px; " "padding-left: 10px; " "}").arg(bgColor); _rightDetails = new QLabel; _rightDetails->setTextFormat(Qt::RichText); _rightDetails->setStyleSheet(styleR); _rightDetails->setMinimumWidth(detailMinWidth); _rightDetails->setWordWrap(true); hbox->addWidget(_rightDetails); } void DocDigestDetailView::slotClearView() { const QString details; mHtmlCanvas->displayContent( details ); } QString DocDigestDetailView::widgetStylesheet( Location loc, Detail det ) { const QString bgColor = mHtmlCanvas->palette().base().color().name(); QString style = QString("QLabel { background-color: %1; ").arg(bgColor); QString image; QString bgPos; if( loc == Left ) { if( det == Year ) { image = "Calendar_page.png"; bgPos = "center top"; style += QLatin1String("padding-top: 95px; "); } else if( det == Month ) { image = "Calendar_page.png"; bgPos = "center top"; style += QLatin1String("padding-top: 75px; "); } else { // Document image = "kraft_customer.png"; bgPos = "top left"; style += QLatin1String( "padding-top: 50px; padding-left:15px;"); } } else if(loc == Middle ) { if( det == Year ) { } else if( det == Month ) { } else { // Document } } else if(loc == Right ) { if( det == Year ) { } else if( det == Month ) { } else { // Document image = "postit.png"; bgPos = "top center"; style += QLatin1String("padding: 0px; padding-left: 30px; "); } } else { // undef. } if( !image.isEmpty() ) { style += QString("background-image: url(:/kraft/%1); background-repeat: repeat-none;" "background-position: %2;").arg(image).arg(bgPos); } style += QLatin1String("}"); return style; } #define DOCDIGEST_TAG void DocDigestDetailView::documentListing( TextTemplate *tmpl, int year, int month ) { QString minDate; QString maxDate; if( month > -1 ) { QDate theDate(year, month, 1); // not a year minDate = theDate.toString("yyyy-MM-dd"); int lastDay = theDate.daysInMonth(); theDate.setDate(year, month, lastDay); maxDate = theDate.toString("yyyy-MM-dd"); } else { // is is a year minDate = QString::number(year)+"-01-01"; maxDate = QString::number(year)+"-12-31"; } // read data in the given timeframe from database QSqlQuery q; const QString query = QString("SELECT archDocID, ident, MAX(printDate) FROM archdoc WHERE " "date BETWEEN date('%1') AND date('%2') " "GROUP BY ident").arg(minDate, maxDate); // qDebug() << "***" << query; QMap > docMatrix; q.prepare(query); q.exec(); while( q.next() ) { dbID archDocId(q.value(0).toInt()); const ArchDoc doc(archDocId); const QString docType = doc.docType(); Geld g; int n = 0; if( docMatrix.contains(docType)) { g = docMatrix[docType].second; n = docMatrix[docType].first; } Geld g1 = doc.nettoSum(); g += g1; docMatrix[docType].first = n+1; docMatrix[docType].second = g; } // now create the template tmpl->setValue("I18N_AMOUNT", i18n("Amount")); tmpl->setValue("I18N_TYPE", i18n("Type")); tmpl->setValue("I18N_SUM", i18n("Sum")); QStringList doctypes = docMatrix.keys(); doctypes.sort(); foreach( const QString dtype, doctypes ) { qDebug() << "creating doc list for "<createDictionary( "DOCUMENTS" ); tmpl->setValue("DOCUMENTS", "DOCTYPE", dtype); const QString am = QString::number(docMatrix[dtype].first); tmpl->setValue("DOCUMENTS", "AMOUNT", am); const QString sm = docMatrix[dtype].second.toString(); tmpl->setValue("DOCUMENTS", "SUM", sm); } } void DocDigestDetailView::slotShowMonthDetails( int year, int month ) { if( _monthTemplFileName.isEmpty() ) { _monthTemplFileName = DefaultProvider::self()->locateFile( "views/monthdigest.thtml" ); } TextTemplate tmpl; tmpl.setTemplateFileName(_monthTemplFileName); if( !tmpl.isOk() ) { return; } const QString monthStr = DefaultProvider::self()->locale()->monthName(month); const QString yearStr = QString::number(year); tmpl.setValue( DOCDIGEST_TAG("HEADLINE"), i18n("Results in %1 %2", monthStr, yearStr) ); tmpl.setValue( DOCDIGEST_TAG("YEAR_LABEL"), i18n("Year")); tmpl.setValue( DOCDIGEST_TAG("YEAR_NUMBER"), yearStr); tmpl.setValue( DOCDIGEST_TAG("MONTH_LABEL"), i18n("Month")); tmpl.setValue( DOCDIGEST_TAG("MONTH_NAME"), monthStr); // Document listing documentListing(&tmpl, year, month); // left and right information blocks _leftDetails->setStyleSheet(widgetStylesheet(Left, Month)); _leftDetails->setText( "

            "+monthStr + "
            " + yearStr + "

            "); _leftDetails->setAlignment(Qt::AlignHCenter); _rightDetails->setStyleSheet(widgetStylesheet(Right, Month)); _rightDetails->clear(); const QString details = tmpl.expand(); mHtmlCanvas->displayContent(details); } void DocDigestDetailView::slotShowYearDetails( int year ) { if( _yearTemplFileName.isEmpty() ) { _yearTemplFileName = DefaultProvider::self()->locateFile( "views/yeardigest.thtml" ); } TextTemplate tmpl; tmpl.setTemplateFileName(_yearTemplFileName); if( !tmpl.isOk() ) { return; } const QString yearStr = QString::number(year); tmpl.setValue( DOCDIGEST_TAG("YEAR_LABEL"), i18n("Year")); tmpl.setValue( DOCDIGEST_TAG("YEAR_NUMBER"), yearStr); tmpl.setValue( DOCDIGEST_TAG("HEADLINE"), i18n("Results in Year %1", yearStr) ); documentListing(&tmpl, year, -1); const QString details = tmpl.expand(); _leftDetails->setStyleSheet(widgetStylesheet(Left, Year)); _leftDetails->setText("

            "+ yearStr +"

            "); _leftDetails->setAlignment(Qt::AlignHCenter); _rightDetails->setStyleSheet(widgetStylesheet(Right, Year)); _rightDetails->clear(); mHtmlCanvas->displayContent( details ); } void DocDigestDetailView::showAddress( const KContacts::Addressee& addressee, const QString& manAddress ) { Q_UNUSED(addressee) QString content = "

            " + i18n("Customer") +"

            "; if( !manAddress.isEmpty() ) { content += "
            " + manAddress +"
            "; } else { content += QLatin1String("

            ")+i18n("not set")+QLatin1String("

            "); } _leftDetails->setText( content ); #if 0 // tmpl.setValue( "URL", mHtmlCanvas->baseURL().prettyUrl()); tmpl.setValue( DOCDIGEST_TAG( "CUSTOMER_LABEL" ), i18n("Customer")); KContacts::Addressee addressee = digest.addressee(); QString adr = digest.clientAddress(); adr.replace('\n', "
            " ); tmpl.setValue( DOCDIGEST_TAG("CUSTOMER_ADDRESS_FIELD"),adr ); QString addressBookInfo; if( addressee.isEmpty() ) { if( digest.clientId().isEmpty() ) { addressBookInfo = i18n("The address is not listed in an address book."); } else { addressBookInfo = i18n("The client has the address book id %1 but can not found in our address books.", digest.clientId()); } } else { addressBookInfo = i18n("The client can be found in our address books."); tmpl.createDictionary( "CLIENT_ADDRESS_SECTION"); tmpl.setValue( "CLIENT_ADDRESS_SECTION", DOCDIGEST_TAG( "CLIENTID" ), digest.clientId() ); tmpl.setValue( "CLIENT_ADDRESS_SECTION", DOCDIGEST_TAG( "CLIENT_ADDRESS" ), digest.clientAddress() ); tmpl.setValue( "CLIENT_ADDRESS_SECTION", DOCDIGEST_TAG( "CLIENT_NAME"), addressee.realName() ); tmpl.setValue( "CLIENT_ADDRESS_SECTION", DOCDIGEST_TAG( "CLIENT_ORGANISATION"), addressee.organization() ); tmpl.setValue( "CLIENT_ADDRESS_SECTION", DOCDIGEST_TAG( "CLIENT_URL"), addressee.url().toString() ); tmpl.setValue( "CLIENT_ADDRESS_SECTION", DOCDIGEST_TAG( "CLIENT_EMAIL"), addressee.preferredEmail() ); KContacts::Address clientAddress; clientAddress = addressee.address( KContacts::Address::Pref ); QString addressType = i18n("preferred address"); if( clientAddress.isEmpty() ) { clientAddress = addressee.address( KContacts::Address::Home ); addressType = i18n("home address"); } if( clientAddress.isEmpty() ) { clientAddress = addressee.address( KContacts::Address::Work ); addressType = i18n("work address"); } if( clientAddress.isEmpty() ) { clientAddress = addressee.address( KContacts::Address::Postal ); addressType = i18n("postal address"); } if( clientAddress.isEmpty() ) { clientAddress = addressee.address( KContacts::Address::Intl ); addressType = i18n("international address"); } if( clientAddress.isEmpty() ) { clientAddress = addressee.address( KContacts::Address::Dom ); addressType = i18n("domestic address"); } if( clientAddress.isEmpty() ) { addressType = i18n("unknown"); // qDebug () << "WRN: Address is still empty!"; } tmpl.setValue( "CLIENT_ADDRESS_SECTION", DOCDIGEST_TAG( "CLIENT_POSTBOX" ), clientAddress.postOfficeBox() ); tmpl.setValue( "CLIENT_ADDRESS_SECTION", DOCDIGEST_TAG( "CLIENT_EXTENDED" ), clientAddress.extended() ); tmpl.setValue( "CLIENT_ADDRESS_SECTION", DOCDIGEST_TAG( "CLIENT_STREET" ), clientAddress.street() ); tmpl.setValue( "CLIENT_ADDRESS_SECTION", DOCDIGEST_TAG( "CLIENT_LOCALITY" ), clientAddress.locality() ); tmpl.setValue( "CLIENT_ADDRESS_SECTION", DOCDIGEST_TAG( "CLIENT_REGION" ), clientAddress.region() ); tmpl.setValue( "CLIENT_ADDRESS_SECTION", DOCDIGEST_TAG( "CLIENT_POSTCODE" ), clientAddress.postalCode() ); tmpl.setValue( "CLIENT_ADDRESS_SECTION", DOCDIGEST_TAG( "CLIENT_COUNTRY" ), clientAddress.country() ); tmpl.setValue( "CLIENT_ADDRESS_SECTION", DOCDIGEST_TAG( "CLIENT_REGION" ), clientAddress.region() ); tmpl.setValue( "CLIENT_ADDRESS_SECTION", DOCDIGEST_TAG( "CLIENT_LABEL" ), clientAddress.label() ); tmpl.setValue( "CLIENT_ADDRESS_SECTION", DOCDIGEST_TAG( "CLIENT_ADDRESS_TYPE" ), addressType ); } tmpl.setValue( DOCDIGEST_TAG("CUSTOMER_ADDRESSBOOK_INFO"), addressBookInfo ); #endif } void DocDigestDetailView::slotShowDocDetails( DocDigest digest ) { // qDebug () << "Showing details about this doc: " << digest.id(); if( _docTemplFileName.isEmpty() ) { // QString templFileName = QString( "kraftdoc_%1_ro.trml" ).arg( doc->docType() ); _docTemplFileName = DefaultProvider::self()->locateFile( "views/docdigest.thtml" ); } TextTemplate tmpl; // template file with name docdigest.trml tmpl.setTemplateFileName(_docTemplFileName); if( !tmpl.isOk() ) { return; } tmpl.setValue( DOCDIGEST_TAG( "HEADLINE" ), digest.type() + " " + digest.ident() ); tmpl.setValue( DOCDIGEST_TAG( "DATE" ), digest.date() ); tmpl.setValue( DOCDIGEST_TAG( "DATE_LABEL" ), i18n("Date") ); tmpl.setValue( DOCDIGEST_TAG( "WHITEBOARD"), digest.whiteboard() ); tmpl.setValue( DOCDIGEST_TAG( "WHITEBOARD_LABEL"), i18n("Whiteboard")); if( !digest.projectLabel().isEmpty() ) { tmpl.createDictionary( "PROJECT_INFO" ); tmpl.setValue( "PROJECT_INFO", DOCDIGEST_TAG( "PROJECT"), digest.projectLabel() ); tmpl.setValue( "PROJECT_INFO", DOCDIGEST_TAG( "PROJECT_LABEL"), i18n("Project")); } showAddress( digest.addressee(), digest.clientAddress() ); // Information about archived documents. ArchDocDigestList archDocs = digest.archDocDigestList(); if( archDocs.isEmpty() ) { // qDebug () << "No archived docs for this document!"; tmpl.createDictionary( DOCDIGEST_TAG( "NEVER_PRINTED" )); tmpl.setValue( "NEVER_PRINTED", DOCDIGEST_TAG("NEVER_PRINTED_LABEL"), i18n("This document was never printed.")); } else { ArchDocDigest digest = archDocs[0]; QFileInfo fi(digest.pdfArchiveFileName()); if (fi.exists()) { tmpl.createDictionary( DOCDIGEST_TAG( "PRINTED" )); tmpl.setValue( "PRINTED", DOCDIGEST_TAG("LAST_PRINT_LABEL"), i18n( "Last printed" ) ); tmpl.setValue( "PRINTED", DOCDIGEST_TAG("LAST_PRINT_TITLE"), i18n( "Opens last created PDF document" ) ); tmpl.setValue( "PRINTED", DOCDIGEST_TAG("LAST_PRINT_LINK_TEXT"), i18n( "open" ) ); tmpl.setValue( "PRINTED", DOCDIGEST_TAG("LAST_PRINT_DATE"), digest.printDate().toString() ); tmpl.setValue( "PRINTED", DOCDIGEST_TAG("LAST_PRINTED_ID"), digest.archDocId().toString() ); if( archDocs.size() == 1 ) { tmpl.setValue( "PRINTED", DOCDIGEST_TAG("ARCHIVED_COUNT"), i18n("One older print")); } else { tmpl.setValue( "PRINTED", DOCDIGEST_TAG("ARCHIVED_COUNT"), i18n("%1 older prints", archDocs.count())); } } else { tmpl.createDictionary( DOCDIGEST_TAG( "NEVER_PRINTED" )); tmpl.setValue( "NEVER_PRINTED", DOCDIGEST_TAG("NEVER_PRINTED_LABEL"), i18n("Archived documents can not be found. Check PDF Output dir.")); } } const QString details = tmpl.expand(); mHtmlCanvas->displayContent( details ); _rightDetails->setText(digest.whiteboard()); _leftDetails->setStyleSheet(widgetStylesheet(Left, Document)); _leftDetails->setAlignment(Qt::AlignLeft); _rightDetails->setStyleSheet(widgetStylesheet(Right, Document)); // qDebug () << "BASE-URL of htmlview is " << mHtmlCanvas->baseURL(); } kraft-0.97/src/docdigestdetailview.h000066400000000000000000000043561410616450300175530ustar00rootroot00000000000000/*************************************************************************** docdigestdetailview.cpp - Details of a doc digest ------------------- begin : februry 2011 copyright : (C) 2011 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef DOCDIGESTDETAILVIEW_H #define DOCDIGESTDETAILVIEW_H #include #include #include "docdigest.h" #include "htmlview.h" class dbID; class TextTemplate; class DocDigestHtmlView : public HtmlView { Q_OBJECT public: DocDigestHtmlView( QWidget *parent ); signals: void showLastPrint( const dbID& ); protected slots: void slotLinkClicked(const QUrl& url); }; class DocDigestDetailView : public QFrame { Q_OBJECT public: explicit DocDigestDetailView(QWidget *parent = 0); signals: void showLastPrint( const dbID& ); public slots: void slotShowDocDetails( DocDigest ); void slotClearView(); void slotShowMonthDetails( int year, int month ); void slotShowYearDetails( int year); private: void showAddress( const KContacts::Addressee& addressee, const QString& manAddress ); void documentListing( TextTemplate *tmpl, int year, int month ); enum Location { Left, Middle, Right }; enum Detail { Month, Year, Document }; QString widgetStylesheet( Location loc, Detail det ); DocDigestHtmlView *mHtmlCanvas; QLabel *_leftDetails; QLabel *_rightDetails; QString _docTemplFileName; QString _monthTemplFileName; QString _yearTemplFileName; }; #endif // DOCDIGESTDETAILVIEW_H kraft-0.97/src/docfooter.ui000066400000000000000000000071601410616450300156760ustar00rootroot00000000000000 DocFooterEdit 0 0 560 282 3 Qt::Vertical QSizePolicy::Expanding 20 16 Footer Texts &Summary Text on Last Page: false m_teSummary 0 0 &Greeting: false m_cbGreeting true false Tax Document &Tax: false mTaxCombo Qt::Horizontal QSizePolicy::Expanding 361 20 kraft-0.97/src/docguardedptr.h000066400000000000000000000002061410616450300163450ustar00rootroot00000000000000#ifndef DOCGUARDEDPTR #define DOCGUARDEDPTR #include class KraftDoc; typedef QPointer DocGuardedPtr; #endif kraft-0.97/src/docheader.ui000066400000000000000000000203611410616450300156260ustar00rootroot00000000000000 DocHeaderEdit 0 0 614 451 0 30 30 16777215 TextLabel Qt::Horizontal QSizePolicy::Expanding 0 0 true &Project: Qt::AlignTop false mProjectLabelEdit &Whiteboard: Qt::AlignTop false m_whiteboardEdit 0 0 Enter a label that describes the project. This may appear on the customer document. 255 Qt::Vertical 20 40 0 0 16777215 80 true Postal &Address: Qt::AlignTop false m_postAddressEdit not selected false Qt::Horizontal QSizePolicy::Fixed 10 20 Select an addressee from the address books. select... Qt::Horizontal QSizePolicy::Expanding 41 20 &Salutatory Address: false m_letterHead Customer: false 0 0 16777215 100 &Entry Text on First Page: false m_teEntry mButtLang m_cbType m_letterHead kraft-0.97/src/docposition.cpp000066400000000000000000000235111410616450300164070ustar00rootroot00000000000000/*************************************************************************** docposition.cpp - a position in a document ------------------- begin : Fri Jan 20 2006 copyright : (C) 2006 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ // include files for Qt #include #include #include #include // application specific includes #include "einheit.h" #include "geld.h" #include "docposition.h" #include "ui_positionwidget.h" #include "positionviewwidget.h" #include "defaultprovider.h" /** @author Klaas Freitag */ DocPositionBase::DocPositionBase() : QObject(), m_dbId( -1 ), mToDelete( false ), mTaxType( TaxFull ), mType( Position ), mAttribs( QString::fromLatin1( "Position" ) ) { } DocPositionBase::DocPositionBase( const PositionType& t ) : QObject(), m_dbId( -1 ), mToDelete( false ), mTaxType( TaxFull ), mType( t ), mAttribs( QString::fromLatin1( "Position" ) ) { } DocPositionBase::DocPositionBase(const DocPositionBase& b ) : QObject(), m_dbId( b.m_dbId ), m_position( b.m_position ), m_text( b.m_text ), mToDelete( b.mToDelete ), mTaxType( TaxFull ), mType( b.mType ), mAttribs( b.mAttribs ) { } DocPositionBase& DocPositionBase::operator=( const DocPositionBase& dp ) { if ( this == &dp ) return *this; m_dbId = dp.m_dbId; m_position = dp.m_position; m_text = dp.m_text; mToDelete = dp.mToDelete; mType = dp.mType; mAttribs = dp.mAttribs; mTaxType = dp.mTaxType; return *this; } void DocPositionBase::setAttribute( const Attribute& attrib ) { if( ! attrib.name().isEmpty() ) { mAttribs[ attrib.name() ] = attrib; } } AttributeMap DocPositionBase::attributes() { return mAttribs; } void DocPositionBase::setAttributeMap( AttributeMap attmap ) { mAttribs = attmap; } void DocPositionBase::loadAttributes() { if ( m_dbId == -1 ) { // qDebug () << "Can not load attributes, no valid database id!" << endl; return; } mAttribs.load( m_dbId ); } void DocPositionBase::removeAttribute( const QString& name ) { if ( !name.isEmpty() ) mAttribs.markDelete( name ); } QString DocPositionBase::attribute( const QString& attName ) const { Attribute att = mAttribs[ attName ]; return att.value().toString(); } void DocPositionBase::setTag( const QString& tag ) { if ( tag.isEmpty() ) return; if ( mAttribs.contains( DocPosition::Tags ) ) { if ( hasTag( tag ) ) return; Attribute att = mAttribs[DocPosition::Tags]; QStringList li = att.value().toStringList(); li.append( tag ); att.setValue( QVariant( li ) ); setAttribute( att ); } else { QStringList li; li.append( tag ); Attribute a( DocPosition::Tags ); a.setValueRelation( "tagTemplates", "tagTmplID", "name" ); a.setListValue( true ); a.setPersistant( true ); a.setValue( QVariant( li ) ); setAttribute( a ); } } void DocPositionBase::removeTag( const QString& tag ) { if ( hasTag( tag ) ) { Attribute att = mAttribs[DocPosition::Tags]; QStringList li = att.value().toStringList(); li.removeAll( tag ); att.setValue( QVariant( li ) ); setAttribute( att ); } } bool DocPositionBase::hasTag( const QString& tag ) { if ( ! mAttribs.contains( DocPosition::Tags ) ) { return false; } Attribute att = mAttribs[DocPosition::Tags]; QStringList li = att.value().toStringList(); if( li.contains( tag, Qt::CaseInsensitive ) ) { // ignore case return true; } return false; } QStringList DocPositionBase::tags() { QStringList tags; if ( mAttribs.contains( DocPosition::Tags ) ) { // qDebug () << mAttribs[DocPosition::Tags].toString() << endl; tags = mAttribs[DocPosition::Tags].value().toStringList(); } return tags; } DocPositionBase::TaxType DocPositionBase::taxType() { return mTaxType; } void DocPositionBase::setTaxType( TaxType tt ) { mTaxType = tt; } void DocPositionBase::setTaxType( int tt ) { mTaxType = (TaxType) tt; } int DocPositionBase::taxTypeNumeric() { if ( mTaxType == TaxNone ) return 1; else if ( mTaxType == TaxReduced ) return 2; else if ( mTaxType == TaxFull ) return 3; // qDebug () << "ERR: Vat-type ambigous!"; return 0; // Invalid } // ############################################################## const QString DocPosition::Kind( QString::fromLatin1( "kind" ) ); const QString DocPosition::Discount( QString::fromLatin1( "discount" ) ); const QString DocPosition::Tags( QString::fromLatin1( "tags" ) ); const QString DocPosition::ExtraDiscountTagRequired( QString::fromLatin1( "discountTagRequired" ) ); DocPosition::DocPosition(): DocPositionBase() ,m_amount( 1.0 ), mWidget( 0 ) { m_text = QString(); } DocPosition::DocPosition( const PositionType& t ) : DocPositionBase( t ), mWidget( 0 ) { } Geld DocPosition::overallPrice() { Geld g; AttributeMap atts = attributes(); // all kinds beside from no kind mean that the position is not // counted for the overall price. That's a FIXME if ( ! atts.contains( DocPosition::Kind ) ) { g = unitPrice() * amount(); } return g; } // ############################################################## DocPositionList::DocPositionList() : QList() { // setAutoDelete( true ); } Geld DocPositionList::bruttoPrice(double fullTax, double reducedTax ) { Geld g = nettoPrice(); g += taxSum( fullTax, reducedTax ); return g; } Geld DocPositionList::nettoPrice() { Geld g; DocPositionListIterator it( *this ); while( it.hasNext() ) { g += static_cast(it.next())->overallPrice(); } return g; } Geld DocPositionList::fullTaxSum( double fullTax ) { Geld sum; if ( fullTax < 0 ) { qCritical() << "Full Tax is not loaded!"; } DocPositionListIterator it( *this ); while( it.hasNext() ) { DocPosition *dp = static_cast( it.next() ); if( dp->taxTypeNumeric() == DocPositionBase::TaxFull ) { sum += dp->overallPrice(); } } Geld tax; if( sum.toLong() > 0 ) { tax = sum.percent(fullTax); } return tax; } Geld DocPositionList::reducedTaxSum( double reducedTax ) { Geld sum; if ( reducedTax < 0 ) { qCritical() << "Reduced Tax is not loaded!"; } DocPositionListIterator it( *this ); while( it.hasNext() ) { DocPosition *dp = static_cast( it.next() ); if( dp->taxTypeNumeric() == DocPositionBase::TaxReduced ) { sum += dp->overallPrice(); } } Geld tax; if(sum.toLong() > 0 ) { tax = sum.percent(reducedTax); } return tax; } Geld DocPositionList::taxSum( double fullTax, double redTax ) { Geld sum; Geld fulltax = fullTaxSum(fullTax); Geld reducedtax = reducedTaxSum(redTax); sum += fulltax; sum += reducedtax; return sum; } QString DocPositionList::posNumber( DocPositionBase* pos ) { return QString::number( 1+indexOf( pos ) ); } QDomElement DocPositionList::domElement( QDomDocument& doc ) { QDomElement topElem = doc.createElement( "positions" ); QDomElement posElem; int num = 1; DocPositionListIterator it( *this ); while( it.hasNext() ) { DocPosition *dpb = static_cast( it.next() ); if( dpb->type() == DocPositionBase::Position ) { DocPosition *dp = static_cast(dpb); posElem = doc.createElement( "position" ); posElem.setAttribute( "number", num++ ); topElem.appendChild( posElem ); posElem.appendChild( xmlTextElement( doc, "text", dp->text() ) ); double am = dp->amount(); QString h = QString::number(am, 'f', 2 ); posElem.appendChild( xmlTextElement( doc, "amount", h )); Einheit e = dp->unit(); posElem.appendChild( xmlTextElement( doc, "unit", e.einheit( am ) ) ); Geld g = dp->unitPrice(); posElem.appendChild( xmlTextElement( doc, "unitprice", QString::number(g.toDouble(), 'f', 2 ))); Geld sum(g * am); posElem.appendChild( xmlTextElement( doc, "sumprice", QString::number(sum.toDouble(), 'f', 2 ) ) ); } } return topElem; } int DocPositionList::compareItems ( DocPosition *dp1, DocPosition *dp2 ) { //DocPositionBase *dpb1 = static_cast( item1 ); //DocPositionBase *dpb2 = static_cast( item2 ); int sortkey1 = dp1->positionNumber(); int sortkey2 = dp2->positionNumber(); int res = 0; if( sortkey1 > sortkey2 ) res = 1; if( sortkey2 < sortkey1 ) res = -1; // qDebug()<< "In sort: comparing " << p1 << " with " << p2 << " = " << res << endl; return res; } QDomElement DocPositionList::xmlTextElement( QDomDocument& doc, const QString& name, const QString& value ) { QDomElement elem = doc.createElement( name ); QDomText t = doc.createTextNode( value ); elem.appendChild( t ); return elem; } DocPositionBase *DocPositionList::positionFromId( int id ) { DocPosition *dp = 0; DocPositionListIterator it( *this ); while( it.hasNext() ) { dp = static_cast( it.next() ); if( dp->dbId() == id ) { break; } } return dp; } kraft-0.97/src/docposition.h000066400000000000000000000111171410616450300160530ustar00rootroot00000000000000/*************************************************************************** docposition.h - a position in a document ------------------- begin : Fri Jan 20 2006 copyright : (C) 2006 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef DOCPOSITION_H #define DOCPOSITION_H // include files for Qt #include #include #include // application specific includes #include "dbids.h" #include "calcpart.h" #include "attribute.h" #include "einheit.h" /** @author Klaas Freitag */ class QString; class QDomElement; class QDomDocument; class Geld; class dbID; class QLocale; class PositionViewWidget; class DocPositionBase : public QObject { public: enum PositionType { Position, ExtraDiscount }; enum TaxType { TaxInvalid = 0, TaxNone = 1, TaxReduced = 2, TaxFull = 3, TaxIndividual = 4 }; DocPositionBase(); DocPositionBase( const PositionType& ); ~DocPositionBase() {} DocPositionBase(const DocPositionBase&); void setDbId( int id ) { m_dbId = id; } dbID dbId() { return dbID( m_dbId ); } void setAttribute( const Attribute& ); void removeAttribute( const QString& ); void loadAttributes(); QString attribute(const QString& ) const; AttributeMap attributes(); void setAttributeMap( AttributeMap ); void setText( const QString& string ) { m_text = string; } QString text() const { return m_text; } void setTag( const QString& ); void removeTag( const QString& ); bool hasTag( const QString& ); QStringList tags(); int taxTypeNumeric(); TaxType taxType(); void setTaxType( DocPositionBase::TaxType ); void setTaxType( int ); /** * Position means the number in the document */ virtual int positionNumber() { return m_position; } virtual void setPositionNumber( const int& pos ) { m_position = pos; } virtual void setToDelete( bool doit ) { mToDelete = doit; } virtual bool toDelete() { return mToDelete; } PositionType type() { return mType; } DocPositionBase& operator=( const DocPositionBase& ); protected: int m_dbId; int m_position; QString m_text; bool mToDelete; TaxType mTaxType; PositionType mType; AttributeMap mAttribs; }; class DocPosition : public DocPositionBase { public: DocPosition(); DocPosition( const PositionType& ); ~DocPosition(){}; void setUnit( const Einheit& unit ) { m_unit = unit; } Einheit unit() const { return m_unit; } void setUnitPrice( const Geld& g ) { m_unitPrice = g; } Geld unitPrice() const { return m_unitPrice; } Geld overallPrice(); void setAmount( double amount ) { m_amount = amount; } double amount() { return m_amount; } PositionViewWidget* associatedWidget() { return mWidget; } void setAssociatedWidget( PositionViewWidget *w ) { mWidget = w; } static const QString Kind; static const QString Discount; static const QString Tags; static const QString ExtraDiscountTagRequired; private: Einheit m_unit; Geld m_unitPrice; double m_amount; PositionViewWidget *mWidget; // No calculation yet }; class DocPositionList : public QList { public: DocPositionList(); QDomElement domElement( QDomDocument& ); DocPositionBase *positionFromId( int id ); QString posNumber( DocPositionBase* ); Geld nettoPrice(); Geld bruttoPrice( double fullTax, double reducedTax ); Geld taxSum(double fullTax, double redTax ); Geld fullTaxSum( double fullTax ); Geld reducedTaxSum( double reducedTax ); protected: int compareItems ( DocPosition *dp1, DocPosition *dp2 ); private: QDomElement xmlTextElement( QDomDocument&, const QString& , const QString& ); }; typedef QListIterator DocPositionListIterator; typedef QPointer DocPositionGuardedPtr; #endif kraft-0.97/src/docpostcard.cpp000066400000000000000000000265501410616450300163700ustar00rootroot00000000000000/*************************************************************************** DocPostCard - a postcard version of the document ------------------- begin : Aug 2006 copyright : (C) 2006 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include "docpostcard.h" #include "kraftdoc.h" #include #include #include #include #include #include #define QL1(X) QLatin1String(X) DocPostCard::DocPostCard( QWidget *parent ) :HtmlView( parent ), mMode( Full ), mShowPrices(true) { setStylesheetFile( "docoverview.css" ); setTitle( i18n( "Document Overview" ) ); connect( this, SIGNAL(openUrl(QUrl)), this, SLOT(slotUrlSelected(QUrl)) ); } void DocPostCard::setHeaderData( const QString& type, const QString& date, const QString& address, const QString& id, const QString& pretext ) { mType = type; mDate = date; mAddress = address; mPreText = htmlify( pretext ); mId = id; } QString DocPostCard::htmlify( const QString& str ) const { QStringList li = Qt::escape(str).split( "\n" ); return QL1("

            ") + li.join( "

            " ) + QL1("

            "); } #define REDUCED_TAX_MARK "²" #define NO_TAX_MARK "¹" void DocPostCard::setPositions( DocPositionList posList, DocPositionBase::TaxType taxType, double tax, double reducedTax ) { mPositions = "
            "; DocPositionListIterator it(posList); while( it.hasNext() ) { DocPositionBase *dpb = it.next(); DocPosition *dp = static_cast(dpb); mPositions += ""; mPositions += ""; if( mShowPrices ) { mPositions += ""; mPositions += ""; } mPositions += ""; } mPositions += "
            "; if ( dp->toDelete() ) mPositions += ""; mPositions += posList.posNumber( dpb ) + ". "; if ( dp->toDelete() ) mPositions += ""; mPositions += ""; if ( dp->toDelete() ) mPositions += ""; if ( dp->attributes().contains( DocPosition::Kind ) ) { mPositions += "" + dp->text() + ""; } else { mPositions += htmlify(dp->text()); } if ( dp->toDelete() ) mPositions += ""; mPositions += ""; if ( dp->toDelete() ) mPositions += ""; mPositions += dp->overallPrice().toHtmlString(); if ( dp->toDelete() ) mPositions += ""; mPositions += ""; if( taxType == DocPositionBase::TaxIndividual && (dp->taxType() == DocPositionBase::TaxReduced) ) { if ( dp->toDelete() ) mPositions += ""; mPositions += QString(REDUCED_TAX_MARK); if ( dp->toDelete() ) mPositions += ""; } if( taxType == DocPositionBase::TaxIndividual && (dp->taxType() == DocPositionBase::TaxNone) ) { if ( dp->toDelete() ) mPositions += ""; mPositions += QString(NO_TAX_MARK); if ( dp->toDelete() ) mPositions += ""; } mPositions += "
            "; // Create the sum table mPositionCount = posList.count(); if( mShowPrices ) { mPositions += "
            "; mPositionCount = posList.count(); mTotal = posList.nettoPrice().toHtmlString(); QString brutto = posList.bruttoPrice( tax, reducedTax ).toHtmlString(); mPositions += QString( "" ); if ( taxType != DocPositionBase::TaxInvalid && taxType != DocPositionBase::TaxNone ) { mPositions += QString( "" ).arg( mTotal ); QString curTax; curTax.setNum( tax, 'f', 1 ); QString taxStr; if( taxType == DocPositionBase::TaxReduced || taxType == DocPositionBase::TaxIndividual ) { curTax.setNum( reducedTax, 'f', 1 ); taxStr = posList.reducedTaxSum( reducedTax ).toHtmlString(); mPositions += QString( "" ).arg( taxStr ).arg(REDUCED_TAX_MARK); } if( taxType == DocPositionBase::TaxFull || taxType == DocPositionBase::TaxIndividual ) { curTax.setNum( tax, 'f', 1 ); taxStr = posList.fullTaxSum( tax ).toHtmlString(); mPositions += QString( "" ).arg( taxStr ); } if( taxType == DocPositionBase::TaxIndividual ) { taxStr = posList.taxSum( tax, reducedTax ).toHtmlString(); mPositions += QString( "" ).arg( taxStr ); } } mPositions += QString( "" ).arg( brutto ); } // showPrices mPositions += "
            ______________________________
            " ) + i18n( "Netto:" )+ QString( "%1
            " ); mPositions += i18n( "+ %1% Tax:", curTax ) + QString( "%1%2
            " ) + i18n( "+ %1% Tax:", curTax ) + QString( "%1
            " ) + i18n( "Sum Tax:" ) + QString( "%1
            " ) + i18n( "Total:" )+ QString( "%1
            "; // qDebug() << "Positions-HTML: " << mPositions << endl; } void DocPostCard::setFooterData( const QString& postText, const QString& goodbye ) { mPostText = htmlify( postText ); mGoodbye = goodbye; } void DocPostCard::renderDoc( int id ) { QString t; // qDebug() << "rendering postcard for active id " << id << //( mMode == Full ? " (full) " : " (mini) " ) << endl; if ( mMode == Full ) { t = renderDocFull( id ); } else if ( mMode == Mini ) { t = renderDocMini( id ); } else { // qDebug () << "Unknown postcard mode" << endl; } // qDebug() << t << endl; displayContent( t ); } #define SEL_STRING(X) ( id == X ? QL1("_selected"): QL1("")) QString DocPostCard::renderDocFull( int id ) { QString rethtml; QString t; rethtml = QL1( "" ); t += QL1(""); t += QString( "
            \n" ).arg( SEL_STRING(KraftDoc::Header) ); t += header( id == KraftDoc::Header, "headerlink", KraftDoc::partToString(KraftDoc::Header), "kraftdoc://header" ); QString h = mAddress; h.replace( '\n', "
            " ); t += ""; t += "
            "; t += QString( "%1\n" ).arg( h ); t += ""; t += QString( "%1
            %2\n" ).arg( mType ).arg( mDate ); t += "
            "; t += "

            " + mPreText + "

            \n"; t += "
            \n"; rethtml += t; // the Body section showing the positions t = QL1(""); t += QString( "
            \n" ).arg( SEL_STRING(KraftDoc::Positions ) ); t += header( id == KraftDoc::Positions, "bodylink", KraftDoc::partToString(KraftDoc::Positions), "kraftdoc://positions" ); t += mPositions; t += "\n
            \n"; rethtml += t; t = QL1(""); t += QString( "
            \n" ).arg( SEL_STRING(KraftDoc::Footer) ); t += header( id == KraftDoc::Footer, "footerlink", KraftDoc::partToString(KraftDoc::Footer), "kraftdoc://footer" ); t += "

            " + mPostText + "

            \n"; if ( ! mGoodbye.isEmpty() ) t += "

            " + mGoodbye + "

            \n"; t += "
            \n"; rethtml += t + ""; return rethtml; } QString DocPostCard::renderDocMini( int id ) const { QString t; QString rethtml = QL1( "" ); t = QString( "
            \n" ).arg( SEL_STRING(KraftDoc::Header) ); t += header( id == KraftDoc::Header, "headerlink", KraftDoc::partToString(KraftDoc::Header), "kraftdoc://header", QString( "%1, %2" ).arg( mType ).arg( mDate ) ); t += QL1("
            "); rethtml += t; t = QString( "
            \n" ).arg( SEL_STRING(KraftDoc::Positions)); QString d = i18n("%1 Items", mPositionCount); if( mShowPrices ) d = i18n("%1 Items, netto %2", mPositionCount, mTotal); // do not add another "Items" string to the header to not bloat t += header( id == KraftDoc::Positions, "bodylink", QString(), "kraftdoc://positions", d ); t += QL1("
            "); rethtml += t; t = QString( "
            \n" ).arg(SEL_STRING(KraftDoc::Footer)); t += header( id == KraftDoc::Footer, "footerlink", KraftDoc::partToString(KraftDoc::Footer), "kraftdoc://footer" ); t += QL1("
            "); rethtml += t; rethtml += QL1(""); return rethtml; } QString DocPostCard::header( bool selected, const QString& styleName, const QString& displayName, const QString& protocol, const QString& addons ) const { const QString content = QString("

            %2  %3

            ") .arg( styleName + (selected ? QL1("_selected") : QL1(""))) .arg(displayName).arg(addons); // These colors do the frame around the header boxes QString bgCol("#aaaaaa"); if( !selected ) bgCol = QL1("#cccccc"); return QString( "" "" "
            < a href=\"%2\">%3
            ").arg(bgCol).arg(protocol).arg(content); } void DocPostCard::slotUrlSelected( const QUrl& kurl) { KraftDoc::Part id = KraftDoc::Header; if ( kurl.scheme() == "kraftdoc" ) { if ( kurl.host() == "header" ) { // qDebug () << "Header selected!" << endl; id = KraftDoc::Header; } else if ( kurl.host() == "positions" ) { // qDebug () << "Positions selected!" << endl; id = KraftDoc::Positions; } else if ( kurl.host() == "footer" ) { // qDebug () << "Footer selected!" << endl; id = KraftDoc::Footer; } emit selectPage( id ); } } void DocPostCard::slotSetMode( DisplayMode mode, int id ) { mMode = mode; renderDoc( id ); } void DocPostCard::slotShowPrices( bool showIt ) { mShowPrices = showIt; } kraft-0.97/src/docpostcard.h000066400000000000000000000043541410616450300160330ustar00rootroot00000000000000/*************************************************************************** DocPostCard - a postcard version of the document ------------------- begin : Aug 2006 copyright : (C) 2006 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef DOCPOSTCARD_H #define DOCPOSTCARD_H #include #include "htmlview.h" #include "kraftdoc.h" class QUrl; class DocPostCard : public HtmlView { Q_OBJECT public: enum DisplayMode { Full, Mini }; DocPostCard( QWidget *parent = 0 ); signals: void selectPage( int ); public slots: void setHeaderData( const QString&, const QString&, const QString&, const QString&, const QString& ); void setPositions( DocPositionList, DocPositionBase::TaxType, double, double ); void setFooterData( const QString&, const QString& ); void renderDoc( int id = -1 ); void slotSetMode( DisplayMode, int id = -1 ); void slotShowPrices( bool showIt ); protected: QString renderDocMini( int ) const; QString renderDocFull( int ); QString header(bool, const QString&, const QString&, const QString& protocol, const QString& = QString() ) const; private slots: void slotUrlSelected( const QUrl& kurl); private: QString htmlify( const QString& ) const; DocGuardedPtr mDoc; QString mType; QString mId; QString mPreText; QString mPostText; QString mDate; QString mAddress; QString mPositions; QString mGoodbye; QString mTotal; int mPositionCount; DisplayMode mMode; bool mShowPrices; }; #endif kraft-0.97/src/doctext.cpp000066400000000000000000000051601410616450300155270ustar00rootroot00000000000000/*************************************************************************** doctext.cpp - texts like header or footer for documents ------------------- begin : March 2007 copyright : (C) 2007 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include #include #include #include #include "doctext.h" DocText::DocText() : mTextType( KraftDoc::Unknown ), mCurrentItem( 0 ) { } void DocText::setName( const QString& t ) { mName = t; } void DocText::setText( const QString& t ) { mText = t; } void DocText::setDescription( const QString& t ) { mDescription = t; } void DocText::setDocType( const QString& t ) { mDocType = t; } void DocText::setTextType( KraftDoc::Part t ) { mTextType = t; } bool DocText::isStandardText() const { return QString::compare(mName, i18n( "Standard" ), Qt::CaseInsensitive) == 0; // can surely be improved... } KraftDoc::Part DocText::stringToTextType( const QString& str ) { KraftDoc::Part tt = KraftDoc::Unknown; if ( str == textTypeToString( KraftDoc::Header ) ) tt = KraftDoc::Header; if ( str == textTypeToString( KraftDoc::Footer ) ) tt = KraftDoc::Footer; if ( str == textTypeToString( KraftDoc::Positions ) ) tt = KraftDoc::Positions; return tt; } QString DocText::textTypeToString( KraftDoc::Part tt ) { if ( tt == KraftDoc::Header ) return i18n( "Header Text" ); if ( tt == KraftDoc::Footer ) return i18n( "Footer Text" ); if ( tt == KraftDoc::Positions ) return i18n( "Items" ); return i18n( "Unknown" ); } bool DocText::operator==( const DocText& _dt ) const { return ( ( mName == _dt.mName ) && ( mDocType == _dt.mDocType ) && ( mTextType == _dt.mTextType ) ); } void DocText::setDbId( long id ) { mDbId = id ; } void DocText::setDbId( const dbID& id ) { mDbId = id ; } dbID DocText::dbId() const { return mDbId; } kraft-0.97/src/doctext.h000066400000000000000000000046101410616450300151730ustar00rootroot00000000000000/*************************************************************************** doctext.h - texts like header or footer for documents ------------------- begin : March 2007 copyright : (C) 2007 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef DOCTEXT_H #define DOCTEXT_H #include #include "kraftcat_export.h" #include "kraftdoc.h" #include "dbids.h" class QTreeWidgetItem; class QPixmap; class KRAFTCAT_EXPORT DocText { public: DocText(); QString name() const { return mName; } void setName( const QString& ); QString text() const { return mText; } void setText( const QString& ); QString description() const { return mDescription; } void setDescription( const QString& ); KraftDoc::Part type() { return mTextType; } QString textTypeString() const { return textTypeToString( mTextType ); } bool isStandardText() const; void setTextType( KraftDoc::Part ); KraftDoc::Part textType() const { return mTextType; } QString docType() const { return mDocType; } void setDocType( const QString& ); QTreeWidgetItem *listViewItem() const { return mCurrentItem; } void setListViewItem( QTreeWidgetItem *item ) { mCurrentItem = item; } void setDbId( long ); void setDbId( const dbID& ); dbID dbId() const; static KraftDoc::Part stringToTextType( const QString& ); static QString textTypeToString( KraftDoc::Part ); bool operator==( const DocText& ) const; private: QString mName; QString mText; QString mDescription; QString mDocType; KraftDoc::Part mTextType; QTreeWidgetItem *mCurrentItem; dbID mDbId; }; typedef QList DocTextList; #endif kraft-0.97/src/doctype.cpp000066400000000000000000000370521410616450300155310ustar00rootroot00000000000000/*************************************************************************** doctype.cpp - doc type class ------------------- begin : Oct. 2007 copyright : (C) 2007 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ // include files for Qt #include #include // application specific includes #include "doctype.h" #include "kraftdb.h" #include "numbercycle.h" #include "attribute.h" /** @author Klaas Freitag */ idMap DocType::mNameMap = idMap(); DocType::DocType() : mAttributes( QStringLiteral( "DocType" ) ), mDirty( false ) { init(); } DocType::DocType( const QString& name, bool dirty ) : mAttributes( QStringLiteral( "DocType" ) ), mName( name ), mDirty( dirty ) { init(); if ( mNameMap.contains( name ) ) { dbID id = mNameMap[ name ]; mAttributes.load( id ); } readFollowerList(); readIdentTemplate(); } void DocType::init() { // === Start to fill static content if ( ! mNameMap.empty() ) return; QSqlQuery q; q.prepare( "SELECT docTypeID, name FROM DocTypes ORDER BY name" ); q.exec(); while ( q.next() ) { dbID id( q.value(0).toInt() ); QString name = q.value(1).toString(); mNameMap[ name ] = id; // QString h = DefaultProvider::self()->locale()->translate( cur.value( "name" ).toString() ); } } void DocType::clearMap() { mNameMap.clear(); } QStringList DocType::all() { init(); QStringList re; QSqlQuery q; q.prepare( "SELECT docTypeID, name FROM DocTypes ORDER BY name" ); q.exec(); while ( q.next() ) { re << q.value(1).toString(); } return re; } QStringList DocType::allLocalised() { return all(); } // static function to retrieve id of a certain doctype dbID DocType::docTypeId( const QString& docType ) { dbID id; init(); if ( mNameMap.contains( docType ) ) { id = mNameMap[ docType ]; return id; } else { qCritical()<< "Can not find id for doctype named " << docType; } return id; } bool DocType::allowDemand() { bool re = false; if ( mAttributes.contains( "AllowDemand" ) ) { re = true; } return re; } bool DocType::allowAlternative() { bool re = false; if ( mAttributes.contains( "AllowAlternative" ) ) { re = true; } return re; } bool DocType::pricesVisible() { bool re = true; if( mAttributes.contains("HidePrices")) { re = false; } return re; } bool DocType::substractPartialInvoice() { bool re = false; if( mAttributes.contains("SubstractPartialInvoice")) { re = true; } return re; } bool DocType::partialInvoice() { bool re = false; if( mAttributes.contains("PartialInvoice")) { re = true; } return re; } // returns the amount of followers added int DocType::setAllFollowers( const QStringList& followers) { QSqlQuery q; q.prepare("INSERT INTO DocTypeRelations (typeId, followerId, sequence) VALUES (:typeId, :followerId, 0)"); QSqlQuery qu; qu.prepare("UPDATE DocTypeRelations SET sequence=:seq WHERE typeId=:typeId AND followerId=:followerId"); // get "my" doc type Id int typeId = mNameMap[mName].toInt(); q.bindValue(":typeId", typeId); qu.bindValue(":typeId", typeId); // get the max sequence for me int seq = 0; { QSqlQuery cq; cq.prepare("SELECT MAX(sequence) FROM DocTypeRelations WHERE typeId=:tdId"); cq.bindValue(":tdId", typeId); cq.exec(); if( cq.next() ) { seq = cq.value(0).toInt(); } } const QStringList existingFollowers = follower(); int cnt = 0; // simple counter to return. for( const QString& f : followers ) { if( mNameMap.contains(f) ) { int followerId = mNameMap[f].toInt(); if( !existingFollowers.contains(f) ) { q.bindValue(":followerId", followerId ); q.exec(); cnt++; } // use the updater qu.bindValue(":seq", ++seq); qu.bindValue(":followerId", followerId); qu.exec(); } } return cnt; } QStringList DocType::follower() { return mFollowerList; } void DocType::readFollowerList() { QSqlQuery q; q.prepare( "SELECT typeId, followerId, sequence FROM DocTypeRelations WHERE typeId=:type ORDER BY sequence"); q.bindValue( ":type", mNameMap[mName].toInt() ); q.exec(); while ( q.next() ) { dbID followerId( q.value(1).toInt() ); idMap::Iterator it; for ( it = mNameMap.begin(); it != mNameMap.end(); ++it ) { if ( it.value() == followerId ) { mFollowerList << it.key(); } } } } QString DocType::numberCycleName() { QString re = NumberCycle::defaultName(); if ( mAttributes.hasAttribute( "identNumberCycle" ) ) { re = mAttributes["identNumberCycle"].value().toString(); } return re; } void DocType::setNumberCycleName( const QString& name ) { if ( name.isEmpty() ) return; if ( name != NumberCycle::defaultName() ) { Attribute att( "identNumberCycle" ); att.setPersistant( true ); att.setValue( name ); mAttributes["identNumberCycle"] = att; } else { // remove default value from map mAttributes.markDelete( "identNumberCycle" ); // qDebug () << "Removing identNumberCycle Attribute"; } mDirty = true; readIdentTemplate(); } QString DocType::templateFile( ) { QString tmplFile; QString reportFileName = QString( "%1.trml").arg( name().toLower() ); reportFileName.replace(QChar(' '), QChar('_')); if ( mAttributes.hasAttribute( "docTemplateFile" ) ) { tmplFile = mAttributes["docTemplateFile"].value().toString(); if( !tmplFile.isEmpty() ) { QFileInfo fi(tmplFile); if( fi.isAbsolute() ) { return tmplFile; } else { // it is not an absolute file name, try to find it reportFileName = tmplFile; } tmplFile.clear(); } } // Try to find it from the installation QStringList searchList; searchList << QString("kraft/reports/%1").arg(reportFileName); searchList << QLatin1String("kraft/reports/invoice.trml"); const QString prjPath = QString::fromUtf8(qgetenv( "KRAFT_HOME" )); if( tmplFile.isEmpty() && !prjPath.isEmpty() ) { foreach( QString searchPath, searchList ) { if( searchPath.startsWith(QLatin1String("kraft"))) { // remove the kraft-String here. searchPath.remove(0, 5); // remove "kraft" } const QString tFile = prjPath + searchPath; if( !tFile.isEmpty() && QFile::exists(tFile) ) { // qDebug () << "Found template file " << tFile; tmplFile = tFile; break; } } } if( tmplFile.isEmpty() ) { foreach( QString searchPath, searchList ) { const QString tFile = QStandardPaths::locate(QStandardPaths::GenericDataLocation, searchPath); if( !tFile.isEmpty() && tFile != searchPath && QFile::exists( tFile )) { tmplFile = tFile; // qDebug () << "Found template file " << tmplFile; break; } } } if( tmplFile.isEmpty() ) { qDebug () << "unable to find a template file for " << name(); } else { qDebug () << "Found template file " << tmplFile; } return tmplFile; } QString DocType::defaultTemplateFile() const { // first check for a country specific file QLocale locale; const QString country = locale.bcp47Name(); QString findFile = QString("kraft/reports/%1/invoice.trml").arg(country); QString re = QStandardPaths::locate(QStandardPaths::GenericDataLocation, findFile); if( re.isEmpty() ) { // No lang specific one. findFile = "kraft/reports/invoice.trml"; re = QStandardPaths::locate(QStandardPaths::GenericDataLocation, findFile); } return re; } void DocType::setTemplateFile( const QString& name ) { if ( name.isEmpty() || name == defaultTemplateFile() ) { // the default is returned anyway. // remove default value from map mAttributes.markDelete( "docTemplateFile" ); // qDebug () << "Removing docTemplateFile Attribute"; } else { Attribute att( "docTemplateFile" ); att.setPersistant( true ); att.setValue( name ); mAttributes["docTemplateFile"] = att; } mDirty = true; } QString DocType::mergeIdent() { QString re = "0"; if ( mAttributes.hasAttribute( "docMergeIdent" ) ) { re = mAttributes["docMergeIdent"].value().toString(); } return re; } void DocType::setMergeIdent( const QString& ident ) { if ( !ident.isEmpty() ) { Attribute att( "docMergeIdent" ); att.setPersistant( true ); att.setValue( ident ); mAttributes["docMergeIdent"] = att; } else { // remove default value from map mAttributes.markDelete( "docMergeIdent" ); // qDebug () << "Removing docMergeIdent Attribute"; } mDirty = true; } void DocType::setAttribute( const QString& attribute, const QString& val) { if ( !(attribute.isEmpty() || val.isEmpty()) ) { Attribute att( attribute ); att.setPersistant( true ); att.setValue( val); mAttributes[attribute] = att; mDirty = true; } } QString DocType::watermarkFile() { QString re; if ( mAttributes.hasAttribute( "watermarkFile" ) ) { re = mAttributes["watermarkFile"].value().toString(); } return re; } void DocType::setWatermarkFile( const QString& file ) { if ( !file.isEmpty() ) { Attribute att( "watermarkFile" ); att.setPersistant( true ); att.setValue( file ); mAttributes["watermarkFile"] = att; } else { // remove default value from map mAttributes.markDelete( "watermarkFile" ); // qDebug () << "Removing docMergeFile Attribute"; } mDirty = true; } QString DocType::generateDocumentIdent( const QDate& docDate, const QString& docType, const QString& addressUid, int id ) { /* * The pattern may contain the following tags: * %y - the year of the documents date. * %w - the week number of the documents date * %d - the day number of the documents date * %m - the month number of the documents date * %c - the customer id from kaddressbook * %i - the uniq identifier from db. * %type - the localised doc type (offer, invoice etc.) * %uid - the customer uid */ QString pattern = identTemplate(); if ( pattern.indexOf( "%i" ) == -1 ) { qWarning() << "No %i found in identTemplate, appending it to meet law needs!"; pattern += "-%i"; } KraftDB::StringMap m; m[ "%yyyy" ] = docDate.toString( "yyyy" ); m[ "%yy" ] = docDate.toString( "yy" ); m[ "%y" ] = docDate.toString( "yyyy" ); QString h; h = QString("%1").arg( docDate.weekNumber(), 2, 10, QChar('0') ); m[ "%ww" ] = h; m[ "%w" ] = QString::number( docDate.weekNumber( ) ); m[ "%dd" ] = docDate.toString( "dd" ); m[ "%d" ] = docDate.toString( "d" ); m[ "%m" ] = QString::number( docDate.month() ); m[ "%MM" ] = docDate.toString( "MM" ); m[ "%M" ] = docDate.toString( "M" ); int i = id; if ( id == -1 ) { // hot mode: The database id is incremented by nextIdentId() i = nextIdentId(); } h = QString("%1").arg(i, 6, 10, QChar('0') ); m[ "%iiiiii" ] = h; h = QString("%1").arg(i, 5, 10, QChar('0') ); m[ "%iiiii" ] = h; h = QString("%1").arg(i, 4, 10, QChar('0') ); m[ "%iiii" ] = h; h = QString("%1").arg(i, 3, 10, QChar('0') ); m[ "%iii" ] = h; h = QString("%1").arg(i, 2, 10, QChar('0') ); m[ "%ii" ] = h; m[ "%i" ] = QString::number( i ); m[ "%c" ] = addressUid; m[ "%type" ] = docType; m[ "%uid" ] = addressUid; QString re = KraftDB::self()->replaceTagsInWord( pattern, m ); // qDebug () << "Generated document ident: " << re; return re; } // if hot, the id is updated in the database, otherwise not. int DocType::nextIdentId( bool hot ) { QString numberCycle = numberCycleName(); if ( numberCycle.isEmpty() ) { qCritical() << "NumberCycle name is empty"; return -1; } QSqlQuery qLock; if ( hot ) { qLock.exec( "LOCK TABLES numberCycles WRITE" ); } QSqlQuery q; q.prepare( "SELECT lastIdentNumber FROM numberCycles WHERE name=:name" ); int num = -1; q.bindValue( ":name", numberCycle ); q.exec(); if ( q.next() ) { num = 1+( q.value( 0 ).toInt() ); // qDebug () << "Got current number: " << num; if ( hot ) { QSqlQuery setQuery; setQuery.prepare( "UPDATE numberCycles SET lastIdentNumber=:newNumber WHERE name=:name" ); setQuery.bindValue( ":name", numberCycle ); setQuery.bindValue( ":newNumber", num ); setQuery.exec(); if ( setQuery.isActive() ) { // qDebug () << "Successfully created new id number for numbercycle " << numberCycle << ": " << num << endl; } } } if ( hot ) { qLock.exec( "UNLOCK TABLES" ); } return num; } QString DocType::identTemplate() { return mIdentTemplate; } void DocType::setIdentTemplate( const QString& t ) { mIdentTemplate = t; } void DocType::readIdentTemplate() { QSqlQuery q; QString tmpl; const QString defaultTempl = QString::fromLatin1( "%y%ww-%i" ); QString numberCycle = numberCycleName(); if ( numberCycle.isEmpty() ) { qCritical() << "Numbercycle for doctype is empty, returning default"; mIdentTemplate = defaultTempl; } // qDebug () << "Picking ident Template for numberCycle " << numberCycle; q.prepare( "SELECT identTemplate FROM numberCycles WHERE name=:name" ); q.bindValue( ":name", numberCycle ); q.exec(); if ( q.next() ) { tmpl = q.value( 0 ).toString(); // qDebug () << "Read ident template from database: " << tmpl; } // FIXME: Check again. if ( tmpl.isEmpty() ) { // qDebug () << "Writing ident template to database: " << pattern; QSqlQuery insQuery; insQuery.prepare( "UPDATE numberCycles SET identTemplate=:pattern WHERE name=:name" ); insQuery.bindValue( ":name", numberCycle ); insQuery.bindValue( ":pattern", defaultTempl); insQuery.exec(); tmpl = defaultTempl; } mIdentTemplate = tmpl; } QString DocType::name() const { return mName; } void DocType::setName( const QString& name ) { QString oldName = mName; dbID id = mNameMap[ oldName ]; // The old id. mNameMap[ name ] = id; mNameMap.remove( oldName ); mName = name; mDirty = true; } /* * Saves the name and the attriutes (numbercycle, demand, etc.) */ void DocType::save() { if ( !mDirty ) { // qDebug () << "Saving: not DIRTY!"; return; } if ( !mNameMap.contains( mName ) ) { qCritical() << "nameMap does not contain id for " << mName; return; } dbID id = mNameMap[ mName ]; QSqlQuery q; bool doInsert = false; if ( id.isOk() ) { q.prepare( "UPDATE DocTypes SET name=:name WHERE docTypeId=:id" ); q.bindValue( ":id", id.toInt() ); } else { q.prepare( "INSERT INTO DocTypes (name) VALUES (:name)" ); doInsert = true; } q.bindValue( ":name", mName ); q.exec(); if ( doInsert ) { mNameMap[mName] = KraftDB::self()->getLastInsertID(); } mAttributes.save( mNameMap[mName] ); } kraft-0.97/src/doctype.h000066400000000000000000000055331410616450300151750ustar00rootroot00000000000000/*************************************************************************** doctype.h - doc type class ------------------- begin : Oct. 2007 copyright : (C) 2007 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef DOCTYPE_H #define DOCTYPE_H // include files for Qt #include #include #include "kraftcat_export.h" #include "dbids.h" #include "attribute.h" /** @author Klaas Freitag */ typedef QMap idMap; class KRAFTCAT_EXPORT DocType { public: DocType(); /** * create a doctype from its localised or tech name */ DocType( const QString&, bool dirty = false ); static QStringList all(); static QStringList allLocalised(); static dbID docTypeId( const QString& ); QString name() const; void setName( const QString& ); bool allowDemand(); bool allowAlternative(); bool pricesVisible(); bool partialInvoice(); bool substractPartialInvoice(); QStringList follower(); int setAllFollowers( const QStringList& followers); QString generateDocumentIdent( const QDate& docDate, const QString& docType, const QString& addressUid, int id = -1 ); QString identTemplate(); void setIdentTemplate( const QString& ); QString numberCycleName(); void setNumberCycleName( const QString& ); QString defaultTemplateFile() const; QString templateFile(); void setTemplateFile( const QString& ); QString watermarkFile(); void setWatermarkFile( const QString& ); QString mergeIdent(); void setMergeIdent( const QString& ); void setAttribute( const QString& attribute, const QString& val); static void clearMap(); int nextIdentId( bool hot = true ); void save(); void readIdentTemplate(); protected: void readFollowerList(); private: static void init(); private: AttributeMap mAttributes; QStringList mFollowerList; QString mName; QString mIdentTemplate; bool mDirty; QString mMergeIdent; static idMap mNameMap; }; #endif kraft-0.97/src/doctypeedit.cpp000066400000000000000000000372441410616450300164020ustar00rootroot00000000000000/*************************************************************************** doctypeedit.h - the document type editor ------------------- begin : Fri Jan 2 2009 copyright : (C) 2009 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "prefsdialog.h" #include "kraftsettings.h" #include "kraftdb.h" #include "kraftdoc.h" #include "defaultprovider.h" #include "doctype.h" #include "doctypeedit.h" #include "numbercycledialog.h" // -------------------------------------------------------------------------------- DocTypeEdit::DocTypeEdit( QWidget *parent ) : QWidget(parent), Ui::DocTypeEditBase( ), mExampleDocType(i18n("")), mExampleAddressUid(i18n("
            ")) { setupUi( this ); connect( mTypeListBox, SIGNAL( currentTextChanged( const QString& ) ), this, SLOT( slotDocTypeSelected( const QString& ) ) ); QStringList types = DocType::allLocalised(); mTypeListBox->clear(); mTypeListBox->addItems( types ); for ( QStringList::Iterator it = types.begin(); it != types.end(); ++it ) { DocType dt( *it ); mOrigDocTypes[*it] = dt; } mTypeListBox->setCurrentRow( 0, QItemSelectionModel::Select ); QString dtype; if(mTypeListBox->currentRow() != -1) dtype = mTypeListBox->currentItem()->text(); mPbAdd->setIcon( QIcon::fromTheme( "list-add" ) ); mPbEdit->setIcon( QIcon::fromTheme( "document-edit" ) ); mPbRemove->setIcon( QIcon::fromTheme( "list-remove" ) ); connect( mPbAdd, SIGNAL( clicked() ), SLOT( slotAddDocType() ) ); connect( mPbEdit, SIGNAL( clicked() ), SLOT( slotEditDocType() ) ); connect( mPbRemove, SIGNAL( clicked() ), SLOT( slotRemoveDocType() ) ); connect( mNumberCycleCombo, SIGNAL( activated( const QString& ) ), SLOT( slotNumberCycleChanged( const QString& ) ) ); connect( mPbEditCycles, SIGNAL( clicked() ), SLOT( slotEditNumberCycles() ) ); connect( mWatermarkCombo, SIGNAL( activated( int ) ), SLOT( slotWatermarkModeChanged( int ) ) ); connect( mWatermarkUrl, SIGNAL( textChanged( const QString& ) ), SLOT( slotWatermarkUrlChanged( const QString& ) ) ); connect( mTemplateUrl, SIGNAL( textChanged( const QString& ) ), SLOT( slotTemplateUrlChanged( const QString& ) ) ); fillNumberCycleCombo(); DocType dt( dtype ); mNumberCycleCombo->setCurrentIndex(mNumberCycleCombo->findText( dt.numberCycleName() )); #if 0 mTemplateUrl->setFilter( "*.trml" ); mWatermarkUrl->setFilter( "*.pdf" ); mTemplateUrl->setUrl( dt.templateFile() ); mWatermarkUrl->setUrl( dt.watermarkFile() ); #endif int newMode = dt.mergeIdent().toInt(); mWatermarkCombo->setCurrentIndex( newMode ); bool state = true; if ( newMode == 0 ) state = false; mWatermarkUrl->setEnabled( state ); } void DocTypeEdit::fillNumberCycleCombo() { QSqlQuery q; q.prepare( "SELECT name FROM numberCycles ORDER BY name" ); q.exec(); QStringList cycles; while ( q.next() ) { cycles << q.value( 0 ).toString(); } mNumberCycleCombo->clear(); mNumberCycleCombo->insertItems(-1, cycles ); } void DocTypeEdit::slotAddDocType() { // qDebug () << "Adding a doctype!"; QString newName = QInputDialog::getText( this, i18n( "Add Document Type" ), i18n( "Enter the name of a new document type" ) ); if ( newName.isEmpty() ) return; // qDebug () << "New Name to add: " << newName; if ( mTypeListBox->findItems(newName, Qt::MatchExactly).count() > 0 ) { // qDebug () << "New Name already exists"; } else { mTypeListBox->addItem( newName ); DocType newDt( newName, true ); mOrigDocTypes[newName] = newDt; mChangedDocTypes[newName] = newDt; // Check again! mAddedTypes.append( newName ); } } void DocTypeEdit::slotEditDocType() { // qDebug () << "Editing a doctype!"; QString currName = mTypeListBox->currentItem()->text(); if ( currName.isEmpty() ) return; QString newName = QInputDialog::getText( this, i18n( "Add Document Type" ), i18n( "Edit the name of a document type" ), QLineEdit::Normal, currName ); if ( newName.isEmpty() ) return; // qDebug () << "edit: " << currName << " became " << newName; if ( newName != currName ) { mTypeListBox->currentItem()->setText(newName); /* check if the word that was changed now was already changed before. */ bool prechanged = false; bool skipEntry = false; QMap::Iterator it; for ( it = mTypeNameChanges.begin(); !prechanged && it != mTypeNameChanges.end(); ++it ) { if (it.key() == currName ) { // it was changed back to an original name. mTypeNameChanges.erase( it ); skipEntry = true; } if ( !skipEntry && it.value() == currName ) { // qDebug () << "Was changed before, key is " << it.key(); currName = it.key(); prechanged = true; } } if ( ! skipEntry ) { mTypeNameChanges[currName] = newName; DocType dt( currName ); if ( mChangedDocTypes.contains( currName ) ) { dt = mChangedDocTypes[currName]; } dt.setName( newName ); mChangedDocTypes[newName] = dt; } } } void DocTypeEdit::slotRemoveDocType() { // qDebug () << "Removing a doctype!"; QListWidgetItem *currItem = mTypeListBox->currentItem(); if ( !currItem || currItem->text().isEmpty() ) { // qDebug () << "No current Item, return"; return; } QString currName = currItem->text(); if ( mAddedTypes.indexOf( currName ) != -1 ) { // remove item from recently added list. mChangedDocTypes.remove( currName ); mAddedTypes.removeAll( currName ); mOrigDocTypes.remove( currName ); } else { QString toRemove = currName; QMap::Iterator it; for ( it = mTypeNameChanges.begin(); it != mTypeNameChanges.end(); ++it ) { if ( currName == it.value() ) { // remove the original name toRemove = it.key(); // the original name } } mRemovedTypes.append( toRemove ); } delete currItem; // qDebug () << "removed type: " << mRemovedTypes; emit removedType( currName ); } void DocTypeEdit::slotDocTypeSelected( const QString& newValue ) { // qDebug () << "docTypeSelected: " << newValue << " and previous: " << mPreviousType; DocType dt( newValue ); if ( mChangedDocTypes.contains( newValue ) ) { dt = mChangedDocTypes[newValue]; // qDebug () << "new docType taken from ChangedDocTypes: "; } // store the previous type DocType prevType = mOrigDocTypes[mPreviousType]; if ( mChangedDocTypes.contains( mPreviousType ) ) { prevType = mChangedDocTypes[mPreviousType]; // qDebug () << "previous docType taken from ChangedDocTypes: "; } prevType.setNumberCycleName( mNumberCycleCombo->currentText() ); prevType.setTemplateFile( mTemplateUrl->text() ); prevType.setWatermarkFile( mWatermarkUrl->text() ); prevType.setMergeIdent( QString::number( mWatermarkCombo->currentIndex() ) ); mChangedDocTypes[mPreviousType] = prevType; // dt.setNumberCycleName( dt.numberCycleName() ); // qDebug () << "Selected doc type " << newValue; mIdent->setText( dt.identTemplate() ); int nextNum = dt.nextIdentId( false )-1; mCounter->setText( QString::number( nextNum ) ); mNumberCycleCombo->setCurrentIndex(mNumberCycleCombo->findText( dt.numberCycleName() )); // mHeader->setText( i18n( "Details for %1:", dt.name() ) ); mExampleId->setText( dt.generateDocumentIdent( QDate::currentDate(), mExampleDocType, mExampleAddressUid, nextNum ) ); mTemplateUrl->setText( dt.templateFile() ); mWatermarkUrl->setText( dt.watermarkFile() ); int mergeIdent = dt.mergeIdent().toInt(); mWatermarkCombo->setCurrentIndex( mergeIdent ); mWatermarkUrl->setEnabled( mergeIdent > 0 ); mPreviousType = newValue; } void DocTypeEdit::slotEditNumberCycles() { saveDocTypes(); QString currNumbercycle = mNumberCycleCombo->currentText(); NumberCycleDialog dia( this, currNumbercycle ); if ( dia.exec() == QDialog::Accepted ) { fillNumberCycleCombo(); mNumberCycleCombo->setCurrentIndex(mNumberCycleCombo->findText( currNumbercycle )); DocType dt = currentDocType(); dt.readIdentTemplate(); // only the numbercycle has changed - refresh the display mIdent->setText( dt.identTemplate() ); int nextNum = dt.nextIdentId( false )-1; mCounter->setText( QString::number( nextNum ) ); mExampleId->setText( dt.generateDocumentIdent( QDate::currentDate(), mExampleDocType, mExampleAddressUid, nextNum ) ); } } DocType DocTypeEdit::currentDocType() { QString docType = mTypeListBox->currentItem()->text(); DocType dt = mOrigDocTypes[docType]; if ( mChangedDocTypes.contains( docType ) ) { dt = mChangedDocTypes[docType]; } return dt; } void DocTypeEdit::slotWatermarkModeChanged( int newMode ) { DocType dt = currentDocType(); QString newMergeIdent = QString::number( newMode ); if ( newMergeIdent != dt.mergeIdent() ) { dt.setMergeIdent( newMergeIdent ); if ( !mTypeListBox->currentItem()->text().isEmpty() ) { mChangedDocTypes[ mTypeListBox->currentItem()->text() ] = dt; } } bool state = true; if ( newMode == 0 ) state = false; mWatermarkUrl->setEnabled( state ); } void DocTypeEdit::slotTemplateUrlChanged( const QString& newUrl ) { QString docType; if(mTypeListBox->currentRow() != -1) docType = mTypeListBox->currentItem()->text(); if( docType.isEmpty() || ! mOrigDocTypes.contains(docType) ) return; DocType dt = mOrigDocTypes[docType]; if ( mChangedDocTypes.contains( docType ) ) { dt = mChangedDocTypes[docType]; } if ( newUrl != dt.defaultTemplateFile() ) { dt.setTemplateFile( newUrl ); mChangedDocTypes[docType] = dt; } } void DocTypeEdit::slotWatermarkUrlChanged( const QString& newUrl ) { QString docType = mTypeListBox->currentItem()->text(); DocType dt = mOrigDocTypes[docType]; if ( mChangedDocTypes.contains( docType ) ) { dt = mChangedDocTypes[docType]; } if ( newUrl != dt.watermarkFile() ) { dt.setWatermarkFile( newUrl ); mChangedDocTypes[docType] = dt; } } void DocTypeEdit::slotNumberCycleChanged( const QString& newCycle ) { QString docTypeName = mTypeListBox->currentItem()->text(); DocType dt = currentDocType(); dt.setNumberCycleName( newCycle ); mChangedDocTypes[docTypeName] = dt; // qDebug () << "Changing the cycle name of " << docTypeName << " to " << newCycle; mIdent->setText( dt.identTemplate() ); int nextNum = dt.nextIdentId( false )-1; mCounter->setText( QString::number( nextNum ) ); mExampleId->setText( dt.generateDocumentIdent( QDate::currentDate(), mExampleDocType, mExampleAddressUid, nextNum ) ); } QStringList DocTypeEdit::allNumberCycles() { QStringList re; re << NumberCycle::defaultName(); QSqlQuery q( "SELECT av.value FROM attributes a, attributeValues av " "WHERE a.id=av.attributeId AND a.hostObject='DocType' " "AND a.name='identNumberCycle'" ); while ( q.next() ) { QString cycleName = q.value(0).toString(); re << cycleName; } return re; } void DocTypeEdit::saveDocTypes() { // removed doctypes // FIXME: Remove unreferenced number cycles for ( QStringList::Iterator it = mRemovedTypes.begin(); it != mRemovedTypes.end(); ++it ) { if ( mOrigDocTypes.contains( *it ) ) { DocType dt = mOrigDocTypes[*it]; removeTypeFromDb( *it ); mOrigDocTypes.remove( *it ); mChangedDocTypes.remove( *it ); emit removedType( *it ); } } // added doctypes for ( QStringList::Iterator it = mAddedTypes.begin(); it != mAddedTypes.end(); ++it ) { QString name = *it; if ( mOrigDocTypes.contains( name ) ) { // just to check DocType dt = mChangedDocTypes[name]; QString numCycleName = dt.numberCycleName(); // qDebug () << "Number cycle name for to add doctype " << name << ": " << numCycleName; dt.save(); } } // edited doctypes QMap::Iterator it; for ( it = mTypeNameChanges.begin(); it != mTypeNameChanges.end(); ++it ) { QString oldName( it.key() ); if ( mOrigDocTypes.contains( oldName ) ) { QString newName = it.value(); // qDebug () << "Renaming " << oldName << " to " << newName; DocType dt = mOrigDocTypes[oldName]; if ( mChangedDocTypes.contains( newName ) ) { dt = mChangedDocTypes[newName]; } else { dt.setName( newName ); } mOrigDocTypes.remove( oldName ); mOrigDocTypes[newName] = dt; dt.save(); } else { qCritical() << "Can not find doctype to change named " << oldName; } } // check if numberCycles have changed. QMap::Iterator mapit; for ( mapit = mChangedDocTypes.begin(); mapit != mChangedDocTypes.end(); ++mapit ) { DocType dt = mapit.value(); dt.save(); } // now the list of document types should be up to date and reflected into // the database. DocType::clearMap(); } void DocTypeEdit::removeTypeFromDb( const QString& name ) { QSqlQuery delQuery; dbID id = DocType::docTypeId( name ); if ( !id.isOk() ) { // qDebug () << "Can not find doctype " << name << " to remove!"; return; } // delete in DocTypeRelations delQuery.prepare( "DELETE FROM DocTypeRelations WHERE followerId=:id or typeId=:id" ); delQuery.bindValue( ":id", id.toString() ); delQuery.exec(); // delete in DocTexts delQuery.prepare( "DELETE FROM DocTexts WHERE DocTypeId=:id" ); delQuery.bindValue( ":id", id.toString() ); delQuery.exec(); // delete in the DocTypes table delQuery.prepare( "DELETE FROM DocTypes WHERE docTypeId=:id" ); delQuery.bindValue( ":id", id.toString() ); delQuery.exec(); AttributeMap attMap( "DocType" ); attMap.dbDeleteAll( id ); } void DocTypeEdit::renameTypeInDb( const QString& oldName, const QString& newName ) { QSqlQuery q; q.prepare( "UPDATE DocTypes SET name=:newName WHERE docTypeID=:oldId" ); dbID id = DocType::docTypeId( oldName ); if ( id.isOk() ) { q.bindValue( ":newName", newName ); q.bindValue( ":oldId", id.toInt() ); q.exec(); if ( q.numRowsAffected() == 0 ) { qCritical() << "Database update failed for renaming " << oldName << " to " << newName; } else { // qDebug () << "Renamed doctype " << oldName << " to " << newName; } } else { qCritical() << "Could not find the id for doctype named " << oldName; } } kraft-0.97/src/doctypeedit.h000066400000000000000000000050441410616450300160400ustar00rootroot00000000000000/*************************************************************************** doctypeedit.h - the document type editor ------------------- begin : Fri Jan 2 2009 copyright : (C) 2009 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef DOCTYPEEDIT_H #define DOCTYPEEDIT_H #include #include #include #include "doctype.h" #include "ui_doctypeeditbase.h" class QLineEdit; class QLabel; class QPushButton; class QComboBox; class QCheckBox; /** * @author Klaas Freitag */ // ################################################################################ class DocTypeEdit : public QWidget, protected Ui::DocTypeEditBase { Q_OBJECT public: DocTypeEdit( QWidget *parent = 0 ); void saveDocTypes(); public slots: void slotDocTypeSelected( const QString& = QString() ); void slotAddDocType(); void slotEditDocType(); void slotRemoveDocType(); protected slots: void fillNumberCycleCombo(); void slotNumberCycleChanged( const QString& ); void slotEditNumberCycles(); void slotWatermarkModeChanged( int ); void slotWatermarkUrlChanged( const QString& ); void slotTemplateUrlChanged( const QString& ); signals: /** * emitted for every doctype which is deleted */ void removedType( const QString& ); private: DocType currentDocType(); DocType mOrigDocType; QStringList allNumberCycles(); QStringList removedTypes() { return mRemovedTypes; } void removeTypeFromDb( const QString& ); void renameTypeInDb( const QString&, const QString& ); QMap mTypeNameChanges; QMap mChangedDocTypes; QMap mOrigDocTypes; QStringList mAddedTypes; QStringList mRemovedTypes; QString mPreviousType; QString mExampleDocType; QString mExampleAddressUid; }; #endif kraft-0.97/src/doctypeeditbase.ui000066400000000000000000000212101410616450300170520ustar00rootroot00000000000000 DocTypeEditBase 0 0 598 370 0 Qt::Horizontal QSizePolicy::Expanding 37 20 Click to add a new document type to the list. Add click to edit the selected document type name Edit click to remove the current document type Remove Unique Document Number Number &Cycle: false mNumberCycleCombo example Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter false ident Template: false Example Id: false example Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter false example Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter false Counter: false Qt::Horizontal QSizePolicy::Expanding 71 20 &Edit Number Cycles... Qt::Vertical QSizePolicy::Expanding 20 54 Template for PDF Creation no watermark on first page on all pages &Template File: false mTemplateUrl W&atermark: false mWatermarkCombo &Watermark File: false mWatermarkUrl kraft-0.97/src/documentman.cpp000066400000000000000000000147601410616450300163750ustar00rootroot00000000000000/*************************************************************************** documentman.cpp - Document Manager ------------------- begin : 2006 copyright : (C) 2006 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include #include #include #include #include "documentman.h" #include "defaultprovider.h" #include "docdigest.h" #include "kraftdb.h" #include "doctype.h" #include "kraftsettings.h" Q_GLOBAL_STATIC(DocumentMan, mSelf) DocumentMan *DocumentMan::self() { return mSelf; } DocumentMan::DocumentMan() : mFullTax( -1 ), mReducedTax( -1 ) { } DocGuardedPtr DocumentMan::copyDocument( const QString& copyFromId ) { DocGuardedPtr doc = new KraftDoc( ); if ( ! copyFromId.isEmpty() ) { // copy the content from the source document to the new doc. DocGuardedPtr sourceDoc = openDocument( copyFromId ); if ( sourceDoc ) { *doc = *sourceDoc; // copies all data from the previous doc doc->setPredecessor(QString()); // clear the predecessor } doc->setLastModified( QDateTime::currentDateTime()); } return doc; } DocGuardedPtr DocumentMan::createDocument( const QString& docType, const QString& copyFromId, const DocPositionList& listToCopy) { DocGuardedPtr doc = new KraftDoc(); // qDebug () << "new document ID: " << doc->docID().toString() << endl; if ( ! copyFromId.isEmpty() ) { // copy the content from the source document to the new doc. DocGuardedPtr sourceDoc = openDocument( copyFromId ); if ( sourceDoc ) { *doc = *sourceDoc; // copies all data from the previous doc doc->setIdent(QString()); doc->setDocType(docType); // sets the defaults for the new doc type doc->deleteItems(); // remove all items that exist so far doc->setPositionList(listToCopy, true); // check for relations between old and new doc DocType sourceDocType( sourceDoc->docType() ); // for new docs check if it should substract the sum of the predecessor doc DocType newDocType(docType); if( newDocType.substractPartialInvoice() ) { if( sourceDocType.partialInvoice() ) { Geld g = sourceDoc->nettoSum(); DocPosition *pos = doc->createPosition(DocPositionBase::Position); if(pos) { Einheit e; pos->setUnit(e); pos->setAmount(1.0); Geld ng(-1 * g.toLong()); pos->setUnitPrice(ng); pos->setText(i18nc("Text to be inserted into a doc, if the sum of another doc needs to be substracted " "ie. in a final invoice if there was a partial invoice before" "%1 is substited by the doc type, %2 by the id of the predecessor doc.", "Substract sum from %1 %2", sourceDocType.name(), sourceDoc->ident())); } } } doc->setPredecessor(sourceDoc->ident()); // Take the default pre- and posttext for the new docType, or, if that is empty, the texts of the old doc QString newText = DefaultProvider::self()->defaultText( docType, KraftDoc::Header ); if (newText.isEmpty() ) { newText = sourceDoc->preText(); } doc->setPreText(newText); newText = DefaultProvider::self()->defaultText( docType, KraftDoc::Footer ); if (newText.isEmpty() ) { newText = sourceDoc->postText(); } doc->setPostText(newText); delete sourceDoc; } } else { // Absolute new document doc->setDocType(docType); doc->setPreText(DefaultProvider::self()->defaultText(docType, KraftDoc::Header)); doc->setPostText(DefaultProvider::self()->defaultText(docType, KraftDoc::Footer)); doc->setGoodbye( KraftSettings::greeting() ); } // set the proper texts and other data doc->setLastModified( QDateTime::currentDateTime()); return doc; } DocGuardedPtr DocumentMan::openDocumentbyIdent( const QString& ident ) { QSqlQuery q; q.prepare("SELECT docID FROM document WHERE ident=:ident"); q.bindValue(":ident", ident); q.exec(); if( q.next() ) { const QString id = q.value(0).toString(); return openDocument(id); } return nullptr; } DocGuardedPtr DocumentMan::openDocument( const QString& id ) { // qDebug () << "Opening Document with id " << id << endl; DocGuardedPtr doc = new KraftDoc(); doc->openDocument( id ); return doc; } void DocumentMan::clearTaxCache() { mFullTax = -1; mReducedTax = -1; } double DocumentMan::tax( const QDate& date ) { if ( mFullTax < 0 || date != mTaxDate ) readTaxes( date ); return mFullTax; } double DocumentMan::reducedTax( const QDate& date ) { if ( mReducedTax < 0 || date != mTaxDate ) readTaxes( date ); return mReducedTax; } bool DocumentMan::readTaxes( const QDate& date ) { QString sql; QSqlQuery q; sql = "SELECT fullTax, reducedTax, startDate FROM taxes "; sql += "WHERE startDate <= :date ORDER BY startDate DESC LIMIT 1"; q.prepare( sql ); QString dateStr = date.toString( "yyyy-MM-dd" ); // qDebug () << "** Datestring: " << dateStr; q.bindValue( ":date", dateStr ); q.exec(); if ( q.next() ) { mFullTax = q.value( 0 ).toDouble(); mReducedTax = q.value( 1 ).toDouble(); mTaxDate = date; // qDebug () << "* Taxes: " << mFullTax << "/" << mReducedTax << " from " << q.value( 2 ).toDate(); } return ( mFullTax > 0 && mReducedTax > 0 ); } DocumentMan::~DocumentMan() { } kraft-0.97/src/documentman.h000066400000000000000000000034761410616450300160440ustar00rootroot00000000000000/*************************************************************************** documentman.h - Document Manager ------------------- begin : 2006 copyright : (C) 2006 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef DOCUMENTMAN_H #define DOCUMENTMAN_H #include "kraftdoc.h" class DocPosition; class QSqlQuery; typedef QMap DocumentMap; class DocumentMan { public: ~DocumentMan(); static DocumentMan *self(); DocGuardedPtr createDocument(const QString& docType, const QString& copyFromId = QString(), const DocPositionList &listToCopy = DocPositionList() ); DocGuardedPtr copyDocument( const QString& copyFromId ); DocGuardedPtr openDocument( const QString& ); DocGuardedPtr openDocumentbyIdent( const QString& ident ); double tax( const QDate& ); double reducedTax( const QDate& ); void clearTaxCache(); DocumentMan(); private: bool readTaxes( const QDate& ); double mFullTax; double mReducedTax; QDate mTaxDate; }; #endif kraft-0.97/src/documentsaverbase.cpp000066400000000000000000000023151410616450300175660ustar00rootroot00000000000000/*************************************************************************** documentsaverbase - saver for documents ------------------- begin : 2006-20-01 copyright : (C) 2006 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ // include files for Qt // include files for KDE #include #include "documentsaverbase.h" DocumentSaverBase::DocumentSaverBase( ) : QObject() { } DocumentSaverBase::~DocumentSaverBase( ) { } /* END */ kraft-0.97/src/documentsaverbase.h000066400000000000000000000026001410616450300172300ustar00rootroot00000000000000/*************************************************************************** documentsaverbase - Base class of a document save class ------------------- begin : 2006-02-21 copyright : (C) 2005 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef _DOCUMENTSAVERBASE_H #define _DOCUMENTSAVERBASE_H // include files #include /** * */ class KraftDoc; class dbID; class DocumentSaverBase : public QObject { Q_OBJECT public: DocumentSaverBase(); virtual ~DocumentSaverBase(); virtual bool saveDocument( KraftDoc* ) = 0; virtual void load( const QString&, KraftDoc * ) = 0; }; #endif /* END */ kraft-0.97/src/documentsaverdb.cpp000066400000000000000000000405201410616450300172410ustar00rootroot00000000000000/*************************************************************************** templatesaverbase - ------------------- begin : 2005-20-01 copyright : (C) 2005 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ // include files for Qt #include #include #include #include // include files for KDE #include #include "documentsaverdb.h" #include "docposition.h" #include "kraftdoc.h" #include "kraftdb.h" #include "unitmanager.h" #include "dbids.h" #include "kraftsettings.h" #include "doctype.h" #include "defaultprovider.h" /* Table document: * +----------------+--------------+------+-----+-------------------+----------------+ * | Field | Type | Null | Key | Default | Extra | * +----------------+--------------+------+-----+-------------------+----------------+ * | docID | int(11) | NO | PRI | NULL | auto_increment | * | ident | varchar(32) | YES | MUL | NULL | | * | docType | varchar(255) | YES | | NULL | | * | docDescription | text | YES | | NULL | | * | clientID | varchar(32) | YES | MUL | NULL | | * | clientAddress | text | YES | | NULL | | * | salut | varchar(255) | YES | | NULL | | * | goodbye | varchar(128) | YES | | NULL | | * | lastModified | timestamp | NO | | CURRENT_TIMESTAMP | | * | date | date | YES | | NULL | | * | pretext | text | YES | | NULL | | * | posttext | text | YES | | NULL | | * | country | varchar(32) | YES | | NULL | | * | language | varchar(32) | YES | | NULL | | * | projectLabel | varchar(255) | YES | | NULL | | * | predecessor | varchar(32) | YES | | NULL | | * +----------------+--------------+------+-----+-------------------+----------------+ * 14 rows in set (0.00 sec) * */ namespace { void checkAndSet(bool& changes, QSqlRecord& record, const QString& name, const QVariant& setValue) { Q_ASSERT(record.contains(name)); // the record must have the column if( record.value(name) != setValue) { record.setValue(name, setValue); changes = true; } } bool fillDocumentBuffer(QSqlRecord &buf, KraftDoc *doc) { bool changes {false}; if( doc ) { checkAndSet(changes, buf, "ident", doc->ident()); checkAndSet(changes, buf, "docType", doc->docType()); checkAndSet(changes, buf, "docDescription", KraftDB::self()->mysqlEuroEncode(doc->whiteboard())); checkAndSet(changes, buf, "clientID", doc->addressUid()); checkAndSet(changes, buf, "clientAddress", doc->address()); checkAndSet(changes, buf, "salut", doc->salut()); checkAndSet(changes, buf, "goodbye", doc->goodbye()); checkAndSet(changes, buf, "date", doc->date()); checkAndSet(changes, buf, "pretext", KraftDB::self()->mysqlEuroEncode( doc->preText())); checkAndSet(changes, buf, "posttext", KraftDB::self()->mysqlEuroEncode( doc->postText())); checkAndSet(changes, buf, "country", DefaultProvider::self()->locale()->bcp47Name()); checkAndSet(changes, buf, "language", ""); checkAndSet(changes, buf, "projectLabel", doc->projectLabel()); checkAndSet(changes, buf, "predecessor", doc->predecessor()); } return changes; } } DocumentSaverDB::DocumentSaverDB( ) : DocumentSaverBase(), PosTypePosition( QString::fromLatin1( "Position" ) ), PosTypeExtraDiscount( QString::fromLatin1( "ExtraDiscount" ) ), PosTypeHeader( QString::fromLatin1( "Header" ) ) { } bool DocumentSaverDB::saveDocument(KraftDoc *doc ) { bool result = false; if( ! doc ) return result; QSqlTableModel model; model.setTable("document"); QSqlRecord record; // qDebug () << "############### Document Save ################" << endl; if( doc->isNew() ) { record = model.record(); } else { model.setFilter("docID=" + doc->docID().toString()); model.select(); if ( model.rowCount() > 0 ) { record = model.record(0); } else { qCritical() << "Could not select document record" << endl; return result; } // The document was already saved. } if( !doc->isNew() && doc->docTypeChanged() ) { // an existing doc has a new document type. Fix the doc number cycle and pick a new ident DocType dt( doc->docType() ); QString ident = dt.generateDocumentIdent( doc->date(), doc->docType(), doc->addressUid() ); doc->setIdent( ident ); } bool hasChanges = fillDocumentBuffer( record, doc ); if( doc->isNew() ) { // qDebug () << "Doc is new, inserting" << endl; if( !model.insertRecord(-1, record)) { QSqlError err = model.lastError(); // qDebug () << "################# SQL Error: " << err.text(); } model.submitAll(); dbID id = KraftDB::self()->getLastInsertID(); doc->setDocID( id ); // get the uniq id and write it into the db DocType dt( doc->docType() ); QString ident = dt.generateDocumentIdent( doc->date(), doc->docType(), doc->addressUid() ); doc->setIdent( ident ); model.setFilter("docID=" + id.toString()); model.select(); if ( model.rowCount() > 0 ) { model.setData(model.index(0, 1), ident); model.submitAll(); } } else { // qDebug () << "Doc is not new, updating #" << doc->docID().intID() << endl; checkAndSet(hasChanges, record, "docID", doc->docID().toString()); if (!hasChanges) { // if there haven't been changes in the document record, we update the changes // timestamp manually, otherwise it is not updated at all. const QString dt = QDateTime::currentDateTime().toString("yyyy-MM-ddThh:mm:ss"); checkAndSet(hasChanges, record, "lastModified", QVariant(dt)); } } model.setRecord(0, record); model.submitAll(); saveDocumentPositions( doc ); // qDebug () << "Saved document no " << doc->docID().toString() << endl; return result; } void DocumentSaverDB::saveDocumentPositions( KraftDoc *doc ) { DocPositionList posList = doc->positions(); // invert all pos numbers to avoid a unique violation // FIXME: We need non-numeric ids QSqlQuery upq; QString queryStr = "UPDATE docposition SET ordNumber = -1 * ordNumber WHERE docID="; queryStr += doc->docID().toString(); queryStr += " AND ordNumber > 0"; upq.prepare( queryStr ); upq.exec(); int ordNumber = 1; QSqlTableModel model; model.setTable("docposition"); model.setEditStrategy(QSqlTableModel::OnManualSubmit); QVector deleteIds; DocPositionListIterator it( posList ); while( it.hasNext() ) { DocPositionBase *dpb = it.next(); DocPosition *dp = static_cast(dpb); QSqlRecord record ; bool doInsert = true; int posDbID = dp->dbId().toInt(); if( posDbID > -1 ) { const QString selStr = QString("docID=%1 AND positionID=%2").arg( doc->docID().toInt() ).arg( posDbID ); // qDebug() << "Selecting with " << selStr << endl; model.setFilter( selStr ); model.select(); if ( model.rowCount() > 0 ) { if( ! dp->toDelete() ) record = model.record(0); doInsert = false; } else { qCritical() << "ERR: Could not select document position record" << endl; return; } } else { // The record is new record = model.record(); } if( dp->toDelete() ) { // qDebug () << "This one is to delete, do it!" << endl; if( doInsert ) { qWarning() << "Attempt to delete a toInsert-Item, obscure" << endl; } // delete all existing attributes dp->attributes().dbDeleteAll( dp->dbId() ); model.removeRow(0); model.submitAll(); continue; } if( record.count() > 0 ) { // qDebug() << "Updating position " << dp->position() << " is " << dp->text() << endl; QString typeStr = PosTypePosition; double price = dp->unitPrice().toDouble(); if ( dp->type() == DocPositionBase::ExtraDiscount ) { typeStr = PosTypeExtraDiscount; } record.setValue( "docID", QVariant(doc->docID().toInt())); record.setValue( "ordNumber", QVariant(ordNumber)); record.setValue( "text", QVariant(dp->text())); record.setValue( "postype", QVariant(typeStr)); record.setValue( "amount", QVariant(dp->amount())); int unitId = dp->unit().id(); record.setValue( "unit", QVariant(unitId)); record.setValue( "price", QVariant(price)); record.setValue( "taxType", QVariant(dp->taxType())); ordNumber++; // FIXME if( doInsert ) { // qDebug () << "Inserting!" << endl; model.insertRecord(-1, record); model.submitAll(); dp->setDbId( KraftDB::self()->getLastInsertID().toInt() ); } else { // qDebug () << "Updating!" << endl; model.setRecord(0, record); model.submitAll(); } } else { // qDebug () << "ERR: No record object found!" << endl; } dp->attributes().save( dp->dbId() ); QSqlError err = model.lastError(); if( err.type() != QSqlError::NoError ) { // qDebug () << "SQL-ERR: " << err.text() << " in " << model.tableName() << endl; } } model.submitAll(); /* remove the docpositions that were marked to be deleted */ if( deleteIds.count() ) { QSqlQuery delQuery; delQuery.prepare( "DELETE FROM docposition WHERE positionID=:id" ); foreach( int id, deleteIds ) { // kDebug() << "Deleting attribute id " << id; delQuery.bindValue( ":id", id ); delQuery.exec(); } } } void DocumentSaverDB::load( const QString& id, KraftDoc *doc ) { if( !id.isEmpty() ) { QSqlQuery q; q.prepare("SELECT ident, docType, clientID, clientAddress, salut, goodbye, date, lastModified, language, country, " "pretext, posttext, docDescription, projectlabel, predecessor FROM document WHERE docID=:docID"); q.bindValue(":docID", id); q.exec(); // qDebug () << "Loading document id " << id << endl; if( q.next()) { // qDebug () << "loading document with id " << id << endl; dbID dbid; dbid = id; doc->setDocID(dbid); doc->setIdent( q.value( 0 ).toString() ); doc->setDocType( q.value( 1 ).toString() ); doc->setAddressUid( q.value( 2 ).toString() ); doc->setAddress( q.value( 3 ).toString() ); QString salut = q.value(4).toString(); doc->setSalut( salut ); doc->setGoodbye( q.value( 5 ).toString() ); doc->setDate ( q.value( 6 ).toDate() ); QDateTime dt = q.value(7).toDateTime(); // Sqlite stores the timestamp as UTC in the database. Mysql does not. if (KraftDB::self()->isSqlite()) { dt.setTimeSpec(Qt::UTC); doc->setLastModified(dt.toLocalTime()); } else { doc->setLastModified(dt); } // Removed, as with Kraft 0.80 there is no locale management on doc level any more // doc->setCountryLanguage( q.value( 8 ).toString(), // q.value( 9 ).toString()); doc->setPreText( KraftDB::self()->mysqlEuroDecode( q.value( 10 ).toString() ) ); doc->setPostText( KraftDB::self()->mysqlEuroDecode( q.value( 11 ).toString() ) ); doc->setWhiteboard( KraftDB::self()->mysqlEuroDecode( q.value( 12 ).toString() ) ); doc->setProjectLabel( q.value(13).toString() ); doc->setPredecessor( q.value(14).toString() ); } } // load the dbID of the predecessor document from the database. const QString pIdent = doc->predecessor(); if( ! pIdent.isEmpty() ) { QSqlQuery q1; q1.prepare("SELECT docID FROM document WHERE ident=:docID"); q1.bindValue(":docID", pIdent); q1.exec(); if( q1.next() ) { const QString pDbId = q1.value(0).toString(); doc->setPredecessorDbId(pDbId); } } // finally load the item data. loadPositions( id, doc ); } /* docposition: +------------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +------------+--------------+------+-----+---------+----------------+ | positionID | int(11) | | PRI | NULL | auto_increment | | docID | int(11) | | MUL | 0 | | | ordNumber | int(11) | | | 0 | | | text | mediumtext | YES | | NULL | | | amount | decimal(6,2) | YES | | NULL | | | unit | varchar(64) | YES | | NULL | | | price | decimal(6,2) | YES | | NULL | | +------------+--------------+------+-----+---------+----------------+ */ void DocumentSaverDB::loadPositions( const QString& id, KraftDoc *doc ) { QSqlQuery q; q.prepare("SELECT positionID, postype, text, amount, unit, price, taxType FROM docposition WHERE docID=:docID ORDER BY ordNumber"); q.bindValue(":docID", id); q.exec(); // qDebug () << "* loading document positions for document id " << id << endl; while( q.next() ) { // qDebug () << " loading position id " << q.value( 0 ).toInt() << endl; DocPositionBase::PositionType type = DocPositionBase::Position; QString typeStr = q.value( 1 ).toString(); if ( typeStr == PosTypeExtraDiscount ) { type = DocPositionBase::ExtraDiscount; } else if ( typeStr == PosTypePosition ) { // nice, default position type. type = DocPositionBase::Position; } else { // qDebug () << "ERROR: Strange type string loaded from db: " << typeStr << endl; } DocPosition *dp = doc->createPosition( type ); dp->setDbId( q.value(0).toInt() ); dp->setText( q.value(2).toString() ); // Note: empty fields are treated as Positions which is intended because // the type col was added later and thus might be empty for older entries dp->setAmount( q.value(3).toDouble() ); dp->setUnit( UnitManager::self()->getUnit( q.value(4).toInt() ) ); dp->setUnitPrice( q.value(5).toDouble() ); dp->setTaxType( q.value(6).toInt() ); dp->loadAttributes(); } } DocumentSaverDB::~DocumentSaverDB( ) { } /* END */ kraft-0.97/src/documentsaverdb.h000066400000000000000000000031741410616450300167120ustar00rootroot00000000000000/*************************************************************************** documentsaverdb - save documents to the database ------------------- begin : 2006-02-21 copyright : (C) 2005 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef _DOCUMENTSAVERDB_H #define _DOCUMENTSAVERDB_H #include "documentsaverbase.h" class KraftDoc; class QSqlRecord; class dbID; class QString; class DocumentSaverDB : public DocumentSaverBase { Q_OBJECT public: DocumentSaverDB(); virtual ~DocumentSaverDB(); virtual bool saveDocument( KraftDoc* ); virtual void load( const QString& , KraftDoc * ); protected: virtual void loadPositions( const QString&, KraftDoc* ); virtual void saveDocumentPositions( KraftDoc* ); private: const QString PosTypePosition; const QString PosTypeExtraDiscount; const QString PosTypeHeader; }; #endif /* END */ kraft-0.97/src/documenttemplate.cpp000066400000000000000000000377731410616450300174460ustar00rootroot00000000000000/*************************************************************************** Template for Kraft Documents - Grantlee and ctemplate ------------------- begin : March 2020 copyright : (C) 2020 by Klaas Freitag email : kraft@freisturz.de ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include "documenttemplate.h" #include "texttemplate.h" #include "grantleetemplate.h" #include "format.h" #include "kraftsettings.h" #include #define TAG( THE_TAG ) QStringLiteral( THE_TAG ) #define DICT( THE_DICT ) QStringLiteral( THE_DICT ) // ================================================================================== namespace { QString escapeTrml2pdfXML( const QString& str ) { return( str.toHtmlEscaped() ); } QString rmlString( const QString& str, const QString& paraStyle = QString() ) { QString rml; QString style( paraStyle ); if ( style.isEmpty() ) style = QStringLiteral("text"); // QStringList li = QStringList::split( "\n", escapeTrml2pdfXML( str ) ); QStringList li = escapeTrml2pdfXML( str ).split( "\n" ); rml = QString( "" ).arg( style ); rml += li.join( QString( "" ).arg( style ) ) + ""; // qDebug () << "Returning " << rml; return rml; } QVariantHash contactToVariantHash(const KContacts::Addressee& contact ) { QVariantHash hash; QString n = contact.realName(); if (n.isEmpty()) n = QStringLiteral("Not set!"); hash.insert( QStringLiteral( "NAME" ), escapeTrml2pdfXML(n) ); if( contact.isEmpty() ) return hash; QString co = contact.organization(); if( co.isEmpty() ) { co = contact.realName(); } hash.insert( QStringLiteral( "ORGANISATION" ), escapeTrml2pdfXML( co ) ); const QUrl url = contact.url().url(); hash.insert( QStringLiteral( "URL" ), escapeTrml2pdfXML( url.url() ) ); hash.insert( QStringLiteral( "EMAIL" ), escapeTrml2pdfXML( contact.preferredEmail() ) ); hash.insert( QStringLiteral( "PHONE" ), escapeTrml2pdfXML( contact.phoneNumber( KContacts::PhoneNumber::Work ).number() ) ); hash.insert( QStringLiteral( "FAX" ), escapeTrml2pdfXML( contact.phoneNumber( KContacts::PhoneNumber::Fax ).number() ) ); hash.insert( QStringLiteral( "CELL" ), escapeTrml2pdfXML( contact.phoneNumber( KContacts::PhoneNumber::Cell ).number() ) ); KContacts::Address address; address = contact.address( KContacts::Address::Pref ); if( address.isEmpty() ) address = contact.address(KContacts::Address::Work ); if( address.isEmpty() ) address = contact.address(KContacts::Address::Home ); if( address.isEmpty() ) address = contact.address(KContacts::Address::Postal ); hash.insert( QStringLiteral( "POSTBOX" ), escapeTrml2pdfXML( address.postOfficeBox() ) ); hash.insert( QStringLiteral( "EXTENDED" ), escapeTrml2pdfXML( address.extended() ) ); hash.insert( QStringLiteral( "STREET" ), escapeTrml2pdfXML( address.street() ) ); hash.insert( QStringLiteral( "LOCALITY" ), escapeTrml2pdfXML( address.locality() ) ); hash.insert( QStringLiteral( "REGION" ), escapeTrml2pdfXML( address.region() ) ); hash.insert( QStringLiteral( "POSTCODE" ), escapeTrml2pdfXML( address.postalCode() ) ); hash.insert( QStringLiteral( "COUNTRY" ), escapeTrml2pdfXML( address.country() ) ); hash.insert( QStringLiteral( "REGION" ), escapeTrml2pdfXML( address.region() ) ); hash.insert( QStringLiteral("LABEL" ), escapeTrml2pdfXML( address.label() ) ); return hash; } QVariantHash labelVariantHash() { QVariantHash hash; hash.insert( TAG( "NO_SHORT"), i18nc("Sequence number printed on the document", "No.") ); hash.insert( TAG( "ITEM"), i18nc("Document item printed on the document", "Item") ); hash.insert( TAG( "QUANTITY_SHORT"), i18nc("Abbrev. of Quantity printed on the document", "Qty.") ); hash.insert( TAG( "UNIT"), i18nc("Unit printed on the document", "Unit") ); hash.insert( TAG( "PRICE"), i18nc("Price of an item printed on the document", "Price") ); hash.insert( TAG( "SUM"), i18nc("Printed on the document", "Sum") ); hash.insert( TAG( "NET"), i18nc("printed on the document", "Net") ); hash.insert( TAG( "VAT"), i18nc("Printed on the document", "VAT") ); hash.insert( TAG( "PHONE"), i18nc("Printed on the document", "Phone")); hash.insert( TAG( "FAX"), i18nc("Printed on the document", "FAX")); hash.insert( TAG( "MOBILE"), i18nc("Printed on the document", "Mobile")); hash.insert( TAG( "EMAIL"), i18nc("Printed on the document", "Email")); hash.insert( TAG( "WEBSITE"), i18nc("Printed on the document", "Website")); hash.insert( TAG( "PAGE"), i18nc("Printed on the document", "Page")); hash.insert( TAG( "PAGE_OF"), i18nc("the 'of' in page X of Y", "of")); hash.insert( TAG( "DOC_NO"), i18nc("Document number on document", "Document No.")); hash.insert( TAG( "DATE"), i18nc("Date on document", "Date")); hash.insert( TAG( "PROJECT"), i18nc("Project label", "Project")); hash.insert( TAG( "CUST_ID"), i18nc("Customer ID on document", "Customer Id")); hash.insert( TAG( "CURRENCY_SIGN"), DefaultProvider::self()->currencySymbol()); return hash; } void variantHashToTemplate( TextTemplate& tmpl, const QString& prefix, const QVariantHash& hash) { QVariantHash::const_iterator i; for (i = hash.constBegin(); i != hash.constEnd(); ++i) { QString key = i.key(); if (!prefix.isEmpty()) { key = QString("%1_%2").arg(prefix).arg(i.key()); } tmpl.setValue(key, i.value().toString()); } } void contactToTemplate( TextTemplate& tmpl, const QString& prefix, const KContacts::Addressee& contact ) { const QVariantHash hash = contactToVariantHash(contact); variantHashToTemplate(tmpl, prefix, hash); } void addLabelsToTemplate(TextTemplate& tmpl) { const QVariantHash hash = labelVariantHash(); variantHashToTemplate(tmpl, QStringLiteral("LAB"), hash); } } // ================================================================================== DocumentTemplate::DocumentTemplate( const QString& tmplFile ) :_tmplFile(tmplFile) { } // ================================================================================== CTemplateDocumentTemplate::CTemplateDocumentTemplate(const QString& tmplFile) :DocumentTemplate(tmplFile) { } const QString CTemplateDocumentTemplate::expand(ArchDoc *archDoc, const KContacts::Addressee& myContact, const KContacts::Addressee& customerContact) { if (archDoc == nullptr) { return QString(); } // create a text template TextTemplate tmpl; tmpl.setTemplateFileName(_tmplFile); /* replace the placeholders */ /* A placeholder has the format */ const ArchDocPositionList posList = archDoc->positions(); QString h; ArchDocPositionList::const_iterator it; int specialPosCnt = 0; int taxFreeCnt = 0; int reducedTaxCnt = 0; int fullTaxCnt = 0; bool individualTax = false; /* Check for the tax settings: If the taxType is not the same for all items, * we have individual Tax setting and show the tax marker etc. */ DocPositionBase::TaxType ttype = DocPositionBase::TaxInvalid; for ( it = posList.begin(); it != posList.end(); ++it ) { ArchDocPosition pos (*it); if( ttype == DocPositionBase::TaxInvalid ) { ttype = pos.taxType(); } else { if( ttype != pos.taxType() ) { // different from previous one? individualTax = true; break; } } } /* now loop over the items to fill the template structures */ for ( it = posList.begin(); it != posList.end(); ++it ) { ArchDocPosition pos (*it); tmpl.createDictionary( "POSITIONS" ); tmpl.setValue( DICT("POSITIONS"), TAG( "POS_NUMBER" ) , pos.posNumber() ); tmpl.setValue( DICT("POSITIONS"), TAG("POS_TEXT"), rmlString( pos.text(), QString( "%1text" ).arg( pos.kind().toLower() ) ) ); // format the amount value of the item, do not show the precision if there is no fraction double amount = pos.amount(); h = Format::localeDoubleToString(amount, *DefaultProvider::self()->locale()); tmpl.setValue( DICT("POSITIONS"), TAG("POS_AMOUNT"), h ); tmpl.setValue( DICT("POSITIONS"), TAG("POS_UNIT"), escapeTrml2pdfXML( pos.unit() ) ); tmpl.setValue( DICT("POSITIONS"), TAG("POS_UNITPRICE"), pos.unitPrice().toString() ); tmpl.setValue( DICT("POSITIONS"), TAG("POS_TOTAL"), pos.nettoPrice().toString() ); tmpl.setValue( DICT("POSITIONS"), TAG("POS_KIND"), pos.kind().toLower() ); QString taxType; if( individualTax ) { if( pos.taxType() == 1 ) { taxFreeCnt++; taxType = "TAX_FREE"; } else if( pos.taxType() == 2 ) { reducedTaxCnt++; taxType = "REDUCED_TAX"; } else { // ATTENTION: Default for all non known tax types is full tax. fullTaxCnt++; taxType = "FULL_TAX"; } tmpl.createSubDictionary( "POSITIONS", taxType ); } /* item kind: Normal, alternative or demand item. For normal items, the kind is empty. */ if ( !pos.kind().isEmpty() ) { specialPosCnt++; } } if ( specialPosCnt ) { tmpl.createDictionary( "SPECIAL_POS" ); tmpl.setValue( DICT("SPECIAL_POS"), TAG("COUNT"), QString::number( specialPosCnt ) ); tmpl.setValue( DICT("SPECIAL_POS"), TAG("LAB_SPECIAL_ITEMS"), i18n("Please note: This offer contains %1 alternative or demand positions, printed in italic font. These do not add to the overall sum.", QString::number( specialPosCnt ) ) ); } /* * Just show the tax index if we have multiple tax settings */ if( individualTax ) { tmpl.createDictionary( "TAX_FREE_ITEMS" ); tmpl.setValue( DICT("TAX_FREE_ITEMS"), TAG("COUNT"), QString::number( taxFreeCnt )); tmpl.setValue( DICT("TAX_FREE_ITEMS"), TAG( "LAB_TAX_FREE_ITEMS"), i18n("tax free items (%1 pcs.)", QString::number( taxFreeCnt )) ); tmpl.createDictionary( "REDUCED_TAX_ITEMS" ); tmpl.setValue( DICT("REDUCED_TAX_ITEMS"), TAG("COUNT"), QString::number( reducedTaxCnt )); tmpl.setValue( DICT("REDUCED_TAX_ITEMS"), TAG("TAX"), DefaultProvider::self()->locale()->toString( archDoc->reducedTax()) ); tmpl.setValue( DICT("REDUCED_TAX_ITEMS"), TAG("LAB_TAX_REDUCED_ITEMS"), i18n("items with reduced tax of %1% (%2 pcs.)", DefaultProvider::self()->locale()->toString( archDoc->reducedTax()), QString::number( reducedTaxCnt )) ); tmpl.createDictionary( "FULL_TAX_ITEMS" ); tmpl.setValue( DICT("FULL_TAX_ITEMS"), TAG("COUNT"), QString::number( fullTaxCnt )); tmpl.setValue( DICT("FULL_TAX_ITEMS"), TAG("TAX"), DefaultProvider::self()->locale()->toString( archDoc->tax()) ); tmpl.setValue( DICT("FULL_TAX_ITEMS"), TAG("LAB_TAX_FULL_ITEMS"), i18n("No label: items with full tax of %1% (%2 pcs.)", DefaultProvider::self()->locale()->toString( archDoc->tax()), QString::number( fullTaxCnt )) ); } /* now replace stuff in the whole document */ tmpl.setValue( TAG( "DATE" ), Format::toDateString(archDoc->date(), KraftSettings::self()->dateFormat())); tmpl.setValue( TAG( "DOCTYPE" ), escapeTrml2pdfXML( archDoc->docType() ) ); tmpl.setValue( TAG( "ADDRESS" ), escapeTrml2pdfXML( archDoc->address() ) ); contactToTemplate( tmpl, "CLIENT", customerContact ); contactToTemplate( tmpl, "MY", myContact ); tmpl.setValue( TAG( "DOCID" ), escapeTrml2pdfXML( archDoc->ident() ) ); tmpl.setValue( TAG( "PROJECTLABEL" ), escapeTrml2pdfXML( archDoc->projectLabel() ) ); tmpl.setValue( TAG( "SALUT" ), escapeTrml2pdfXML( archDoc->salut() ) ); tmpl.setValue( TAG( "GOODBYE" ), escapeTrml2pdfXML( archDoc->goodbye() ) ); tmpl.setValue( TAG( "PRETEXT" ), rmlString( archDoc->preText() ) ); tmpl.setValue( TAG( "POSTTEXT" ), rmlString( archDoc->postText() ) ); tmpl.setValue( TAG( "BRUTTOSUM" ), archDoc->bruttoSum().toString() ); tmpl.setValue( TAG( "NETTOSUM" ), archDoc->nettoSum().toString() ); h = DefaultProvider::self()->locale()->toString( archDoc->tax() ); // qDebug () << "Tax in archive document: " << h; if ( archDoc->reducedTaxSum().toLong() != 0 ) { tmpl.createDictionary( DICT( "SECTION_REDUCED_TAX" ) ); tmpl.setValue( DICT("SECTION_REDUCED_TAX"), TAG( "REDUCED_TAX_SUM" ), archDoc->reducedTaxSum().toString() ); h = DefaultProvider::self()->locale()->toString( archDoc->reducedTax() ); tmpl.setValue( DICT("SECTION_REDUCED_TAX"), TAG( "REDUCED_TAX" ), h ); tmpl.setValue( DICT("SECTION_REDUCED_TAX"), TAG( "REDUCED_TAX_LABEL" ), i18n( "reduced VAT" ) ); } if ( archDoc->fullTaxSum().toLong() != 0 ) { tmpl.createDictionary( DICT( "SECTION_FULL_TAX" ) ); tmpl.setValue( DICT("SECTION_FULL_TAX"), TAG( "FULL_TAX_SUM" ), archDoc->fullTaxSum().toString() ); h = DefaultProvider::self()->locale()->toString( archDoc->tax() ); tmpl.setValue( DICT("SECTION_FULL_TAX"), TAG( "FULL_TAX" ), h ); tmpl.setValue( DICT("SECTION_FULL_TAX"), TAG( "FULL_TAX_LABEL" ), i18n( "VAT" ) ); } h = DefaultProvider::self()->locale()->toString( archDoc->tax() ); tmpl.setValue( TAG( "VAT" ), h ); tmpl.setValue( TAG( "VATSUM" ), archDoc->taxSum().toString() ); addLabelsToTemplate(tmpl); // finalize the template const QString output = tmpl.expand(); return output; } // ================================================================================== GrantleeDocumentTemplate::GrantleeDocumentTemplate(const QString& tmplFile) : DocumentTemplate(tmplFile) { } const QString GrantleeDocumentTemplate::expand( ArchDoc *archDoc, const KContacts::Addressee &myContact, const KContacts::Addressee &customerContact) { Grantlee::registerMetaType(); Grantlee::registerMetaType(); QFileInfo fi(_tmplFile); if (!fi.exists()) { _errorStr = i18n("Template to convert is not existing!"); } if (!fi.isReadable()) { _errorStr = i18n("Can not read template file!"); } QString rendered; if (_errorStr.isEmpty()) { GrantleeFileTemplate gtmpl(_tmplFile); gtmpl.addToObjMapping("doc", archDoc); gtmpl.addToMapping(QStringLiteral("me"), contactToVariantHash(myContact)); gtmpl.addToMapping(QStringLiteral("customer"), contactToVariantHash(customerContact)); const QVariantHash labelHash = labelVariantHash(); gtmpl.addToMapping(QStringLiteral("label"), labelHash); bool ok; rendered = gtmpl.render(ok); if (!ok) { _errorStr = rendered; rendered.clear(); } } return rendered; } kraft-0.97/src/documenttemplate.h000066400000000000000000000045461410616450300171030ustar00rootroot00000000000000/*************************************************************************** Template for Kraft Documents - Grantlee and ctemplate ------------------- begin : March 2020 copyright : (C) 2020 by Klaas Freitag email : kraft@freisturz.de ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef DOCUMENTTEMPLATE_H #define DOCUMENTTEMPLATE_H #include #include "archdoc.h" class DocumentTemplate { public: DocumentTemplate( const QString& tmplFile ); virtual ~DocumentTemplate(){ }; virtual const QString expand(ArchDoc *archDoc, const KContacts::Addressee &myContact, const KContacts::Addressee &customerContact) = 0; QString error() const { return _errorStr; } protected: QString _tmplFile; QString _errorStr; }; // ================================================================================== class CTemplateDocumentTemplate : public DocumentTemplate { public: CTemplateDocumentTemplate(const QString& tmplFile); const QString expand(ArchDoc *archDoc, const KContacts::Addressee &myContact, const KContacts::Addressee &customerContact) override; }; // ================================================================================== class GrantleeDocumentTemplate : public DocumentTemplate { public: GrantleeDocumentTemplate(const QString& tmplFile); const QString expand(ArchDoc *archDoc, const KContacts::Addressee &myContact, const KContacts::Addressee &customerContact) override; }; #endif // DOCUMENTTEMPLATE_H kraft-0.97/src/einheit.cpp000066400000000000000000000036101410616450300155000ustar00rootroot00000000000000/*************************************************************************** einheit.cpp - ------------------- begin : Don Jan 1 2004 copyright : (C) 2004 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include "einheit.h" Einheit::Einheit() :m_dbId(-1) { } Einheit::Einheit( int id, const QString& einh, const QString& einhLong, const QString& einhPlu, const QString& einhPluLong ) : m_dbId(id) { m_einheitSingular = einh; m_einheitPlural = einhPlu; m_einheitSingularLong = einhLong; m_einheitPluralLong = einhPluLong; } Einheit::Einheit( int id ) : m_dbId(id) { // Ask the Unitmanager here. } Einheit::Einheit( const QString& einhText ) { m_einheitSingular = einhText; m_einheitPlural = einhText; m_einheitSingularLong = einhText; m_einheitPluralLong = einhText; } Einheit::~Einheit(){ } QString Einheit::einheit( int anz ) { if( anz == 1 ) return einheitSingular(); else return einheitPlural(); } QString Einheit::einheit( double anz ) { if( anz == 1.0 ) return einheitSingular(); else return einheitPlural(); } kraft-0.97/src/einheit.h000066400000000000000000000035541410616450300151540ustar00rootroot00000000000000/*************************************************************************** einheit.h - ------------------- begin : Don Jan 1 2004 copyright : (C) 2004 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef EINHEIT_H #define EINHEIT_H #include #include #include "kraftcat_export.h" /** *@author Klaas Freitag */ class KRAFTCAT_EXPORT Einheit { public: typedef QList List; Einheit(); Einheit( int id ); Einheit( const QString& ); // Einheit with arbitrary text. Einheit( int id, const QString&, const QString&, const QString&, const QString& ); ~Einheit(); QString einheitSingular() { return m_einheitSingular; } QString einheitSingularLong() { return m_einheitSingularLong; } QString einheitPlural() { return m_einheitPlural; } QString einheitPluralLong() { return m_einheitPluralLong; } QString einheit( int anz ); QString einheit( double anz ); int id() { return m_dbId; } private: int m_dbId; QString m_einheitSingular; QString m_einheitPlural; QString m_einheitSingularLong; QString m_einheitPluralLong; }; #endif kraft-0.97/src/extractrc000077500000000000000000000347661410616450300153140ustar00rootroot00000000000000#! /usr/bin/env perl ### TODO: other copyrights, license? # Copyright (c) 2004 Richard Evans sub usage { warn <<"EOF"; extractrc [flags] filenames This script extracts messages from designer (.ui) and XMLGUI (.rc) files and writes on standard output (usually redirected to rc.cpp) the equivalent i18n() calls so that xgettext can parse them. --tag=name : Also extract the tag name(s). Repeat the flag to specify multiple names: --tag=tag_one --tag=tag_two --tag-group=group : Use a group of tags - uses 'default' if omitted. Valid groups are: @{[TAG_GROUPS()]} --context=name : Give i18n calls a context name: i18nc("name", ...) --lines : Include source line numbers in comments (deprecated, it is switched on by default now) --cstart=chars : Start of to-EOL style comments in output, defaults to // --language=lang : Create i18n calls appropriate for KDE bindings in the given language. Currently known languages: C++ (default), Python --ignore-no-input : Do not warn if there were no filenames specified --help|? : Display this summary --no-unescape-xml : Don't do xml unescaping EOF exit; } ########################################################################################### use strict; use warnings; use Getopt::Long; use Data::Dumper; use constant TAG_GROUP => { default => "[tT][eE][xX][tT]|title|string|whatsthis|tooltip|label", koffice => "Example|GroupName|Text|Comment|Syntax|TypeName", none => "", }; use constant TAG_GROUPS => join ", ", map "'$_'", sort keys %{&TAG_GROUP}; # Specification to extract nice element-context for strings. use constant ECTX_SPEC => { # Data structure: extension => {tag => [ctxlevel, [attribute, ...]], ...} # Order of attributes determines their order in the extracted comment. "ui" => { "widget" => [10, ["class", "name"]], "item" => [15, []], "property" => [20, ["name"]], "attribute" => [20, ["name"]], }, "rc" => { "Menu" => [10, ["name"]], "ToolBar" => [10, ["name"]], }, "kcfg" => { "group" => [10, ["name"]], "entry" => [20, ["name"]], "whatsthis" => [30, []], "tooltip" => [30, []], "label" => [30, []], }, }; # Specification to exclude strings by trailing section of element-context. use constant ECTX_EXCLUDE => [ # Data structure: [[tag, attribute, attrvalue], [...]] # Empty ("") attribute means all elements with given tag, # empty attrvalue means element with given tag and attribute of any value. [["widget", "class", "KFontComboBox"], ["item", "", ""], ["property", "", ""]], [["widget", "class", "KPushButton"], ["attribute", "name", "buttonGroup"]], [["widget", "class", "QRadioButton"], ["attribute", "name", "buttonGroup"]], [["widget", "class", "QToolButton"], ["attribute", "name", "buttonGroup"]], [["widget", "class", "QCheckBox"], ["attribute", "name", "buttonGroup"]], [["widget", "class", "QPushButton"], ["attribute", "name", "buttonGroup"]], [["widget", "class", "KTimeZoneWidget"], ["property", "name", "text"]], ]; # The parts between the tags of the extensions will be copied verbatim # Same data structure as in ECTX_EXCLUDE, but per extension. my %EXTENSION_VERBATIM_TAGS = ( "kcfg" => [["code", "", ""], ["default", "code", "true"], ["min", "code", "true"], ["max", "code", "true"]], ); # Add attribute lists as hashes, for membership checks. for my $ext ( keys %{&ECTX_SPEC} ) { for my $tag ( keys %{ECTX_SPEC->{$ext}} ) { my $arr = ECTX_SPEC->{$ext}{$tag}[1]; ECTX_SPEC->{$ext}{$tag}[2] = {map {$_ => 1} @{$arr}}; } } ########################################################################################### # Add options here as necessary - perldoc Getopt::Long for details on GetOptions GetOptions ( "tag=s" => \my @opt_extra_tags, "tag-group=s" => \my $opt_tag_group, "context=s" => \my $opt_context, # I18N context "lines" => \my $opt_lines, "cstart=s" => \my $opt_cstart, "language=s" => \my $opt_language, "ignore-no-input" => \my $opt_ignore_no_input, "no-unescape-xml" => \my $opt_no_unescape_xml, "help|?" => \&usage ); unless( @ARGV ) { warn "No filename specified" unless $opt_ignore_no_input; exit; } $opt_tag_group ||= "default"; die "Unknown tag group: '$opt_tag_group', should be one of " . TAG_GROUPS unless exists TAG_GROUP->{$opt_tag_group}; my $tags = TAG_GROUP->{$opt_tag_group}; my $extra_tags = join "", map "|" . quotemeta, @opt_extra_tags; my $text_string = qr/($tags$extra_tags)( [^>]*)?>/; # Precompile regexp my $cstart = $opt_cstart; # no default, selected by language if not given my $language = $opt_language || "C++"; my $ectx_known_exts = join "|", keys %{&ECTX_SPEC}; ########################################################################################### # Unescape basic XML entities. sub unescape_xml ($) { my $text = shift; if (not $opt_no_unescape_xml) { $text =~ s/<//g; $text =~ s/&/&/g; $text =~ s/"/"/g; } return $text; } # Convert uic to C escaping. sub escape_uic_to_c ($) { my $text = shift; $text = unescape_xml($text); $text =~ s/\\/\\\\/g; # escape \ $text =~ s/\"/\\\"/g; # escape " $text =~ s/\r//g; # remove CR (Carriage Return) $text =~ s/\n/\\n\"\n\"/g; # escape LF (Line Feed). uic also change the code line at a LF, we do not do that. return $text; } ########################################################################################### sub dummy_call_infix { my ($cstart, $stend, $ctxt, $text, @cmnts) = @_; for my $cmnt (@cmnts) { print qq|$cstart $cmnt\n|; } if (defined $text) { $text = escape_uic_to_c($text); if (defined $ctxt) { $ctxt = escape_uic_to_c($ctxt); print qq|i18nc("$ctxt", "$text")$stend\n|; } else { print qq|i18n("$text")$stend\n|; } } } my %dummy_calls = ( "C++" => sub { dummy_call_infix($cstart || "//", ";", @_); }, "Python" => sub { dummy_call_infix($cstart || "#", "", @_); }, ); die "unknown language '$language'" if not defined $dummy_calls{$language}; my $dummy_call = $dummy_calls{$language}; # Program start proper - NB $. is the current line number for my $file_name ( @ARGV ) { my $fh; unless ( open $fh, "<", $file_name ) { # warn "Failed to open: '$file_name': $!"; next; } # Ready element-context extraction. my $ectx_ext; my $ectx_string; if ( $file_name =~ /\.($ectx_known_exts)(\.(in|cmake))?$/ ) { $ectx_ext = $1; my $ectx_tag_gr = join "|", keys %{ECTX_SPEC->{$ectx_ext}}; $ectx_string = qr/($ectx_tag_gr)( [^>]*)?>/; # precompile regexp } my $string = ""; my $origstring = ""; my $in_text = 0; my $start_line_no = 0; my $in_skipped_prop = 0; my $tag = ""; my $attr = ""; my $context = ""; my $notr = ""; # Element-context data: [[level, tag, [[attribute, value], ...]], ...] # such that subarrays are ordered increasing by level. my @ectx = (); # All comments to pending dummy call. my @comments = (); while ( <$fh> ) { if ( $. == 1 and $_ !~ /^(?:{$ectx_ext}{$tag} ) { my @atts; for my $ectx_att ( @{ECTX_SPEC->{$ectx_ext}{$tag}[1]} ) { if ( $attr and $attr =~ /\b$ectx_att\s*=\s*(["'])([^"']*?)\1/ ) { my $aval = $2; push @atts, [$ectx_att, $aval]; } } # Kill all tags in element-context with level higer or equal to this, # and add it to the end. my $clevel = ECTX_SPEC->{$ectx_ext}{$tag}[0]; for ( my $i = 0; $i < @ectx; ++$i ) { if ( $clevel <= $ectx[$i][0] ) { @ectx = @ectx[0 .. ($i - 1)]; last; } } push @ectx, [$clevel, $tag, [@atts]]; } if ( ($tag, $attr) = $string =~ /<$text_string/o ) { my ($attr_comment) = $attr =~ /\bcomment=\"([^\"]*)\"/ if $attr; $context = $attr_comment if $attr_comment; my ($attr_context) = $attr =~ /\bcontext=\"([^\"]*)\"/ if $attr; $context = $attr_context if $attr_context; # It is unlikely that both attributes 'context' and 'comment' # will be present, but if so happens, 'context' has priority. my ($attr_extracomment) = $attr =~ /\bextracomment=\"([^\"]*)\"/ if $attr; push @comments, "i18n: $attr_extracomment" if $attr_extracomment; my ($attr_notr) = $attr =~ /\bnotr=\"([^\"]*)\"/ if $attr; $notr = $attr_notr if $attr_notr; my $nongreedystring = $string; $string =~ s/^.*<$text_string//so; $nongreedystring =~ s/^.*?<$text_string//so; if ($string cmp $nongreedystring) { print STDERR "Warning: Line $origstring in file $file_name has more than one tag to extract on the same line, that is not supported by extractrc\n"; } if ( not $attr or $attr !~ /\/ *$/ ) { $in_text = 1; $start_line_no = $.; } } else { @comments = (); $string = ""; } } next unless $in_text; next unless $string =~ /<\/$text_string/o; my $text = $string; $text =~ s/<\/$text_string.*$//o; if ( $text cmp "" ) { # See if the string should be excluded by trailing element-context. my $exclude_by_ectx = 0; my @rev_ectx = reverse @ectx; for my $ectx_tail (@{&ECTX_EXCLUDE}) { my @rev_ectx_tail = reverse @{$ectx_tail}; my $i = 0; $exclude_by_ectx = (@rev_ectx > 0 and @rev_ectx_tail > 0); while ($i < @rev_ectx and $i < @rev_ectx_tail) { my ($tag, $attr, $aval) = @{$rev_ectx_tail[$i]}; $exclude_by_ectx = (not $tag or ($tag eq $rev_ectx[$i][1])); if ($exclude_by_ectx and $attr) { $exclude_by_ectx = 0; for my $ectx_attr_aval (@{$rev_ectx[$i][2]}) { if ($attr eq $ectx_attr_aval->[0]) { $exclude_by_ectx = $aval ? $aval eq $ectx_attr_aval->[1] : 1; last; } } } last if not $exclude_by_ectx; ++$i; } last if $exclude_by_ectx; } if (($context and $context eq "KDE::DoNotExtract") or ($notr eq "true")) { push @comments, "Manually excluded message at $file_name line $."; } elsif ( $exclude_by_ectx ) { push @comments, "Automatically excluded message at $file_name line $."; } else { (my $norm_fname = $file_name) =~ s/^\.\///; push @comments, "i18n: file: $norm_fname:$."; if ( @ectx ) { # Format element-context. my @tag_gr; for my $tgr (reverse @ectx) { my @attr_gr; for my $agr ( @{$tgr->[2]} ) { #push @attr_gr, "$agr->[0]=$agr->[1]"; push @attr_gr, "$agr->[1]"; # no real nead for attribute name } my $attr = join(", ", @attr_gr); push @tag_gr, "$tgr->[1] ($attr)" if $attr; push @tag_gr, "$tgr->[1]" if not $attr; } my $ectx_str = join ", ", @tag_gr; push @comments, "i18n: ectx: $ectx_str"; } push @comments, "xgettext: no-c-format" if $text =~ /%/o; $dummy_call->($context, $text, @comments); @comments = (); } } else { push @comments, "Skipped empty message at $file_name line $."; } $string =~ s/^.*<\/$text_string//o; $in_text = 0; # Text can be multiline in .ui files (possibly), but we warn about it in XMLGUI .rc files. warn "there is floating in: '$file_name'" if $. != $start_line_no and $file_name =~ /\.rc$/i; } close $fh or warn "Failed to close: '$file_name': $!"; die "parsing error in $file_name" if $in_text; if ($ectx_ext && exists $EXTENSION_VERBATIM_TAGS{$ectx_ext}) { unless ( open $fh, "<", $file_name ) { # warn "Failed to open: '$file_name': $!"; next; } while ( <$fh> ) { chomp; $string .= "\n" . $_; for my $elspec (@{ $EXTENSION_VERBATIM_TAGS{$ectx_ext} }) { my ($tag, $attr, $aval) = @{$elspec}; my $rx; if ($attr and $aval) { $rx = qr/<$tag[^<]*$attr=["']$aval["'][^<]*>(.*)<\/$tag>/s } elsif ($attr) { $rx = qr/<$tag[^<]*$attr=[^<]*>(.*)<\/$tag>/s } else { $rx = qr/<$tag>(.*)<\/$tag>/s } if ($string =~ $rx) { # Add comment before any line that has an i18n substring in it. my @matched = split /\n/, $1; my $mlno = $.; (my $norm_fname = $file_name) =~ s/^\.\///; for my $mline (@matched) { # Assume verbatim code is in language given by --language. # Therefore format only comment, and write code line as-is. if ($mline =~ /i18n/) { $dummy_call->(undef, undef, ("i18n: file: $norm_fname:$mlno")); } $mline = unescape_xml($mline); print "$mline\n"; ++$mlno; } $string = ""; } } } close $fh or warn "Failed to close: '$file_name': $!"; } } kraft-0.97/src/filelicense.txt000066400000000000000000000017401410616450300163740ustar00rootroot00000000000000/*************************************************************************** - ------------------- begin : Son Nov 11 2010 copyright : (C) 2010 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ kraft-0.97/src/filterheader.cpp000066400000000000000000000064451410616450300165220ustar00rootroot00000000000000/*************************************************************************** filterheader.cpp ------------------- copyright : (C) 2005 by Cornelius Schumacher (C) 2005 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include "filterheader.h" #include #include #include #include #include #include #include #include #include #include #include FilterHeader::FilterHeader(QWidget *parent , QTreeWidget *listView) : QWidget( parent ), _treeWidget(listView) { QBoxLayout *filterLayout = new QHBoxLayout; setLayout(filterLayout); QLabel *label = new QLabel( i18n("&Search:")); filterLayout->addWidget( label ); mSearchLine = new QLineEdit( this ); mSearchLine->setClearButtonEnabled(true); label->setBuddy(mSearchLine); connect( mSearchLine, SIGNAL(textChanged(QString) ), SLOT( slotTextChanged(QString) ) ); filterLayout->addWidget( mSearchLine ); } void FilterHeader::slotTextChanged( const QString& filter ) { if( ! _treeWidget ) { return; } QTreeWidgetItemIterator it(_treeWidget); while (*it) { // items without parent are root items. Never hide. QTreeWidgetItem *item = (*it); if( item->parent() ) { bool showIt = false; for(int i = 0; !showIt && i < item->columnCount(); i++) { if( item->text(i).contains(filter, Qt::CaseInsensitive)) { showIt = true; } } item->setHidden(!showIt); if( showIt && ! filter.isEmpty() ) { // Make sure that all the parent items are visible too QTreeWidgetItem *parent = nullptr, *child = item; while((parent = child->parent()) != nullptr) { parent->setHidden(false); if( !parent->isExpanded() ) { parent->setExpanded(true); _openedItems[parent] = 1; } child = parent; } } if (filter.isEmpty()) { for( auto item : _openedItems.uniqueKeys()) { item->setExpanded(false); } _openedItems.clear(); } } ++it; } } void FilterHeader::setListView( QTreeWidget* view ) { _treeWidget = view; } void FilterHeader::clear() { mSearchLine->clear(); } kraft-0.97/src/filterheader.h000066400000000000000000000031711410616450300161600ustar00rootroot00000000000000/*************************************************************************** filterheader.h ------------------- copyright : (C) 2005 by Cornelius Schumacher (C) 2005 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef FILTERHEADER_H #define FILTERHEADER_H #include #include #include #include "kraftcat_export.h" class QTreeWidget; class QLabel; class QString; class KRAFTCAT_EXPORT FilterHeader : public QWidget { Q_OBJECT public: FilterHeader(QWidget *parent = 0, QTreeWidget *tree = 0); public slots: void clear(); void setListView( QTreeWidget* ); private slots: void slotTextChanged( const QString& filter ); private: QLineEdit *mSearchLine; QLabel *mTitleLabel; QTreeWidget *_treeWidget; QHash _openedItems; }; #endif kraft-0.97/src/fixcalcdialog.cpp000066400000000000000000000045251410616450300166520ustar00rootroot00000000000000/*************************************************************************** zeitcalcdialog - ------------------- begin : 2004-23-09 copyright : (C) 2004 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ // include files for Qt #include #include // include files for KDE #include #include "fixcalcdialog.h" #include "fixcalcpart.h" #include "stdsatzman.h" #include "defaultprovider.h" FixCalcDialog::FixCalcDialog(QWidget *parent) :CalcDialogBase(parent), _fixWidget(new Ui_calcdetailFix), m_part(0) { setWindowTitle( i18n("Calculation Fix Item")); _fixWidget->setupUi(_centralWidget); _fixWidget->m_inpPreis->setSuffix( DefaultProvider::self()->currencySymbol() ); } void FixCalcDialog::setCalcPart( FixCalcPart *cp ) { if( ! cp ) return; m_part = cp; _fixWidget->m_nameEdit->setText( cp->getName()); _fixWidget->m_inpMenge->setValue( cp->getMenge()); _fixWidget->m_inpPreis->setValue(cp->unitPreis().toDouble()); } void FixCalcDialog::accept() { if( m_part ) { m_part->setMenge( _fixWidget->m_inpMenge->value() ); m_part->setName( _fixWidget->m_nameEdit->text()); m_part->setUnitPreis(Geld(_fixWidget->m_inpPreis->value())); } if( m_part && m_part->isDirty() ) { emit fixCalcPartChanged(m_part); } CalcDialogBase::accept(); } QString FixCalcDialog::getName() { return _fixWidget->m_nameEdit->text(); } double FixCalcDialog::getMenge() { return _fixWidget->m_inpMenge->value(); } double FixCalcDialog::getPreis() { return _fixWidget->m_inpPreis->value(); } /* END */ kraft-0.97/src/fixcalcdialog.h000066400000000000000000000030561410616450300163150ustar00rootroot00000000000000/*************************************************************************** fixcalcdialog - ------------------- begin : 2004-23-09 copyright : (C) 2004 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef _FIXCALCDIALOG_H #define _FIXCALCDIALOG_H #include #include "calcdialogbase.h" #include "ui_fixpartui.h" // designer file zeitpartui.ui /** * */ class FixCalcPart; class FixCalcDialog : public CalcDialogBase { Q_OBJECT public: FixCalcDialog(QWidget *parent=0); QString getName(); double getMenge(); double getPreis(); void setCalcPart( FixCalcPart* ); signals: void fixCalcPartChanged(FixCalcPart*); protected slots: void accept(); private: Ui_calcdetailFix *_fixWidget; FixCalcPart *m_part; }; #endif /* END */ kraft-0.97/src/fixcalcpart.cpp000066400000000000000000000034051410616450300163550ustar00rootroot00000000000000/*************************************************************************** fixcalcpart.cpp - ------------------- begin : Don Jan 1 2004 copyright : (C) 2004 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include "fixcalcpart.h" FixCalcPart::FixCalcPart() :CalcPart(), m_amount( 0 ) { } FixCalcPart::FixCalcPart(QString name, Geld preis, int prozent ) : CalcPart(name, prozent), m_fixPreis(preis), m_amount(1.0) { // setProzentPlus(prozent); } void FixCalcPart::setMenge( double val ) { if( val != m_amount ) { m_amount = val; setDirty(true); } } QString FixCalcPart::getType() const { return KALKPART_FIX; } Geld FixCalcPart::unitPreis() { return m_fixPreis; } void FixCalcPart::setUnitPreis( Geld g ) { if( g != m_fixPreis ) { m_fixPreis = g; setDirty(true); } } FixCalcPart::~FixCalcPart() { } Geld FixCalcPart::basisKosten() { Geld g = m_fixPreis; g = (Geld) m_fixPreis*m_amount; return g; } kraft-0.97/src/fixcalcpart.h000066400000000000000000000031521410616450300160210ustar00rootroot00000000000000/*************************************************************************** fixcalcpart.h - ------------------- begin : Don Jan 1 2004 copyright : (C) 2004 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef FIXCALCPART_H #define FIXCALCPART_H #include "calcpart.h" /**Implementiert einen fixen Betrag pro kalkulierter Einheit. *@author Klaas Freitag */ class FixCalcPart : public CalcPart { public: FixCalcPart(); FixCalcPart( QString name, Geld preis, int prozent = 0); ~FixCalcPart(); virtual Geld basisKosten(); /* der Preis fr eine Einheit */ Geld unitPreis(); void setUnitPreis( Geld ); void setMenge(double); double getMenge() const { return m_amount; } QString getType() const; private: // Private attributes /** */ Geld m_fixPreis; double m_amount; }; #endif kraft-0.97/src/fixpartui.ui000066400000000000000000000075011410616450300157240ustar00rootroot00000000000000 calcdetailFix 0 0 321 137 Calculation Parts Fix <h1>Fix Cost Parts</h1> false Add a fix cost for one unit of the template: Qt::AlignVCenter false &Label: false m_nameEdit describing text amortisation &Amount: false m_inpMenge amount multiplier 99999.000000000000000 1.000000000000000 at &Price: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter false m_inpPreis Price for one piece 99999.000000000000000 10.000000000000000 999999 m_nameEdit m_inpMenge m_inpPreis kraft-0.97/src/floskel.cpp000066400000000000000000000020601410616450300155100ustar00rootroot00000000000000/*************************************************************************** floskel.cpp - ------------------- begin : Mit Dez 31 2003 copyright : (C) 2003 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include "floskel.h" Floskel::Floskel(){ } Floskel::~Floskel(){ } kraft-0.97/src/floskel.h000066400000000000000000000023611410616450300151610ustar00rootroot00000000000000/*************************************************************************** floskel.h - ------------------- begin : Mit Dez 31 2003 copyright : (C) 2003 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef FLOSKEL_H #define FLOSKEL_H #include #include "kraftglobals.h" #include "einheit.h" /** *@author Klaas Freitag */ class Floskel { public: Floskel(); ~Floskel(); private: QString text; Einheit einheit; }; #endif kraft-0.97/src/floskeltemplate.cpp000066400000000000000000000275271410616450300172630ustar00rootroot00000000000000/*************************************************************************** floskeltemplate.cpp - ------------------- begin : Don Jan 1 2004 copyright : (C) 2004 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include #include #include #include "kraftdb.h" #include "templatesaverbase.h" #include "templatesaverdb.h" #include "floskeltemplate.h" #include "unitmanager.h" #include "calcpart.h" #include "materialcalcpart.h" #include "fixcalcpart.h" #include "timecalcpart.h" #include "stockmaterial.h" FloskelTemplate::FloskelTemplate() : CatalogTemplate(), mTemplId(-1), m_chapter(0), mTimeAdd(true), m_listViewItem(0), m_saver(0) { m_calcType = Calculation; } FloskelTemplate::FloskelTemplate( int tID, const QString& text, int einheit, int chapter, int calcKind ) : CatalogTemplate(), mTemplId(tID), m_chapter(chapter), mTimeAdd(true), m_preis(long(0)), m_listViewItem(0), m_saver(0) { if( calcKind == 1 ) { setCalculationType( ManualPrice ); } else if( calcKind == 2 ) { setCalculationType( Calculation ); } else if ( calcKind == 3 ) { setCalculationType( AutoCalc ); } setText( text ); this->setUnitId(einheit); setChapterId( dbID(chapter), false ); } FloskelTemplate::FloskelTemplate( FloskelTemplate& templ ) : CatalogTemplate( templ ), mTemplId( templ.mTemplId ), m_preis( templ.m_preis ), m_listViewItem(templ.m_listViewItem ), m_saver( 0 ) { deepCopyCalcParts( templ ); setModifyDate( templ.modifyDate() ); setEnterDate( templ.enterDate() ); setText( templ.getText() ); setUnitId(templ.unit().id()); // m_calcParts.setAutoDelete(true); } FloskelTemplate& FloskelTemplate::operator= ( FloskelTemplate& src ) { if ( this == &src ) return *this; mText = src.mText; setUnitId(src.unit().id()); mTemplId = src.mTemplId; mChapterId = src.mChapterId; m_preis = src.m_preis; m_listViewItem = src.m_listViewItem; m_saver = 0; // src.m_saver; deepCopyCalcParts( src ); return *this; } FloskelTemplate::~FloskelTemplate() { delete m_saver; } void FloskelTemplate::deepCopyCalcParts( FloskelTemplate& templ ) { CalcPart *cp = 0; m_calcParts.clear(); QListIterator i( templ.m_calcParts ); while( i.hasNext()) { cp = i.next(); CalcPart *ncp = 0; if( cp->getType() == KALKPART_TIME ) { ncp = new TimeCalcPart( *( static_cast(cp) ) ); } else if( cp->getType() == KALKPART_FIX ) { ncp = new FixCalcPart( *( static_cast(cp) ) ); } else if( cp->getType() == KALKPART_MATERIAL ) { ncp = new MaterialCalcPart( *( static_cast(cp) ) ); } else { // qDebug () << "ERROR: Unknown Calculation-Type!" << endl; } m_calcParts.append( ncp ); } } void FloskelTemplate::setBenefit( double g ) { /* Every calc part has an value for benefit. Set the benefit value for each calc part, later on each can have its own value */ for( auto *cp: m_calcParts) { cp->setProzentPlus(g); } } double FloskelTemplate::getBenefit( ) { bool first = true; double b = 0.0; for( auto *cp: m_calcParts) { if( first ) { b = cp->getProzentPlus(); first = false; } // all benefits are the same atm, thus this ASSERT. Q_ASSERT( fabs(b - cp->getProzentPlus()) < std::numeric_limits::epsilon()); } return b; } void FloskelTemplate::setTemplID( int newID ) { mTemplId = newID; } Geld FloskelTemplate::unitPrice() { return calcPreis(); } Geld FloskelTemplate::calcPreis() { Geld g; if( calcKind() == ManualPrice ) { g = m_preis; } else { g = m_calcParts.calcPrice(); double b = getBenefit(); g += g.percent(b); } return g; } CalcPartList FloskelTemplate::getCalcPartsList() { return getCalcPartsList(ALL_KALKPARTS); } // Returns a calcpartlist where all calcparts have lost their connection // to the database from the dbID POV. That's needed for the transition // from template -> document calculations. CalcPartList FloskelTemplate::decoupledCalcPartsList() { return m_calcParts.decoupledCalcPartsList(); } CalcPartList FloskelTemplate::getCalcPartsList( const QString& calcPart ) { return m_calcParts.getCalcPartsList( calcPart ); } void FloskelTemplate::addCalcPart( CalcPart* cpart ) { m_calcParts.append(cpart); } void FloskelTemplate::removeCalcPart( CalcPart *cpart ) { if( cpart) { cpart->setToDelete(true); cpart->setDirty(true); } } void FloskelTemplate::clearCalcParts() { for(int i=0; isaveTemplate( this ); } else { // qDebug () << "ERR: No saver available!" << endl; return false; } } void FloskelTemplate::saveChapterId() { TemplateSaverBase *saver = getSaver(); if( saver ) { saver->saveTemplateChapter( this ); } } #if 0 QDomElement FloskelTemplate::toXML( QDomDocument& doc) { QDomElement templ = doc.createElement("template"); templ.appendChild( createDomNode(doc, "unit", getUnit().einheitSingular())); templ.appendChild( createDomNode(doc, "text", getText())); templ.appendChild( createDomNode(doc, "id", QString::number(getTemplID()))); templ.appendChild( createDomNode(doc, "benefit", QString::number(getBenefit()))); templ.appendChild( createDomNode(doc, "timecount", hasTimeslice() ? "yes": "no" )); QDomElement calcParts = doc.createElement( "calcParts" ); templ.appendChild(calcParts); fixPartsToXML(doc, calcParts); timePartsToXML(doc, calcParts); materialPartsToXML(doc, calcParts); /* Material Calculation Parts */ materialPartsToXML(doc); CalcPartList tpList = getCalcPartsList(KALKPART_MATERIAL); MaterialCalcPart *mc = 0; mc = static_cast(tpList.first()); for( ; mc; mc = static_cast(tpList.next()) ) { QDomElement calcPart = doc.createElement( "MaterialCalcpart" ); calcParts.appendChild(calcPart); calcPart.appendChild(createDomNode(doc, "name", mc->getName())); calcPart.appendChild(createDomNode(doc, "dbid", mc->getDbID().toString())); StockMaterialList materials = mc->getCalcMaterialList(); StockMaterialListIterator it( materials ); StockMaterial *mat=0; while ( (mat = it.current()) != 0 ) { ++it; QDomElement matElem = doc.createElement("Material"); matElem.appendChild(createDomNode(doc, "MatName", mat->getName())); QString h; h = h.setNum(mc->getCalcAmount(mat)); matElem.appendChild(createDomNode(doc, "Amount", h)); matElem.appendChild(createDomNode(doc, "PriceSum", mc->getPriceForMaterial(mat).toString())); matElem.appendChild(createDomNode(doc, "CostSum", mc->getCostsForMaterial(mat).toString())); matElem.appendChild(createDomNode(doc, "Price", mat->salesPrice().toString())); matElem.appendChild(createDomNode(doc, "Cost", mat->purchPrice().toString())); Einheit e = mat->getUnit(); h = e.einheitSingular(); matElem.appendChild(createDomNode(doc, "Unit", h)); calcPart.appendChild(matElem); } } return templ; } void FloskelTemplate::fixPartsToXML( QDomDocument& doc, QDomElement& calcParts ) { CalcPartList tpList = getCalcPartsList(KALKPART_FIX); FixCalcPart *fc = 0; fc = static_cast(tpList.first()); for( ; fc; fc = static_cast(tpList.next()) ) { QDomElement calcPart = doc.createElement("FixCalcPart"); calcParts.appendChild(calcPart); calcPart.appendChild(createDomNode(doc, "name", fc->getName())); calcPart.appendChild(createDomNode(doc, "dbid", fc->getDbID().toString())); QString h; h.setNum(fc->getMenge()); calcPart.appendChild(createDomNode(doc, "amount", h)); Geld g = fc->unitPreis(); calcPart.appendChild(createDomNode(doc, "price", g.toString( mLocale ))); } } void FloskelTemplate::timePartsToXML( QDomDocument& doc, QDomElement& calcParts ) { CalcPartList tpList = getCalcPartsList(KALKPART_TIME); TimeCalcPart *tc = 0; tc = static_cast(tpList.first()); for( ; tc; tc = static_cast(tpList.next()) ) { QDomElement calcPart = doc.createElement("TimeCalcPart"); calcParts.appendChild(calcPart); calcPart.appendChild(createDomNode(doc, "name", tc->getName())); calcPart.appendChild(createDomNode(doc, "dbid", tc->getDbID().toString())); QString h; h.setNum( tc->getMinuten()); calcPart.appendChild(createDomNode(doc, "minutes", h)); StdSatz ss = tc->getStundensatz(); calcPart.appendChild(createDomNode(doc, "stundensatz", ss.getName())); calcPart.appendChild(createDomNode(doc, "globalHourSetup", tc->globalStdSetAllowed() ? "yes" : "no")); } } void FloskelTemplate::materialPartsToXML( QDomDocument& doc, QDomElement& calcParts ) { /* Material Calculation Parts */ CalcPartList tpList = getCalcPartsList(KALKPART_MATERIAL); MaterialCalcPart *mc = 0; mc = static_cast(tpList.first()); for( ; mc; mc = static_cast(tpList.next()) ) { QDomElement calcPart = doc.createElement( "MaterialCalcpart" ); calcParts.appendChild(calcPart); calcPart.appendChild(createDomNode(doc, "name", mc->getName())); calcPart.appendChild(createDomNode(doc, "dbid", mc->getDbID().toString())); StockMaterialList materials = mc->getCalcMaterialList(); StockMaterialListIterator it( materials ); StockMaterial *mat=0; while ( (mat = it.current()) != 0 ) { ++it; QDomElement matElem = doc.createElement("Material"); matElem.appendChild(createDomNode(doc, "MaterialName", mat->name())); QString h; h = h.setNum(mc->getCalcAmount(mat)); matElem.appendChild(createDomNode(doc, "Amount", h)); matElem.appendChild(createDomNode(doc, "Price", mc->getCostsForMaterial(mat).toString())); Einheit e = mat->getUnit(); h = e.einheitSingular(); matElem.appendChild(createDomNode(doc, "Unit", h)); calcPart.appendChild(matElem); } } } QDomElement FloskelTemplate::createDomNode( QDomDocument doc, const QString& name, const QString& value) { QDomElement elem = doc.createElement(name); QDomText text = doc.createTextNode(value); elem.appendChild(text); return elem; } #endif kraft-0.97/src/floskeltemplate.h000066400000000000000000000066511410616450300167230ustar00rootroot00000000000000/*************************************************************************** floskeltemplate.h - ------------------- begin : Don Jan 1 2004 copyright : (C) 2004 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef FLOSKELTEMPLATE_H #define FLOSKELTEMPLATE_H #include #include #include #include #include "kraftglobals.h" #include "einheit.h" #include "calcpart.h" #include "catalogtemplate.h" /** *@author Klaas Freitag */ class QTreeWidgetItem; class TemplateSaverBase; class QDomDocument; class QDomElement; class FloskelTemplate: public CatalogTemplate { public: FloskelTemplate(); FloskelTemplate(int tID, const QString& text, int einheit, int chapter, int calcKind ); FloskelTemplate( FloskelTemplate& ); virtual ~FloskelTemplate(); /** No descriptions */ void setManualPrice( Geld p ) { m_preis = p; } Geld manualPrice() { return m_preis; } Geld unitPrice(); Geld costsByCalcPart( const QString& part); void addCalcPart( CalcPart* cpart ); void removeCalcPart( CalcPart *cpart ); void clearCalcParts(); CalcPartList getCalcPartsList(); CalcPartList getCalcPartsList(const QString& ); CalcPartList decoupledCalcPartsList(); void setBenefit( double ); double getBenefit(); int getTemplID() { return mTemplId; } void setTemplID( int ); bool hasTimeslice() { return mTimeAdd; } void setHasTimeslice(bool ts) { mTimeAdd = ts; } void setListViewItem( QTreeWidgetItem *it ) { m_listViewItem = it; } QTreeWidgetItem* getListViewItem() { return m_listViewItem; } virtual bool save(); // virtual QDomElement toXML( QDomDocument&); FloskelTemplate& operator= ( FloskelTemplate& ); protected: virtual TemplateSaverBase* getSaver(); virtual void deepCopyCalcParts( FloskelTemplate& ); void saveChapterId(); private: // Private methods #if 0 QDomElement createDomNode( QDomDocument, const QString&, const QString&); void materialPartsToXML( QDomDocument&, QDomElement& ); void fixPartsToXML( QDomDocument&, QDomElement& ); void timePartsToXML( QDomDocument&, QDomElement& ); #endif virtual Geld calcPreis(); int mTemplId; // Database ID int m_chapter; CalcPartList m_calcParts; bool mTimeAdd; Geld m_preis; // preis only valid for manual calculation. QTreeWidgetItem *m_listViewItem; TemplateSaverBase *m_saver; /** */ }; class FloskelTemplateList :public QList { public: FloskelTemplateList() { } }; typedef QListIterator FloskelTemplateListIterator; #endif kraft-0.97/src/flostempldialog.cpp000066400000000000000000000600741410616450300172470ustar00rootroot00000000000000/*************************************************************************** flostempldialog - dialog to edit templates ------------------- begin : 2004-15-08 copyright : (C) 2004 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ // include files for Qt #include #include #include #include #include #include #include #include #include #include #include #include // include files for KDE #include #include #include #include #include "floskeltemplate.h" #include "catalogtemplate.h" #include "flostempldialog.h" #include "unitmanager.h" #include "timecalcpart.h" #include "fixcalcpart.h" #include "portal.h" #include "materialcalcpart.h" #include "matcalcdialog.h" #include "stockmaterial.h" #include "timecalcdialog.h" #include "fixcalcdialog.h" #include "stdsatzman.h" #include "katalogman.h" #include "katalog.h" #include "materialselectdialog.h" #include "defaultprovider.h" FlosTemplDialog::FlosTemplDialog( QWidget *parent, bool modal ) : QDialog( parent ), m_template(0), m_katalog(0), m_fixCalcDia(0), m_timePartDialog(0), m_matPartDialog(0) { QWidget *w = new QWidget( this ); QVBoxLayout *mainLayout = new QVBoxLayout; setLayout(mainLayout); mainLayout->addWidget(w); setupUi( w ); setWindowTitle( i18n("Create or Edit Template Items") ); setModal( modal ); _buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); QPushButton *okButton = _buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(_buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(_buttonBox, SIGNAL(rejected()), this, SLOT(reject())); mainLayout->addWidget(_buttonBox); okButton->setDefault(true); //Initialise the buttongroup to switch between manual and calculated price m_gbPriceSrc = new QButtonGroup(this); m_gbPriceSrc->addButton(m_rbManual, 0); m_gbPriceSrc->addButton(m_rbCalculation, 1); m_timeParts->header()->setResizeMode(QHeaderView::ResizeToContents); m_fixParts->header()->setResizeMode(QHeaderView::ResizeToContents); m_matParts->header()->setResizeMode(QHeaderView::ResizeToContents); // disable for now, not used cbMwst->setVisible(false); m_mwstLabel->setVisible(false); setupConnections(); setButtonIcons(); } void FlosTemplDialog::setupConnections() { connect(m_gbPriceSrc, SIGNAL(buttonClicked(int)), this, SLOT(slCalcOrFix(int))); /* connect a value Changed signal of the manual price field */ connect( m_manualPriceVal, SIGNAL( valueChanged(double)), this, SLOT( slManualPriceChanged(double))); connect( m_text, SIGNAL(textChanged()),this, SLOT(slSetNewText())); connect( spBenefit, SIGNAL(valueChanged(int)), this, SLOT(slBenefitChange(int))); //Time calculation connect(m_butAddTime, SIGNAL(clicked()), this, SLOT(slAddTimePart())); connect(m_butEditTime, SIGNAL(clicked()), this, SLOT(slEditTimePart())); connect(m_butRemoveTime, SIGNAL(clicked()), this, SLOT(slRemoveTimePart())); //Fix costs connect(m_butAddFix, SIGNAL(clicked()), this, SLOT(slAddFixPart())); connect(m_butEditFix, SIGNAL(clicked()), this, SLOT(slEditFixPart())); connect(m_butRemoveFix, SIGNAL(clicked()), this, SLOT(slRemoveFixPart())); //Material connect(m_butAddMat, SIGNAL(clicked()), this, SLOT(slAddMatPart())); connect(m_butEditMat, SIGNAL(clicked()), this, SLOT(slEditMatPart())); connect(m_butRemoveMat, SIGNAL(clicked()), this, SLOT(slRemoveMatPart())); } void FlosTemplDialog::setButtonIcons() { m_butAddTime->setIcon(QIcon::fromTheme("list-add")); m_butEditTime->setIcon(QIcon::fromTheme("document-edit")); m_butRemoveTime->setIcon(QIcon::fromTheme("list-remove")); m_butAddFix->setIcon(QIcon::fromTheme("list-add")); m_butEditFix->setIcon(QIcon::fromTheme("document-edit")); m_butRemoveFix->setIcon(QIcon::fromTheme("list-remove")); m_butAddMat->setIcon(QIcon::fromTheme("list-add")); m_butEditMat->setIcon(QIcon::fromTheme("document-edit")); m_butRemoveMat->setIcon(QIcon::fromTheme("list-remove")); } void FlosTemplDialog::setTemplate( FloskelTemplate *t, const QString& katalogname, bool newTempl ) { if( ! t ) return; m_template = t; m_templateIsNew = newTempl; m_katalog = KatalogMan::self()->getKatalog(katalogname); if( m_katalog == 0 ) { // qDebug () << "ERR: Floskel Dialog called without valid Katalog!" << endl; return; } QList chapters = m_katalog->getKatalogChapters( ); QStringList chapNames; foreach( CatalogChapter chap, chapters ) { chapNames.append( chap.name() ); } cbChapter->insertItems(-1, chapNames ); int chapID = t->chapterId().toInt(); QString chap = m_katalog->chapterName(dbID(chapID)); cbChapter->setCurrentIndex(cbChapter->findText( chap )); m_manualPriceVal->setSuffix(m_katalog->locale()->currencySymbol()); /* Text of the template */ m_text->setText( t->getText()); /* Unit */ m_unit->clear(); m_unit->insertItems(-1, UnitManager::self()->allUnits()); m_unit->setCurrentIndex(m_unit->findText( m_template->unit().einheitSingular() )); m_manualPriceVal->setValue( t->unitPrice().toDouble()); /* Kind of Calculation: Manual or calculated? */ _origCalcType = m_template->calcKind(); if( t->calcKind() == CatalogTemplate::ManualPrice ) { slCalcOrFix(0); m_rbManual->setChecked(true); m_rbCalculation->setChecked(false); } else { slCalcOrFix(1); m_rbManual->setChecked(false); m_rbCalculation->setChecked(true); } /* set up the different calculation parts */ setCalcparts(); _calcPartsModified = false; /* set text */ slSetNewText(); m_addTime->setChecked( m_template->hasTimeslice() ); m_text->setFocus(); m_text->selectAll(); } void FlosTemplDialog::setCalcparts( ) { /* time calculation in widget m_timeParts */ CalcPartList tpList = m_template->getCalcPartsList( KALKPART_TIME ); m_timeParts->clear(); QListIterator it( tpList ); while( it.hasNext() ) { TimeCalcPart *cp = static_cast(it.next()); QString stdStd = i18n("No"); if( cp->globalStdSetAllowed() ) stdStd = i18n("Yes"); QTreeWidgetItem *lvItem = new QTreeWidgetItem( m_timeParts ); drawTimeListEntry( lvItem, cp ); mCalcPartDict.insert(lvItem, cp ); } /* Fix calculation parts */ m_fixParts->clear(); tpList = m_template->getCalcPartsList( KALKPART_FIX ); QListIterator fixIt( tpList ); while( fixIt.hasNext() ) { FixCalcPart *fc = static_cast(fixIt.next()); QTreeWidgetItem *lvItem = new QTreeWidgetItem( m_fixParts ); drawFixListEntry( lvItem, fc ); mCalcPartDict.insert( lvItem, fc ); } /* Material calculation */ m_matParts->clear(); tpList = m_template->getCalcPartsList( KALKPART_MATERIAL ); QListIterator matIt( tpList ); while( matIt.hasNext() ) { MaterialCalcPart *mc = static_cast(matIt.next()); QTreeWidgetItem *lvItem = new QTreeWidgetItem( m_matParts ); mCalcPartDict.insert( lvItem, mc ); drawMatListEntry( lvItem, mc ); } } void FlosTemplDialog::refreshPrices() { if( ! m_template ) return; /* assemble the pricing label */ QString t; t = i18n("Calculated Price: "); if( m_template->calcKind() == CatalogTemplate::ManualPrice ) { t = i18n("Manual Price: "); } else if( m_template->calcKind() == CatalogTemplate::Calculation ) { int benefit = spBenefit->value(); QString benefitStr = i18n("(+%1%)", benefit); if( benefit < 0 ) { benefitStr = QLatin1String("")+i18n("%1%", benefit)+QLatin1String(""); } benefitStr += i18n(": "); t += benefitStr; } else { // qDebug () << "ERR: unknown calculation type!" << endl; } m_resPreisName->setText(t); m_resPreisName->setTextFormat(Qt::RichText); /* set Price */ t = m_template->unitPrice().toString(); m_resultPrice->setText( t ); m_manualPriceVal->setValue( m_template->unitPrice().toDouble() ); /* Price parts per calculation part */ Geld g( m_template->costsByCalcPart( KALKPART_TIME )); m_textTimePart->setText( g.toString()); g = m_template->costsByCalcPart( KALKPART_FIX ); m_textFixPart->setText( g.toString()); g = m_template->costsByCalcPart( KALKPART_MATERIAL ); m_textMaterialPart->setText(g.toString()); // Benefit double b = m_template->getBenefit(); spBenefit->setValue( qRound(b)); } FlosTemplDialog::~FlosTemplDialog( ) { delete m_fixCalcDia; delete m_timePartDialog; delete m_matPartDialog; } // Check if the template was modified in the dialog bool FlosTemplDialog::templModified() { bool modified = false; QString str = m_text->toPlainText(); modified = str != m_template->getText(); modified = modified || (m_unit->currentText() != m_template->unit().einheitSingular()); modified = modified || (m_addTime->isChecked() != m_template->hasTimeslice()); str = spBenefit->cleanText(); bool b; int new_val = str.toInt(&b); modified = modified || ( b && new_val != qRound(m_template->getBenefit())); // calculation kind CatalogTemplate::CalculationType currCalcType = m_template->calcKind(); modified = modified || (currCalcType != _origCalcType); modified = modified || _calcPartsModified; return modified; } void FlosTemplDialog::accept() { if( m_template ) { // qDebug () << "Saving template ID " << m_template->getTemplID() << endl; QString h; h = m_text->toPlainText(); if( h != m_template->getText() ) { // qDebug () << "Template Text dirty -> update" << endl; m_template->setText( h ); } h = m_unit->currentText(); if( h != m_template->unit().einheitSingular()) { // qDebug () << "Template Einheit dirty -> update to " << h << endl; m_template->setUnitId( UnitManager::self()->getUnitIDSingular(h)); } /* count time */ bool c = m_addTime->isChecked(); if( c != m_template->hasTimeslice() ) { m_template->setHasTimeslice(c); } /* benefit */ h = spBenefit->cleanText(); bool b; int new_val = h.toInt(&b); if( b && new_val != qRound(m_template->getBenefit())) { m_template->setBenefit(new_val); // qDebug () << "benefit dirty ->update to " << g << endl; } // Calculationtype int selId = m_gbPriceSrc->checkedId(); CatalogTemplate::CalculationType calcType = CatalogTemplate::Unknown; if( selId == 0 ) { calcType = CatalogTemplate::ManualPrice; } else if( selId == 1 ) { calcType = CatalogTemplate::Calculation; } else { // qDebug () << "ERROR: Calculation type not selected, id is " << selId << endl; } m_template->setCalculationType( calcType ); // reread the manual price double dd = m_manualPriceVal->value(); m_template->setManualPrice( Geld( dd ) ); h = cbChapter->currentText(); // qDebug () << "catalog chapter is " << h << endl; _calcPartsModified = false; if( m_template->save() ) { emit( editAccepted( m_template ) ); KatalogMan::self()->notifyKatalogChange( m_katalog, m_template->getTemplID() ); } else { QMessageBox::warning(0, i18n("Template Error"), i18n("Saving of this template failed, sorry")); } } // qDebug () << "*** Saving finished " << endl; QDialog::accept(); } void FlosTemplDialog::reject() { if(confirmClose() == true) { // let QDialog clean away the dialog. QDialog::reject(); } } void FlosTemplDialog::closeEvent ( QCloseEvent * event ) { if(confirmClose() == false) event->ignore(); else event->accept(); } bool FlosTemplDialog::confirmClose() { if( templModified() ) { QMessageBox msgBox; msgBox.setText(i18n("The template has been modified.")); msgBox.setInformativeText(i18n("Do you want to discard your changes?")); msgBox.setStandardButtons(QMessageBox::Discard | QMessageBox::Cancel); msgBox.setDefaultButton(QMessageBox::Cancel); int ret = msgBox.exec(); if( ret == QMessageBox::Cancel ) { return false; } } mCalcPartDict.clear(); m_timeParts->clear (); m_fixParts->clear (); m_matParts->clear (); m_katalog->reload(m_template->getTemplID()); if ( m_templateIsNew ) { // remove the listview item if it was created newly emit editRejected(); } return true; } bool FlosTemplDialog::askChapterChange( FloskelTemplate*, int ) { QMessageBox msgBox; msgBox.setText(i18n( "The catalog chapter was changed for this template.\n" "Do you really want to move the template to the new chapter?")); msgBox.setInformativeText(i18n("Chapter Change")); msgBox.setStandardButtons(QMessageBox::Yes| QMessageBox::No); msgBox.setDefaultButton(QMessageBox::Yes); int ret = msgBox.exec(); return( ret == QMessageBox::Yes); } void FlosTemplDialog::slManualPriceChanged(double dd) { qDebug () << "Changing manual price:" << dd << endl; if( ! m_template ) return; // qDebug () << "Updating manual price!" << endl; m_template->setManualPrice( Geld( dd )); refreshPrices(); } double FlosTemplDialog::benefitValue() { double val = 0; CalcPartList cparts = m_template->getCalcPartsList(); // Currently still all calcparts have the same value of benefit. // once this changes, this needs to be fixed accordingly. if( cparts.size() > 0 ) { val = cparts.at(0)->getProzentPlus(); } return val; } void FlosTemplDialog::slBenefitChange( int newBen ) { int oldBen = qRound(m_template->getBenefit()); if( oldBen != newBen ) { m_template->setBenefit(newBen); refreshPrices(); _calcPartsModified = true; } } void FlosTemplDialog::slAddFixPart() { if( ! m_template ) return; FixCalcDialog dia(this); if( dia.exec() == QDialog::Accepted ) { FixCalcPart *cp = new FixCalcPart( dia.getName(), dia.getPreis()); cp->setMenge( dia.getMenge()); cp->setProzentPlus(benefitValue()); cp->setDirty(true); QTreeWidgetItem *lvItem = new QTreeWidgetItem( m_fixParts); drawFixListEntry( lvItem, cp ); mCalcPartDict.insert( lvItem, cp ); m_template->addCalcPart( cp ); refreshPrices(); _calcPartsModified = true; } } void FlosTemplDialog::slRemoveFixPart() { if( ! m_template || ! m_fixParts ) return; QTreeWidgetItem *item = m_fixParts->currentItem(); if( item ) { CalcPart *cp = mCalcPartDict[item]; if( cp ) { m_template->removeCalcPart(cp); } delete item; refreshPrices(); _calcPartsModified = true; } } void FlosTemplDialog::slEditFixPart() { if( ! m_template || !m_fixParts ) return; // qDebug () << "Edit fix part!" << endl; QTreeWidgetItem *item = m_fixParts->currentItem(); if( item ) { FixCalcPart *cp = static_cast(mCalcPartDict[item]); if( cp ) { m_fixCalcDia = new FixCalcDialog(this); m_fixCalcDia->setCalcPart(cp); connect( m_fixCalcDia, SIGNAL(fixCalcPartChanged(FixCalcPart*)), this, SLOT(slFixCalcPartChanged(FixCalcPart*))); m_fixCalcDia->show(); } } } void FlosTemplDialog::slFixCalcPartChanged(FixCalcPart *cp) { refreshPrices(); _calcPartsModified = true; drawFixListEntry(m_fixParts->currentItem(), cp); } void FlosTemplDialog::slTimeCalcPartChanged(TimeCalcPart *cp) { refreshPrices(); _calcPartsModified = true; drawTimeListEntry(m_timeParts->currentItem(), cp); } void FlosTemplDialog::slAddTimePart() { if( ! m_template ) return; TimeCalcDialog dia(this); if( dia.exec() == QDialog::Accepted ) { TimeCalcPart *cp = new TimeCalcPart( dia.getName(), dia.getDauer(), TimeCalcPart::timeUnitFromString(dia.unitStr()), 0 ); cp->setGlobalStdSetAllowed( dia.allowGlobal()); StdSatz std = StdSatzMan::self()->getStdSatz( dia.getStundensatzName()); cp->setStundensatz( std ); cp->setProzentPlus(benefitValue()); QTreeWidgetItem *lvItem = new QTreeWidgetItem( m_timeParts); drawTimeListEntry( lvItem, cp ); mCalcPartDict.insert( lvItem, cp ); m_template->addCalcPart( cp ); refreshPrices(); _calcPartsModified = true; } } /* * stellt einen TimeCalcPart als ListViewItem dar. Wird gebraucht, wenn das * item neu ist, aber auch beim Editieren */ void FlosTemplDialog::drawTimeListEntry( QTreeWidgetItem *it, TimeCalcPart *cp ) { if( !( it && cp) ) return; it->setText( 0, cp->getName()); it->setText( 1, QString::number(cp->duration()) + QLatin1Char(' ') + TimeCalcPart::timeUnitString(cp->timeUnit())); it->setText( 2, cp->getStundensatz().getName()); it->setText( 3, cp->globalStdSetAllowed() ? i18n("Yes") : i18n("No")); } void FlosTemplDialog::drawFixListEntry( QTreeWidgetItem* it, FixCalcPart *cp ) { if( !( it && cp) ) return; it->setText( 0, DefaultProvider::self()->locale()->toString(cp->getMenge())); it->setText( 1, cp->getName()); it->setText( 2, cp->unitPreis().toString()); it->setText( 3, cp->basisKosten().toString()); } void FlosTemplDialog::drawMatListEntry( QTreeWidgetItem *it, MaterialCalcPart *mc ) { it->setText( 0, mc->getName()); it->setText( 1, QString::number(mc->getCalcAmount(), 'f',2)); it->setText( 2, mc->getMaterial()->unit().einheitSingular()); it->setText( 3, mc->basisKosten().toString()); it->setText( 4, QString::number(mc->getMaterial()->getAmountPerPack(), 'f',2)); it->setText( 5, mc->getMaterial()->salesPrice().toString()); } void FlosTemplDialog::slRemoveTimePart() { if( ! m_template || !m_timeParts ) return; QTreeWidgetItem *item = m_timeParts->currentItem(); if( item ) { CalcPart *cp = mCalcPartDict[item]; if( cp ) { m_template->removeCalcPart(cp); } delete item; refreshPrices(); _calcPartsModified = true; } } void FlosTemplDialog::slEditTimePart() { if( ! m_template || !m_timeParts ) return; // qDebug () << "Edit time part!" << endl; QTreeWidgetItem *item = m_timeParts->currentItem(); if( item ) { TimeCalcPart *cp = static_cast(mCalcPartDict[item]); if( cp ) { m_timePartDialog = new TimeCalcDialog(this); m_timePartDialog->setTimeCalcPart(cp); connect( m_timePartDialog, SIGNAL(timeCalcPartChanged(TimeCalcPart*)), this, SLOT(slTimeCalcPartChanged(TimeCalcPart*)) ); m_timePartDialog->show(); } else { // qDebug () << "No time calc part found for this item" << endl; } } else { // qDebug () << "No current Item!"; } refreshPrices(); _calcPartsModified = true; } /* * Achtung: slAddMatPart fgt keinen neuen kompletten Materialpart * hinzu, sondern nur ein neues Material zu dem einen, bestehenden * Materialpart. */ void FlosTemplDialog::slAddMatPart() { if( ! m_template ) return; MaterialSelectDialog dia( this ); connect( &dia, SIGNAL( materialSelected( int, double ) ), this, SLOT( slNewMaterial( int, double ) ) ); dia.exec(); } /* * Slot, der aufgerufen wird, wenn im Materialeditor ein Material zur Kalkulation * geschickt wird. */ void FlosTemplDialog::slNewMaterial( int matID, double amount ) { // qDebug () << "Material ID: " << matID << endl; // TODO: Checken, ob der richtige Tab aktiv ist. // TODO: Check if the material is already in the calcpart (is this really needed??) MaterialCalcPart *mc = new MaterialCalcPart(matID, 0, amount); if( mc && ! mc->getMaterial() ) { // the material is still unknown to the catalog because it was just entered // in the material catalog qDebug() << "ERR: MaterialCalcPart without Material!"; return; } if( mc ) { mc->setProzentPlus(benefitValue()); m_template->addCalcPart( mc ); QTreeWidgetItem *lvItem = new QTreeWidgetItem(m_matParts); drawMatListEntry( lvItem, mc ); mCalcPartDict.insert( lvItem, mc ); refreshPrices(); _calcPartsModified = true; } } void FlosTemplDialog::slEditMatPart() { if( ! m_template || !m_matParts ) return; // qDebug () << "Edit Material part!" << endl; QTreeWidgetItem *item = m_matParts->currentItem(); MaterialCalcPart *mc = static_cast( mCalcPartDict[item] ); if( mc ) { m_matPartDialog = new MatCalcDialog( mc, this); connect( m_matPartDialog, SIGNAL(matCalcPartChanged(MaterialCalcPart*)), this, SLOT(slMatCalcPartChanged(MaterialCalcPart*))); m_matPartDialog->setModal(true); m_matPartDialog->show(); } else { // qDebug () << "No such MaterialCalcPart!"; } } void FlosTemplDialog::slMatCalcPartChanged(MaterialCalcPart *mc) { drawMatListEntry(m_matParts->currentItem(), mc); refreshPrices(); _calcPartsModified = true; } void FlosTemplDialog::slRemoveMatPart() { if( ! m_template || ! m_matParts ) return; QTreeWidgetItem *item = m_matParts->currentItem(); if( item ) { CalcPart *cp = mCalcPartDict[item]; if( cp ) { m_template->removeCalcPart(cp); } delete item; refreshPrices(); _calcPartsModified = true; } } /* * Slot for managing the switch from manual to calculated price * and vice versa */ void FlosTemplDialog::slCalcOrFix(int button) { bool ok = true; bool manualEnabled = true; if( button == 0 ) { /* switched to manual price */ if( m_template ) { m_template->setCalculationType( CatalogTemplate::ManualPrice ); } } else if( button == 1 ) { /* switched to calculated */ if( m_template ) { m_template->setCalculationType( CatalogTemplate::Calculation ); } manualEnabled = false; } else { /* unknown knob*/ ok = false; } if( ok ) { m_manualPriceVal->setEnabled( manualEnabled ); m_textTimePart->setEnabled(!manualEnabled); m_textFixPart->setEnabled(!manualEnabled); m_textMaterialPart->setEnabled(!manualEnabled); m_tLabelMat->setEnabled(!manualEnabled); m_tLabelFix->setEnabled(!manualEnabled); m_tLabelTime->setEnabled(!manualEnabled); m_tLabelProfit->setEnabled(!manualEnabled); spBenefit->setEnabled(!manualEnabled); refreshPrices(); } } void FlosTemplDialog::slSetNewText() { QPushButton *okButton = _buttonBox->button(QDialogButtonBox::Ok); if( ! m_text || m_text->toPlainText().isEmpty() ) { okButton->setEnabled(false); } else { okButton->setEnabled(true); } if( m_text ) { const QString t = Portal::textWrap( m_text->toPlainText(), 80, 5 ); const QStringList li = t.split(QLatin1Char('\n')); QString longest; for( const QString& p : li ) { if( p.length() > longest.length() ) longest = p; } QFontMetrics fm(m_textDispFix->font()); int w = 10+fm.width(longest); if( m_textDispTime) { m_textDispTime->setText(t); m_textDispTime->setMinimumWidth(w); } if( m_textDispFix) { m_textDispFix->setText(t); m_textDispFix->setMinimumWidth(w); } if( m_textDispMat) { m_textDispMat->setText(t); m_textDispMat->setMinimumWidth(w); } } } /* END */ kraft-0.97/src/flostempldialog.h000066400000000000000000000072551410616450300167160ustar00rootroot00000000000000/*************************************************************************** flostempldialog - ------------------- begin : 2004-15-08 copyright : (C) 2004 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef _FLOSTEMPLDIALOG_H #define _FLOSTEMPLDIALOG_H // include files #include #include "kraftglobals.h" #include "ui_calctemplate.h" #include "calcpart.h" #include "stockmaterial.h" class FloskelTemplate; class TimeCalcPart; class MaterialCalcPart; class FixCalcPart; class FixCalcDialog; class MatCalcDialog; class TimeCalcDialog; class Katalog; class QDialogButtonBox; class FlosTemplDialog : public QDialog, protected Ui::d_calcTempl { Q_OBJECT public: FlosTemplDialog(QWidget *parent=0, bool modal=false ); virtual ~FlosTemplDialog(); void setTemplate( FloskelTemplate* t, const QString&, bool ); bool templateIsNew() { return m_templateIsNew; }; signals: void takeMaterialAnswer(const QString&); void editAccepted( FloskelTemplate* ); void editRejected(); void chapterChanged(int); public slots: virtual void slAddFixPart(); virtual void slEditFixPart(); virtual void slRemoveFixPart(); virtual void slEditTimePart(); virtual void slAddTimePart(); virtual void slRemoveTimePart(); virtual void slAddMatPart(); virtual void slEditMatPart(); virtual void slRemoveMatPart(); virtual void slCalcOrFix(int); virtual void slBenefitChange(int newBen ); /* slot for adding a new material to the material calculation */ void slNewMaterial( int, double ); void refreshPrices(); void slManualPriceChanged(double ); void slSetNewText(); void setCalcparts(); void slFixCalcPartChanged(FixCalcPart*); void slTimeCalcPartChanged(TimeCalcPart*); void slMatCalcPartChanged(MaterialCalcPart*); virtual void accept(); virtual void reject(); virtual void closeEvent ( QCloseEvent * event ); bool confirmClose(); private: void setupConnections(); void setButtonIcons(); double benefitValue(); bool templModified(); virtual void drawTimeListEntry( QTreeWidgetItem *, TimeCalcPart * ); virtual void drawFixListEntry( QTreeWidgetItem*, FixCalcPart* ); virtual void drawMatListEntry( QTreeWidgetItem*, MaterialCalcPart* ); bool askChapterChange( FloskelTemplate*, int); virtual QString stdMaterialKalcPartName() { return i18n("Calculated material"); } FloskelTemplate *m_template; Katalog *m_katalog; /* dict das qlistviewitems auf calcparts abbildet */ QHash mCalcPartDict; QButtonGroup *m_gbPriceSrc; QDialogButtonBox *_buttonBox; FixCalcDialog *m_fixCalcDia; TimeCalcDialog *m_timePartDialog; MatCalcDialog *m_matPartDialog; bool m_templateIsNew; bool _calcPartsModified; CatalogTemplate::CalculationType _origCalcType; }; #endif /* END */ kraft-0.97/src/footertemplateprovider.cpp000066400000000000000000000056651410616450300206740ustar00rootroot00000000000000/*************************************************************************** footertemplateprovider - template provider classes for footer data like addresses or texts ------------------- begin : 2007-05-02 copyright : (C) 2007 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include #include "footertemplateprovider.h" #include "texteditdialog.h" #include "doctext.h" #include "defaultprovider.h" FooterTemplateProvider::FooterTemplateProvider( QWidget *parent ) :TemplateProvider( parent ) { } void FooterTemplateProvider::slotNewTemplate() { // qDebug () << "SlotNewTemplate called!" << endl; TextEditDialog dia( mParent, KraftDoc::Footer ); DocText dt; dt.setTextType( KraftDoc::Footer ); dt.setDocType( mDocType ); dia.setDocText( dt ); if ( dia.exec() ) { // qDebug () << "Successfully edited new text" << endl; DocText dt = dia.docText(); /* save to database */ dbID newId = DefaultProvider::self()->saveDocumentText( dt ); dt.setDbId( newId ); emit newFooterText( dt ); } } void FooterTemplateProvider::slotEditTemplate() { // qDebug () << "SlotEditTemplate called!" << endl; TextEditDialog dia( mParent, KraftDoc::Footer ); /* mCurrentText is set through the slot slotSetCurrentDocText */ DocText dt = currentText(); if ( dt.type() == KraftDoc::Unknown ) { dt.setTextType( KraftDoc::Footer ); dt.setDocType( mDocType ); } dia.setDocText( dt ); if ( dia.exec() ) { // qDebug () << "Successfully edited texts" << endl; DocText dt = dia.docText(); /* write back the listview item stored in the input text */ dt.setListViewItem( currentText().listViewItem() ); /* save to database */ DefaultProvider::self()->saveDocumentText( dt ); // this ends up in the footerselection, slot updateDocText emit updateFooterText( dt ); } } void FooterTemplateProvider::slotDeleteTemplate() { DocText dt = currentText(); emit deleteFooterText( dt ); DefaultProvider::self()->deleteDocumentText( dt ); } void FooterTemplateProvider::slotTemplateToDocument() { // qDebug () << "Moving template to document" << endl; emit footerTextToDocument( currentText() ); } kraft-0.97/src/footertemplateprovider.h000066400000000000000000000031671410616450300203340ustar00rootroot00000000000000/*************************************************************************** footertemplateprovider - template provider classes for footer data like addresses or texts ------------------- begin : 2007-05-02 copyright : (C) 2007 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef FOOTERTEMPLATEPROVIDER_H #define FOOTERTEMPLATEPROVIDER_H #include "templateprovider.h" class QWidget; class DocText; class FooterTemplateProvider : public TemplateProvider { Q_OBJECT public: FooterTemplateProvider( QWidget* ); public slots: void slotNewTemplate(); void slotEditTemplate(); void slotDeleteTemplate(); void slotTemplateToDocument(); signals: void newFooterText( const DocText& ); void updateFooterText( const DocText& ); void footerTextToDocument( const DocText& ); void deleteFooterText( const DocText& ); }; #endif kraft-0.97/src/format.cpp000066400000000000000000000046431410616450300153520ustar00rootroot00000000000000/*************************************************************************** Simple format functions for double, date etc. ------------------- begin : March 2020 copyright : (C) 2020 by Klaas Freitag email : kraft@freisturz.de ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include #include #include #include "format.h" namespace Format { QString localeDoubleToString(double val, const QLocale& loc) { int prec = 0; const QString num = QString::number(val); if( num.contains( QChar('.') ) ) { // there is a decimal point // calculate the precision prec = num.length() - (1+num.lastIndexOf( QChar('.') ) ); if (prec > 3 ) prec = 3; // lets dont go wild } const QString re = loc.toString(val, 'f', prec); return re; } QString toDateString( const QDate& date, const QString& format ) { if (format == Format::DateFormatIso) { return date.toString(Qt::ISODate); } if (format == DateFormatShort || format.isEmpty()) { return date.toString(Qt::DefaultLocaleShortDate); } if (format == DateFormatLong) { return date.toString(Qt::DefaultLocaleLongDate); } if (format == DateFormatRFC) { return date.toString(Qt::RFC2822Date); } if (format == DateFormatGerman) { return date.toString("dd.MM.yyyy"); } return date.toString(format); // good luck! } QString toDateTimeString(const QDateTime& dt, const QString &format) { const QString dateStr = QString("%1, %2:%3").arg(toDateString(dt.date(), format)) .arg(dt.time().hour(), 2, 10, QLatin1Char('0')) .arg(dt.time().minute(), 2, 10, QLatin1Char('0')); return dateStr; } } kraft-0.97/src/format.h000066400000000000000000000043451410616450300150160ustar00rootroot00000000000000/*************************************************************************** Simple format functions for double, date etc. ------------------- begin : March 2020 copyright : (C) 2020 by Klaas Freitag email : kraft@freisturz.de ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef FORMAT_H #define FORMAT_H #include class QString; namespace Format { /** * @brief localeDoubleToString - convert a double into a string locale aware. * @param val - the value * @param loc - a locale, if skipped, the default locale is used. * @return the string * * The additional cleverness is that the returned string has the right * precision, ie. it returns "2" for val = 2.00 but "2.23" if val is 2.23. */ QString localeDoubleToString(double val, const QLocale& loc = QLocale()); const QString DateFormatIso = QStringLiteral("ISO"); const QString DateFormatShort = QStringLiteral("Short"); const QString DateFormatLong = QStringLiteral("Long"); const QString DateFormatRFC = QStringLiteral("RFC"); const QString DateFormatGerman = QStringLiteral("German"); /** * @brief toDateString - format date to a given format * @param date - the QDate * @param format - A name of a format (read from settings) or format string * @return the string containing the date. */ QString toDateString(const QDate& date, const QString &format); QString toDateTimeString(const QDateTime& dt, const QString &format); } #endif // FORMAT_H kraft-0.97/src/geld.cpp000066400000000000000000000046741410616450300150010ustar00rootroot00000000000000/*************************************************************************** geld - A class that represents money and supports calculation ------------------- begin : 2004-16-08 copyright : (C) 2004 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ // include files for Qt // include files for KDE #include #include "geld.h" #include "defaultprovider.h" Geld::Geld( ) { m_cent = 0; } Geld::Geld( long l ) { m_cent = l; } Geld::Geld( double g ) { m_cent = qRound(100*g); } Geld& Geld::operator=(const long l) { m_cent = l; return *this; } Geld& Geld::operator=(const double d) { m_cent = qRound( 100.0 * d ); return *this; } Geld& Geld::operator+=(const Geld& g) { m_cent += g.m_cent; return *this; } Geld Geld::operator/(const double divisor) const { // FIXME Geld g( this->m_cent / divisor / 100 ); return g; } Geld Geld::percent( double p ) { Geld g( this->m_cent * p / 100 /100 ); return g; } Geld Geld::operator*(const long mult) const { // FIXME Geld g( this->m_cent * mult / 100); return g; } Geld Geld::operator*(const double mult) const { Geld g(double(this->m_cent) * mult / 100); return g; } bool Geld::operator!=(Geld g) { return g.m_cent != m_cent; } QString Geld::toString() const { return DefaultProvider::self()->locale()->toCurrencyString(m_cent/100.0); } QString Geld::toHtmlString() const { QString re = toString(); re.replace( " ", " " ); if ( m_cent < 0 ) { re = QString( "%1" ).arg( re ); } return re; } double Geld::toDouble() { return m_cent/100.0; } long Geld::toLong() { return m_cent; } Geld::~Geld( ) { } /* END */ kraft-0.97/src/geld.h000066400000000000000000000032051410616450300144330ustar00rootroot00000000000000 /*************************************************************************** geld - ------------------- begin : 2004-16-08 copyright : (C) 2004 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef _GELD_H #define _GELD_H #include "kraftcat_export.h" #include #include class KRAFTCAT_EXPORT Geld { public: Geld(); Geld(long); Geld(double); ~Geld(); Geld& operator=(const long); Geld& operator=(const double); Geld operator/(const double) const; Geld operator*(const long) const; Geld operator*(const double) const; Geld& operator+=(const Geld&); bool operator!=(Geld); Geld percent( double ); QString toString() const; QString toHtmlString() const; double toDouble(); // toLong returns the amount in cents! long toLong(); private: long m_cent; }; #endif /* END */ kraft-0.97/src/grantleetemplate.cpp000066400000000000000000000053671410616450300174230ustar00rootroot00000000000000/*************************************************************************** GrantleeTemplate.cpp - fill a template with text tags ------------------- begin : March 2020 copyright : (C) 2020 by Klaas Freitag email : kraft@freisturz.de ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include "grantleetemplate.h" #include "klocalizedstring.h" #include #include #include #include #include #include #include GrantleeFileTemplate::GrantleeFileTemplate( const QString& file) :_tmplFileName(file) { } void GrantleeFileTemplate::addToMapping(const QString& key, const QVariant& variant) { _mapping.insert(key, variant); } void GrantleeFileTemplate::addToObjMapping(const QString& key, QObject *obj) { _objs.insert(key, obj); } QString GrantleeFileTemplate::render(bool &ok) const { QScopedPointer engine(new Grantlee::Engine()); QFileInfo fi(_tmplFileName); ok = true; // assume all goes well. auto loader = QSharedPointer::create(); loader->setTemplateDirs( {fi.absolutePath()} ); engine->addTemplateLoader( loader ); QString output; auto t = engine->loadByName(fi.fileName()); if (t->error() != Grantlee::Error::NoError) { ok = false; output = t->errorString(); qDebug() << "Grantlee template load failed:" << output; } if (ok) { Grantlee::Context c(_mapping); QHash::const_iterator i = _objs.constBegin(); while (i != _objs.constEnd()) { const QString k = i.key(); QObject *obj = i.value(); c.insert(k, obj); ++i; } output = t->render(&c); if (t->error() != Grantlee::Error::NoError) { ok = false; // Rendering error. output = t->errorString(); qDebug() << "Grantlee template err:" << output; } } return output; } kraft-0.97/src/grantleetemplate.h000066400000000000000000000030611410616450300170550ustar00rootroot00000000000000/*************************************************************************** GrantleeTemplate.cpp - fill a template with text tags ------------------- begin : March 2020 copyright : (C) 2020 by Klaas Freitag email : kraft@freisturz.de ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef GRANTLEETEMPLATE_H #define GRANTLEETEMPLATE_H #include #include #include "texttemplateinterface.h" #include #include class GrantleeFileTemplate { public: GrantleeFileTemplate( const QString& file); void addToMapping(const QString& key, const QVariant& variant); void addToObjMapping(const QString& key, QObject *obj); QString render(bool& ok) const; private: QVariantHash _mapping; const QString& _tmplFileName; QHash _objs; }; #endif kraft-0.97/src/headertemplateprovider.cpp000066400000000000000000000055561410616450300206250ustar00rootroot00000000000000/*************************************************************************** headertemplateprovider - template provider classes for header data like addresses or texts ------------------- begin : 2007-05-02 copyright : (C) 2007 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include #include "headertemplateprovider.h" #include "texteditdialog.h" #include "doctext.h" #include "defaultprovider.h" HeaderTemplateProvider::HeaderTemplateProvider( QWidget *parent ) :TemplateProvider( parent ) { } void HeaderTemplateProvider::slotNewTemplate() { // qDebug () << "SlotNewTemplate called!" << endl; TextEditDialog dia( mParent, KraftDoc::Header ); DocText dt; dt.setTextType( KraftDoc::Header ); dt.setDocType( mDocType ); dia.setDocText( dt ); if ( dia.exec() ) { // qDebug () << "Successfully edited texts" << endl; DocText dt = dia.docText(); /* save to database */ dbID newId = DefaultProvider::self()->saveDocumentText( dt ); dt.setDbId( newId ); emit newHeaderText( dt ); } } void HeaderTemplateProvider::slotEditTemplate() { // qDebug () << "SlotEditTemplate called!" << endl; TextEditDialog dia( mParent, KraftDoc::Header ); /* mCurrentText is set through the slot slotSetCurrentDocText */ DocText dt = currentText(); if ( dt.type() == KraftDoc::Unknown ) { dt.setTextType( KraftDoc::Header ); dt.setDocType( mDocType ); } dia.setDocText( dt ); if ( dia.exec() ) { // qDebug () << "Successfully edited texts" << endl; DocText dt = dia.docText(); /* write back the listview item stored in the input text */ dt.setListViewItem( currentText().listViewItem() ); /* save to database */ DefaultProvider::self()->saveDocumentText( dt ); emit updateHeaderText( dt ); } } void HeaderTemplateProvider::slotDeleteTemplate() { DefaultProvider::self()->deleteDocumentText( currentText() ); emit deleteHeaderText( currentText() ); } void HeaderTemplateProvider::slotTemplateToDocument() { // qDebug () << "Moving template to document" << endl; emit headerTextToDocument( currentText() ); } kraft-0.97/src/headertemplateprovider.h000066400000000000000000000032071410616450300202610ustar00rootroot00000000000000/*************************************************************************** headertemplateprovider - template provider classes for header data like addresses or texts ------------------- begin : 2007-05-02 copyright : (C) 2007 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef HEADERTEMPLATEPROVIDER_H #define HEADERTEMPLATEPROVIDER_H #include "templateprovider.h" #include "doctext.h" class QWidget; class HeaderTemplateProvider : public TemplateProvider { Q_OBJECT public: HeaderTemplateProvider( QWidget* ); public slots: void slotNewTemplate(); void slotEditTemplate(); void slotDeleteTemplate(); void slotTemplateToDocument(); signals: void newHeaderText( const DocText& ); void updateHeaderText( const DocText& ); void headerTextToDocument( const DocText& ); void deleteHeaderText( const DocText& ); private: }; #endif kraft-0.97/src/htmlview.cpp000066400000000000000000000111631410616450300157140ustar00rootroot00000000000000/*************************************************************************** htmlview.cpp - show a html page ------------------- begin : Aug 2006 copyright : (C) 2006 Klaas Freitag (C) 2006 Cornelius Schumacher ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include "htmlview.h" #include #include #include #include #include #include "defaultprovider.h" HtmlView::HtmlView( QWidget *parent ) : QTextBrowser( parent ), mZoomStep( 10 ) { this->setReadOnly(true); this->setOpenLinks(false); // get only the signal below connect (this, SIGNAL(anchorClicked(QUrl)), this, SIGNAL(openUrl(QUrl))); } void HtmlView::setTitle( const QString &title ) { mTitle = title; } void HtmlView::setStylesheetFile( const QString &style ) { const QString file = QString("styles/%1").arg(style); const QString stylesheetFile = DefaultProvider::self()->locateFile(file); if( QFile::exists(stylesheetFile) ) { qDebug () << "Found this stylefile: " << stylesheetFile; mStyles = readStyles(stylesheetFile); } else { qDebug() << "Unable to find stylesheet file "<< style; mStyles.clear(); } } void HtmlView::zoomIn() { // setZoomFactor( zoomFactor() + mZoomStep ); updateZoomActions(); } void HtmlView::zoomOut() { // setZoomFactor( zoomFactor() - mZoomStep ); updateZoomActions(); } void HtmlView::updateZoomActions() { // mZoomInAction->setEnabled( zoomFactor() + mZoomStep <= 300 ); // mZoomOutAction->setEnabled( zoomFactor() - mZoomStep > 100 ); // Prefs::self()->setZoomFactor( zoomFactor() ); } QString HtmlView::topFrame( ) const { QStringList stringList; stringList << QLatin1String(""); stringList << QLatin1String(""); if(!mTitle.isEmpty()) { stringList << QString("%1").arg( mTitle ); } stringList << QLatin1String(""); return stringList.join(QChar('\n')); } QString HtmlView::readStyles(const QString& styleFile) const { QFile file(styleFile); if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { QString styles; QFileInfo fi(styleFile); const QString base = fi.path()+"/"; QTextStream textStream(&file); while( !textStream.atEnd() ) { QString line = textStream.readLine().trimmed(); if( line.startsWith("background-image:url(") ) { QString relative = line.remove(0, 21); // remove background... relative.prepend(base); line = QString( "background-image:url(%1").arg(relative); } line.append('\n'); styles.append(line); } // qDebug() << "+++++++++++++++++++++++++++++++++++++++++++++" << styles; return styles; } return QString(); } QString HtmlView::bottomFrame() const { const QString t("\n" ""); return t; } void HtmlView::displayContent( const QString& content ) { // on empty content just clear and leave. if( content.isEmpty() ) { this->clear(); return; } const QString out = topFrame() + content + bottomFrame(); const QString s = mStyles; #ifdef QT_DEBUG // this file gets written and removed immediately, so if it should be kept, // set the autoDelete to false QTemporaryFile tempFile("/tmp/kraft_XXXXXX"); // tempFile.setAutoRemove(false); if (tempFile.open()) { const QString fName = tempFile.fileName(); qDebug() << "########## HtmlView output written to" << fName; QTextStream outStream(&tempFile); outStream << s; outStream << "##############" << endl; outStream << out; tempFile.close(); } #endif this->document()->setDefaultStyleSheet(s); setHtml(out); } kraft-0.97/src/htmlview.h000066400000000000000000000035201410616450300153570ustar00rootroot00000000000000 /*************************************************************************** htmlview.h - show a html page ------------------- begin : Aug 2006 copyright : (C) 2006 Klaas Freitag (C) 2006 Cornelius Schumacher ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef HTMLVIEW_H #define HTMLVIEW_H #include #include class QUrl; class HtmlView : public QTextBrowser { Q_OBJECT public: HtmlView( QWidget *parent = 0); QString title() const { return mTitle; } public slots: void setTitle( const QString & ); void setStylesheetFile( const QString & ); void displayContent( const QString& ); void zoomIn(); void zoomOut(); protected: void updateZoomActions(); signals: void openUrl( const QUrl& ); private: QString topFrame() const; QString bottomFrame() const; QString readStyles(const QString &styleFile) const; QString mTitle; QString mInternalUrl; QString mStyles; QAction *mZoomInAction; QAction *mZoomOutAction; int mZoomStep; }; #endif kraft-0.97/src/identity.ui000066400000000000000000000067001410616450300155420ustar00rootroot00000000000000 manualOwnIdentity 0 0 586 428 Form Name: Organization: Street: Post Code: City: Phone: Fax: Mobile Phone: EMail: Website: kraft-0.97/src/importfilter.cpp000066400000000000000000000211011410616450300165660ustar00rootroot00000000000000/*************************************************************************** importfilter.cpp - Import positions into Kraft documents ------------------- begin : Oct 2008 copyright : (C) 2008 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ // include files for QT #include #include #include #include #include #include #include #include #include "importfilter.h" #include "unitmanager.h" #include "defaultprovider.h" ImportFilter::ImportFilter() : mStrict( true ) { } bool ImportFilter::readDefinition( const QString& name ) { QString defFile = name; if ( ! name.startsWith( "/" ) ) { QString defFileName = QString( name ).toLower(); QString findFile = kdeStdDirPath() + defFileName; // qDebug () << "KDE StdDir Path: " << findFile; defFile = QStandardPaths::locate(QStandardPaths::GenericDataLocation, findFile); if ( defFile.isEmpty() ) { mError = i18n( "Unable to find filter called %1", name ); return false; } } // qDebug () << "Reading definition file " << defFile; QFile f( defFile ); if ( !f.open( QIODevice::ReadOnly ) ) { mError = i18n( "Could not open the definition file!" ); return false; } QTextStream t( &f ); t.setCodec("UTF-8"); while ( !t.atEnd() ) { mDefinition << t.readLine(); } f.close(); return true; } bool ImportFilter::parse() { return true; } bool ImportFilter::recode( const QString& file, const QString& outfile ) { if ( mEncoding.isEmpty() ) return true; QString cmd = DefaultProvider::self()->iconvTool(); if ( QFile::exists( cmd ) ) { QStringList args = QStringList() << "-f" << mEncoding << "-t" << "utf-8" << "-o" << outfile << file; int result = QProcess::execute( cmd, args ); Q_UNUSED(result); // qDebug () << "Recode finished with exit code " << result; return true; } else { // qDebug () << "Recode-tool does not exist!"; } return false; } // ########################################################################### DocPositionImportFilter::DocPositionImportFilter() :ImportFilter( ) { } QString DocPositionImportFilter::kdeStdDirPath() const { QString re = QString::fromLatin1( "kraft/importfilter/positions/" ); return re; } #define FILTER_TAG( x, y ) x bool DocPositionImportFilter::parseDefinition() { bool ret = true; for ( QStringList::Iterator it = mDefinition.begin(); it != mDefinition.end(); ++it ) { QString l = ( *it ).trimmed(); if ( l.isEmpty() || l.startsWith( "#" ) ) { // continue - whitespace.... } else if ( l.startsWith( FILTER_TAG( "amount:", "amount of the item" ), Qt::CaseInsensitive ) ) { mAmount = ( l.right( l.length()-7 ) ).trimmed(); } else if ( l.startsWith( FILTER_TAG( "text:", "The item text" ), Qt::CaseInsensitive ) ) { mText = ( l.right( l.length()-5 ) ).trimmed(); mText.replace( "
            ", QChar( 0x0A ) ); } else if ( l.startsWith( FILTER_TAG( "unit:", "The item unit" ), Qt::CaseInsensitive ) ) { mUnit = ( l.right( l.length()-5 ) ).trimmed(); } else if ( l.startsWith( FILTER_TAG( "unit_price:", "unit price" ), Qt::CaseInsensitive ) ) { mUnitPrice = ( l.right( l.length()-11 ) ).trimmed(); } else if ( l.startsWith( FILTER_TAG( "name:", "The name of the filter" ), Qt::CaseInsensitive ) ) { mName = ( l.right( l.length()-5 ) ).trimmed(); } else if ( l.startsWith( FILTER_TAG( "description:", "The filter description" ), Qt::CaseInsensitive ) ) { mDescription = ( l.right( l.length()-12 ) ).trimmed(); } else if ( l.startsWith( FILTER_TAG("encoding:", "The encoding of the source file" ), Qt::CaseInsensitive ) ) { mEncoding = ( l.right( l.length()-9 ) ).trimmed(); } else if ( l.startsWith( FILTER_TAG( "separator:", "The separator used in the source file" ), Qt::CaseInsensitive ) ) { // qDebug () << "Separator found: " << l.right( l.length()-10 ); mSeparator = ( l.right( l.length()-10 ) ).trimmed(); } else if ( l.startsWith( FILTER_TAG( "tags:", "Comma separated list of tags for one item" ), Qt::CaseInsensitive ) ) { mTags = ( l.right( l.length()-5 ) ).trimmed(); } else { // qDebug () << "WRN: Unknown filter tag found: " << l; if ( mError.isEmpty() ) mError = i18n( "Unknown tags: " ); mError.append( l ); ret = false; } } if ( mSeparator.isEmpty() ) { mSeparator = QString::fromLatin1( ";" ); } return ret; } void DocPositionImportFilter::debugDefinition() { // qDebug () << "Amount: " << mAmount; // qDebug () << "Unit: " << mUnit; // qDebug () << "UnitPrice: " << mUnitPrice; // qDebug () << "Text: " << mText; // qDebug () << "Separator: <" << mSeparator << ">"; } DocPositionList DocPositionImportFilter::import( const QUrl &inFile ) { DocPositionList list; bool copied = false; if( !inFile.isLocalFile() ) return list; QString file( inFile.toLocalFile() ); // in case we have to recode, the source file needs to be copied. bool ok = true; if ( !mEncoding.isEmpty() ) { file += ".tmp"; copied = true; // qDebug () << "Encoding file to " << file; ok = recode( inFile.toLocalFile(), file ); if ( !ok ) { // qDebug () << "Recoding failed!"; mError = i18n( "Could not recode input file!" ); } } QFile f( file ); if ( ! f.exists() ) { // qDebug () << "File " << file << " could not be found!"; mError = i18n( "Unable to open temp file " ) + file; ok = false; } if ( ok ) { if ( !f.open( QIODevice::ReadOnly ) ) { mError = i18n( "Could not open the import source file!" ); ok = false; } } if ( ok ) { QTextStream t( &f ); t.setCodec("UTF-8"); int cnt = 0; while ( !t.atEnd() ) { cnt++; QString l = t.readLine().trimmed(); // qDebug () << "Importing line " << l; if ( !( l.isEmpty() || l.startsWith( "#" ) ) ) { bool ok; DocPosition dp = importDocPosition( l, ok ); if ( ok ) list.append( new DocPosition( dp ) ); } } f.close(); } if ( copied ) { QFile::remove( file ); } return list; } // creates a DocPosition from one line of the imported file DocPosition DocPositionImportFilter::importDocPosition( const QString& l, bool& ok ) { QStringList parts = l.split( mSeparator, QString::KeepEmptyParts ); // qDebug () << "Importing raw line " << l; QString h; ok = true; DocPosition pos; // the text (mandatory) QString t = replaceCOL( parts, mText ); pos.setText( t ); QString unit = replaceCOL( parts, mUnit ); int unitId = UnitManager::self()->getUnitIDSingular( unit ); if ( unitId > -1 ) { pos.setUnit( UnitManager::self()->getUnit( unitId ) ); } else { pos.setUnit(Einheit( unit )); } // Amount. h = replaceCOL( parts, mAmount ); bool convOk = true; double a = h.toDouble( &convOk ); if ( convOk ) { pos.setAmount( a ); } else { // qDebug () << "WRN: Unable to convert amount to double: " << h; if ( mStrict ) ok = false; } // Unit Price h = replaceCOL( parts, mUnitPrice ); a = h.toDouble( &convOk ); if ( convOk ) { pos.setUnitPrice( Geld( a ) ); } else { // qDebug () << "WRN: Unable to convert unit price to double: " << h; if ( mStrict ) ok = false; } if ( !mTags.isEmpty() ) { QStringList tags = mTags.split(QRegExp( "\\s*,\\s*" )); for ( QStringList::Iterator it = tags.begin(); it != tags.end(); ++it ) { QString t = ( *it ).trimmed(); pos.setTag( t ); } } return pos; } QString DocPositionImportFilter::replaceCOL( const QStringList& cols, const QString& in ) { QString re( in ); for ( int i = 0; i < cols.size(); i++ ) { QString replacer = QString( "COL(%1)" ).arg( i+1 ); QString col = cols[i].trimmed(); re.replace( replacer, col, Qt::CaseInsensitive ); } // qDebug() << "replaced line: " << re; return re; } kraft-0.97/src/importfilter.h000066400000000000000000000043771410616450300162530ustar00rootroot00000000000000/*************************************************************************** importfilter.cpp - Import positions into Kraft documents ------------------- begin : Oct 2008 copyright : (C) 2008 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ // include files for QT #ifndef _IMPORTFILTER_H #define _IMPORTFILTER_H #include #include #include "docposition.h" class ImportFilter { public: ImportFilter(); virtual ~ImportFilter() { } virtual bool parse(); virtual QString kdeStdDirPath() const = 0; bool readDefinition( const QString& ); QString error() { return mError; } void setStrict( bool _strict ) { mStrict = _strict; } bool strict() { return mStrict; } QString name() { return mName; } QString description() { return mDescription; } protected: bool recode( const QString&, const QString& ); QString mName; QString mDescription; QString mEncoding; QString mError; QString mFilename; QString mSeparator; QString mTags; QStringList mDefinition; bool mStrict; }; class DocPositionImportFilter : public ImportFilter { public: DocPositionImportFilter( ); virtual ~DocPositionImportFilter( ) {} virtual QString kdeStdDirPath() const; bool parseDefinition(); DocPositionList import( const QUrl& ); void debugDefinition(); private: DocPosition importDocPosition( const QString&, bool& ); QString replaceCOL( const QStringList&, const QString& ); QString mAmount; QString mText; QString mUnit; QString mUnitPrice; }; #endif kraft-0.97/src/importitemdialog.cpp000066400000000000000000000141101410616450300174210ustar00rootroot00000000000000/*************************************************************************** importitemdialog.h - small dialog to import items into the document ------------------- begin : Nov 2008 copyright : (C) 2008 Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include "importitemdialog.h" // include files for Qt #include #include #include #include #include #include #include #include #include #include #include #include "importfilter.h" #include "defaultprovider.h" #include "kraftsettings.h" #include "tagman.h" ImportItemDialog::ImportItemDialog( QWidget *parent ) : QDialog( parent ) { QVBoxLayout *mainLayout = new QVBoxLayout; setLayout(mainLayout); setObjectName( "IMPORTITEMDIALOG" ); setModal( true ); setWindowTitle( i18n( "Import Items From File" ) ); QWidget *w = new QWidget(this); mBaseWidget = new Ui::importToDocBase; mBaseWidget->setupUi(w); mainLayout->addWidget(w); // Fill the tags list group = new QButtonGroup(this); group->setExclusive(false); QStringList tags = TagTemplateMan::self()->allTagTemplates(); int c = 0; QVBoxLayout *checkboxLayout = new QVBoxLayout; for ( QStringList::Iterator it = tags.begin(); it != tags.end(); ++it ) { QCheckBox *cb = new QCheckBox( *it ); group->addButton(cb, c); checkboxLayout->addWidget(cb); QString desc = TagTemplateMan::self()->getTagTemplate( *it ).description(); cb->setToolTip( desc ); mTagMap[c] = *it; c++; } checkboxLayout->addStretch(2); mBaseWidget->mTagGroup->setLayout(checkboxLayout); connect( mBaseWidget->mSchemaCombo, SIGNAL( activated( const QString& ) ), SLOT( slotSchemaChanged( const QString& ) ) ); QString selectName = readFilterSpecs(); if ( ! KraftSettings::self()->importItemsSchemaName().isEmpty() ) { selectName = KraftSettings::self()->importItemsSchemaName(); } mBaseWidget->mSchemaCombo->setCurrentIndex(mBaseWidget->mSchemaCombo->findText( selectName )); slotSchemaChanged( selectName ); if ( ! KraftSettings::self()->importItemsFileName().isEmpty() ) { mBaseWidget->mFileNameEdit->setText( KraftSettings::self()->importItemsFileName() ); } QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); mainLayout->addWidget(buttonBox); } ImportItemDialog::~ImportItemDialog() { } QComboBox *ImportItemDialog::getPositionCombo() { return mBaseWidget->dmPositionCombo; } void ImportItemDialog::setPositionList( DocPositionList list, int intendedPos ) { if ( ! getPositionCombo() ) { qCritical() << "Can not get a ptr to the position combo"; return; } QStringList strList; strList << i18n( "the Header of the Document" ); DocPositionListIterator it( list ); while( it.hasNext() ) { DocPositionBase *dpb = it.next(); DocPosition *dp = static_cast( dpb ); QString h = QString( "%1. %2" ).arg( list.posNumber( dp ) ).arg( dp->text() ); if ( h.length() > 50 ) { h = h.left( 50 ); h += i18n( "..." ); } strList.append( h ); } getPositionCombo()->insertItems(-1, strList ); getPositionCombo()->setCurrentIndex( intendedPos ); } QString ImportItemDialog::readFilterSpecs() { QString filter = QString::fromLatin1( "kraft/importfilter/positions/*.ftr" ); QStringList filters = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, filter); QStringList combo; QString firstFilter; for ( QStringList::Iterator it = filters.begin(); it != filters.end(); ++it ) { // qDebug () << " -> Import filter file " << *it; DocPositionImportFilter filter; filter.readDefinition( *it ); filter.parseDefinition(); combo << filter.name(); if( firstFilter.isEmpty() ) firstFilter = filter.name(); mFilterMap[filter.name()] = filter; } mBaseWidget->mSchemaCombo->insertItems(-1, combo ); return firstFilter; } void ImportItemDialog::slotSchemaChanged( const QString& name ) { QString desc = mFilterMap[name].description(); mBaseWidget->mSchemaInfo->setText( desc ); } DocPositionList ImportItemDialog::positionList() { DocPositionList list; QUrl url = QUrl::fromLocalFile(mBaseWidget->mFileNameEdit->text()); if ( ! url.isEmpty() ) { DocPositionImportFilter filter = mFilterMap[mBaseWidget->mSchemaCombo->currentText()]; list = filter.import( url ); // get the tags QStringList tags; QMap::Iterator it; for ( it = mTagMap.begin(); it != mTagMap.end(); ++it ) { QCheckBox *b = static_cast( group->button( it.key() ) ); if ( b->isChecked() ) tags.append( it.value() ); } if ( tags.size() > 0 ) { DocPositionListIterator posIt( list ); while( posIt.hasNext() ) { DocPositionBase *dp = posIt.next(); for ( QStringList::Iterator it = tags.begin(); it != tags.end(); ++it ) { dp->setTag( *it ); } } } } return list; } /* END */ kraft-0.97/src/importitemdialog.h000066400000000000000000000035301410616450300170720ustar00rootroot00000000000000/*************************************************************************** importitemdialog.h - small dialog to import items into the document ------------------- begin : Nov 2008 copyright : (C) 2008 Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef IMPORTITEMDIALOG_H #define IMPORTITEMDIALOG_H #include #include #include #include #include "ui_importtodocbase.h" #include "templtopositiondialogbase.h" #include "docposition.h" #include "importfilter.h" class importToDocBase; class DocPositionList; class ImportItemDialog: public QDialog { Q_OBJECT public: ImportItemDialog( QWidget* ); ~ImportItemDialog(); void setPositionList( DocPositionList, int ); DocPositionList positionList(); QComboBox *getPositionCombo(); signals: void positionImported( const DocPosition& ); protected slots: void slotSchemaChanged( const QString& ); protected: QString readFilterSpecs(); private: Ui::importToDocBase *mBaseWidget; QButtonGroup *group; QMap mFilterMap; QMap mTagMap; }; #endif kraft-0.97/src/importtodocbase.ui000066400000000000000000000113501410616450300171040ustar00rootroot00000000000000 importToDocBase 0 0 524 309 0 0 18 75 true Import Document Items Qt::PlainText false Import information Select a &File to import from: false FixMe! Import &Schema: false mSchemaCombo 0 66 this is interesting information about the selected schema Qt::RichText Qt::AlignTop true 5 Insert all Items &after false dmPositionCombo Tags kraft-0.97/src/impviewwidgets.cpp000066400000000000000000000037471410616450300171350ustar00rootroot00000000000000/*************************************************************************** impviewwidgets.cpp - Improved view widgets ------------------- begin : Sun Nov 29 2009 copyright : (C) 2009 by Klaas Freitag and Thomas Richard email : freitag@kde.org, thomas.richard@proan.be ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include #include #include #include #include #include #include "impviewwidgets.h" ImpTreeView::ImpTreeView(QWidget *parent ) : QTreeView(parent) {} void ImpTreeView::setModel ( QAbstractItemModel * model ) { connect(model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)), this, SLOT(headerDataChanged(Qt::Orientation,int,int))); QTreeView::setModel(model); } void ImpTreeView::headerDataChanged(Qt::Orientation orientation, int first, int last) { if(orientation == Qt::Vertical) { for(int i = first; i <= last; ++i) { QVariant sign = model()->headerData(i, orientation); if (sign == QString ("!")) setRowHidden(i, QModelIndex(), true); else setRowHidden(i, QModelIndex(), false); } } } void ImpTreeView::unhideRows() { for(int i = 0; i < model()->rowCount(); ++i) setRowHidden(i, QModelIndex(), false); } kraft-0.97/src/impviewwidgets.h000066400000000000000000000026521410616450300165740ustar00rootroot00000000000000/*************************************************************************** impviewwidgets.h - Improved view widgets ------------------- begin : Sun Nov 29 2009 copyright : (C) 2009 by Klaas Freitag and Thomas Richard email : freitag@kde.org, thomas.richard@proan.be ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef IMPVIEWWIDGETS_H #define IMPVIEWWIDGETS_H #include class QWidget; class QAbstractItemModel; class ImpTreeView : public QTreeView { Q_OBJECT public: ImpTreeView(QWidget *parent = 0); void setModel ( QAbstractItemModel * model ); void unhideRows(); private slots: void headerDataChanged(Qt::Orientation orientation, int first, int last); }; #endif kraft-0.97/src/inserttempldialog.cpp000066400000000000000000000154761410616450300176160ustar00rootroot00000000000000/*************************************************************************** inserttemplatedialog.cpp - small dialog to insert templates into documents ------------------- begin : Sep 2006 copyright : (C) 2006 Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include "inserttempldialog.h" // include files for Qt #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ui_inserttmplbase.h" #include "templtopositiondialogbase.h" #include "katalog.h" #include "einheit.h" #include "unitmanager.h" #include "defaultprovider.h" #include "kraftsettings.h" #include "tagman.h" InsertTemplDialog::InsertTemplDialog( QWidget *parent ) : TemplToPositionDialogBase( parent ) { QWidget *w = new QWidget( this ); mBaseWidget = new Ui::insertTmplBase; mBaseWidget->setupUi( w ); mBaseWidget->dmUnitCombo->insertItems( -1, UnitManager::self()->allUnits() ); mBaseWidget->mPriceVal->setSuffix( DefaultProvider::self()->currencySymbol() ); mBaseWidget->mPriceVal->setMinimum( 0 ); mBaseWidget->mPriceVal->setMaximum( 100000 ); mBaseWidget->mPriceVal->setDecimals( 2 ); mBaseWidget->dmAmount->setDecimals( 2 ); mBaseWidget->dmAmount->setRange( 0, 100000 ); mBaseWidget->dmAmount->setSingleStep( 1 ); // mBaseWidget->dmAmount->setSteps( 1, 10 ); // hide the chapter combo by default mBaseWidget->mKeepGroup->hide(); // Fill the tags list QGroupBox *group = mBaseWidget->mTagGroup; QVBoxLayout *groupLay = new QVBoxLayout; group->setLayout( groupLay ); QStringList tags = TagTemplateMan::self()->allTagTemplates(); int cnt = 0; for ( QStringList::Iterator it = tags.begin(); it != tags.end(); ++it ) { QCheckBox *cb = new QCheckBox( *it ); QString desc = TagTemplateMan::self()->getTagTemplate( *it ).description(); // QToolTip::add( cb, desc ); groupLay->insertWidget( cnt++, cb ); mTagMap[cb] = *it; } groupLay->addStretch(); QVBoxLayout *lay = new QVBoxLayout(this); lay->setMargin(0); lay->addWidget(w); setLayout(lay); connect(mBaseWidget->mButtonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(mBaseWidget->mButtonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); } void InsertTemplDialog::setDocPosition(DocPosition *dp, bool isNew , bool showPrices) { if ( dp ) { mParkPosition = *dp; mBaseWidget->dmTextEdit->setText( prepareText(mParkPosition.text()) ); mBaseWidget->dmAmount->setValue( mParkPosition.amount() ); mBaseWidget->dmUnitCombo->setCurrentIndex(mBaseWidget->dmUnitCombo->findText( mParkPosition.unit().einheit( 1.0 ))); mBaseWidget->mPriceVal->setValue( mParkPosition.unitPrice().toDouble() ); if ( mParkPosition.text().isEmpty() ) { mBaseWidget->dmHeaderText->setText( i18n( "Create a new Item" ) ); } else { mBaseWidget->dmHeaderText->setText( i18n( "Create a new Item from Template" ) ); } if ( isNew ) { mBaseWidget->dmTextEdit->setFocus(); } else { mBaseWidget->dmAmount->setFocus(); } mBaseWidget->mPriceVal->setVisible(showPrices); mBaseWidget->priceBoxTextLabel->setVisible(showPrices); } } #define QL1(x) QLatin1String(x) QString InsertTemplDialog::prepareText( const QString& input ) { QString in(input); QLocale *locale = DefaultProvider::self()->locale(); QString dateStr = locale->toString( QDate::currentDate() ); in.replace(QL1("{{DATE}}"), dateStr, Qt::CaseInsensitive); QString timeStr = locale->toString(QTime::currentTime()); in.replace(QL1("{{TIME}}"), timeStr, Qt::CaseInsensitive); if( in.contains(QL1("{{USERNAME}}"))) { register struct passwd *pw; register uid_t uid; uid = geteuid (); pw = getpwuid (uid); if (pw) { in.replace(QL1("{{USERNAME}}"), QString::fromUtf8(pw->pw_gecos), Qt::CaseInsensitive); } } return in; } QComboBox *InsertTemplDialog::getPositionCombo() { return mBaseWidget->dmPositionCombo; } DocPosition InsertTemplDialog::docPosition() { mParkPosition.setText( mBaseWidget->dmTextEdit->toPlainText() ); mParkPosition.setAmount( mBaseWidget->dmAmount->value() ); mParkPosition.setUnitPrice( Geld( mBaseWidget->mPriceVal->value() ) ); int uid = UnitManager::self()->getUnitIDSingular( mBaseWidget->dmUnitCombo->currentText() ); mParkPosition.setUnit( UnitManager::self()->getUnit( uid ) ); // mParkPosition.setPosition( itemPos ); QMapIterator i(mTagMap); while (i.hasNext()) { i.next(); if( i.key()->isChecked() ) { mParkPosition.setTag( i.value() ); } } // qDebug () << "in the dialog: " << mParkPosition.tags() << endl; return mParkPosition; } InsertTemplDialog::~InsertTemplDialog() { QString c = mBaseWidget->mComboChapter->currentText(); if ( ! c.isEmpty() ) { KraftSettings::self()->setInsertTemplChapterName( c ); KraftSettings::self()->save(); } } void InsertTemplDialog::setCatalogChapters( const QList& chapters, const QString& selectedChap) { if ( chapters.count() > 0 ) { QStringList chapterNames; for( CatalogChapter chapter: chapters ) { if (!chapter.name().isEmpty()) chapterNames.append( chapter.name() ); } mBaseWidget->mKeepGroup->show(); mBaseWidget->mComboChapter->insertItems( -1, chapterNames ); QString selChap { selectedChap }; if (!selectedChap.isEmpty()) { mBaseWidget->mKeepGroup->setChecked(true); } else { selChap = KraftSettings::self()->insertTemplChapterName(); } if (!selChap.isEmpty() && chapterNames.contains(selChap)) mBaseWidget->mComboChapter->setCurrentIndex(mBaseWidget->mComboChapter->findText(selChap)); } } // return only a chapter if the checkbox is checked. QString InsertTemplDialog::chapter() const { QString re; if ( mBaseWidget->mKeepGroup->isChecked() ) re = mBaseWidget->mComboChapter->currentText(); return re; } /* END */ kraft-0.97/src/inserttempldialog.h000066400000000000000000000034431410616450300172520ustar00rootroot00000000000000/*************************************************************************** inserttemplatedialog.h - small dialog to insert templates into documents ------------------- begin : Sep 2006 copyright : (C) 2006 Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef INSERTTEMPLDIALOG_H #define INSERTTEMPLDIALOG_H #include #include #include "docposition.h" #include "templtopositiondialogbase.h" #include "ui_inserttmplbase.h" class QCheckBox; class InsertTemplDialog: public TemplToPositionDialogBase { Q_OBJECT public: InsertTemplDialog( QWidget* ); ~InsertTemplDialog(); void setDocPosition( DocPosition*, bool, bool ) override; DocPosition docPosition() override; void setCatalogChapters( const QList&, const QString& selectedChap) override; QString chapter() const override; protected: QComboBox *getPositionCombo() override; private: QString prepareText( const QString& input ); Ui::insertTmplBase *mBaseWidget; DocPosition mParkPosition; QMap mTagMap; }; #endif kraft-0.97/src/inserttmplbase.ui000066400000000000000000000152611410616450300167470ustar00rootroot00000000000000 insertTmplBase 0 0 584 492 0 0 18 75 true Create Item from Template Qt::PlainText false New Item Text &insert false dmAmount 999999.989999999990687 à false 999999.989999999990687 Qt::Horizontal QSizePolicy::Expanding 70 20 &after item false dmPositionCombo Qt::Horizontal QSizePolicy::Preferred 21 20 Keep this item as template for future documents true false 0 0 save in &chapter false mComboChapter Qt::Horizontal QSizePolicy::Preferred 159 20 Tags QDialogButtonBox::Cancel|QDialogButtonBox::Ok kraft-0.97/src/itemtagdialog.cpp000066400000000000000000000115471410616450300166750ustar00rootroot00000000000000/*************************************************************************** postiontagdialog.h - Edit tags of positions ------------------- begin : Aug 2008 copyright : (C) 2008 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "itemtagdialog.h" #include "defaultprovider.h" #include "tagman.h" class TagDelegate : public QItemDelegate { public: TagDelegate( QObject *parent = 0 ); void paint( QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index ) const; }; TagDelegate::TagDelegate( QObject *parent ) :QItemDelegate( parent ) { } void TagDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index ) const { //If we're in the color column if(index.column() == 1) { QColor c( index.model()->data(index, Qt::DisplayRole).toString() ); QBrush b( c ); int x = option.rect.left(); int y = option.rect.top(); int height = option.rect.height(); int width = option.rect.width(); qDrawShadeRect( painter, x+5, y+4, width-10, height-8, c, false, 1, 0, &b ); } else { QItemDelegate::paint(painter, option, index); } } // ################################################################################ ItemTagDialog::ItemTagDialog( QWidget *parent ) : QDialog( parent ) { setObjectName( "ITEM_TAG_DIALOG" ); setModal( true ); setWindowTitle( i18n("Edit Item Tags" ) ); QVBoxLayout *mainLayout = new QVBoxLayout; setLayout(mainLayout); setMinimumWidth ( 375 ); mainLayout->addWidget( new QLabel( QString::fromLatin1( "

            " ) + i18n( "Item Tags" ) + QString::fromLatin1( "

            " ), this ) ); mainLayout->addWidget( new QLabel( i18n( "Select all tags for the item should be tagged with." ), this) ); mListView = new QTreeWidget( this ); mListView->setAlternatingRowColors( true ); mListView->setItemDelegate(new TagDelegate()); mListView->setContentsMargins ( 3, 3, 3, 3 ); QPalette palette; palette.setColor(QPalette::AlternateBase, QColor( "#dffdd0" )); mListView->setPalette(palette); mListView->setHeaderHidden(true); mListView->setRootIsDecorated( false ); mListView->setColumnCount( 3 ); QStringList headers; headers << i18n( "Tag" ); headers << i18n( "Color" ); headers << i18n( "Description" ); mListView->setHeaderLabels( headers ); mListView->setSelectionMode( QAbstractItemView::NoSelection ); mListView->setColumnWidth(1, 50); mainLayout->addWidget(mListView); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); mainLayout->addWidget(buttonBox); } ItemTagDialog::~ItemTagDialog() { } void ItemTagDialog::setPositionTags( const QStringList& checkedTags ) { QStringList allTags = TagTemplateMan::self()->allTagTemplates(); foreach( QString string, allTags ) { TagTemplate templ = TagTemplateMan::self()->getTagTemplate( string ); QStringList contents; contents << templ.name(); contents << templ.color().name(); contents << templ.description(); QTreeWidgetItem *item = new QTreeWidgetItem( mListView, contents ); if(checkedTags.contains(templ.name())) item->setCheckState( 0, Qt::Checked ); else item->setCheckState( 0, Qt::Unchecked ); } } QStringList ItemTagDialog::getSelectedTags() { QStringList re; QTreeWidgetItem *item = mListView->topLevelItem( 0 ); while ( item ) { if( item->checkState( 0 ) == Qt::Checked ) { re << item->text( 0 ); } item = mListView->itemBelow( item ); } return re; } kraft-0.97/src/itemtagdialog.h000066400000000000000000000025751410616450300163430ustar00rootroot00000000000000/*************************************************************************** postiontagdialog.h - Edit tags of positions ------------------- begin : Aug 2008 copyright : (C) 2008 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef ITEMTAGDIALOG_H #define ITEMTAGDIALOG_H #include #include class QWidget; class QStringList; class QTreeWidget; class QTreeWidgetItem; class ItemTagDialog: public QDialog { Q_OBJECT public: ItemTagDialog( QWidget* ); virtual ~ItemTagDialog( ); void setPositionTags( const QStringList& checkedTags); QStringList getSelectedTags(); private: QTreeWidget* mListView; QMap mItemMap; }; #endif kraft-0.97/src/katalog.cpp000066400000000000000000000123671410616450300155060ustar00rootroot00000000000000/*************************************************************************** katalog.cpp - Abstrakte Katalogklasse ------------------- begin : Son Feb 8 2004 copyright : (C) 2004 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include #include #include #include "floskeltemplate.h" #include "dbids.h" #include "katalog.h" #include "kraftdb.h" #include "unitmanager.h" #include "timecalcpart.h" #include "fixcalcpart.h" #include "materialcalcpart.h" #include "defaultprovider.h" #include /** * constructor of a katalog, which is only a list of Floskel templates. * A name must be given, which is displayed for the root element in the * */ Katalog::Katalog(const QString& name): m_name(name), m_setID(-1), m_readOnly( false ), mChapterListNeedsRefresh( true ) { init(); } Katalog::Katalog(): m_setID(-1), m_readOnly( false ), mChapterListNeedsRefresh( true ) { init(); } void Katalog::init() { // FIXME: Catalogs could have their own locale in the future mLocale = DefaultProvider::self()->locale(); } Katalog::~Katalog() { } /** * virtuell load method for catalogs. */ int Katalog::load() { // CREATE TABLE CatalogSet( // catalogSetID INTEGER PRIMARY KEY ASC autoincrement, // name VARCHAR(255), // description VARCHAR(255), // catalogType VARCHAR(64), // sortKey INT NOT NULL // ); // QSqlQuery q; q.prepare("SELECT catalogSetID, description FROM CatalogSet WHERE name = :name"); q.bindValue(":name", m_name); q.exec(); if( q.next() ) { m_setID = q.value(0).toInt(); m_description = q.value(1).toString(); // qDebug () << "Setting catalogSetID=" << m_setID << " from name " << m_name << endl; } return 0; } QList Katalog::getKatalogChapters( bool freshup ) { if( mChapters.empty() || freshup || mChapterListNeedsRefresh ) { mChapters.clear(); // CREATE TABLE CatalogChapters( // chapterID INTEGER PRIMARY KEY ASC autoincrement, // catalogSetID INT NOT NULL, // chapter VARCHAR(255), // sortKey INT NOT NULL // ); QSqlQuery q; q.prepare("SELECT chapterID, chapter, parentChapter, description FROM CatalogChapters WHERE " "catalogSetId = :catalogSetId ORDER BY parentChapter, sortKey"); q.bindValue(":catalogSetId", m_setID); q.exec(); // qDebug () << "Selecting chapters for catalog no " << QString::number( m_setID ) << endl; while ( q.next() ) { int chapID = q.value(0).toInt(); QString chapterName = q.value(1).toString(); int parentChapter = q.value(2).toInt(); QString desc = q.value(3).toString(); // qDebug () << "Adding catalog chapter " << chapterName << " with ID " << chapID << endl; CatalogChapter c( chapID, m_setID, chapterName, parentChapter, desc ); mChapters.append( c ); } mChapterListNeedsRefresh = false; } return mChapters; } QString Katalog::chapterName(const dbID& id) { foreach( CatalogChapter chapter, mChapters ) { if( chapter.id() == id ) { return chapter.name(); } } return i18n("not found"); } dbID Katalog::chapterID( const QString& name ) { foreach( CatalogChapter chapter, mChapters ) { if( chapter.name() == name ) { return chapter.id(); } } return dbID(); } QString Katalog::getName() const { return m_name; } void Katalog::setName( const QString& n ) { m_name = n; } // Needs reimplementation in the inherited catalogs KatalogType Katalog::type() { return UnspecCatalog; } void Katalog::refreshChapterList() { mChapterListNeedsRefresh = true; } void Katalog::setChapterSortKey( const QString& chap, int key ) { // qDebug () << "Set chapter sortKey for " << chap << " to " << key << endl; QSqlQuery q; q.prepare("UPDATE CatalogChapters SET sortKey = :sortKey WHERE catalogSetID = :catalogSetID AND chapter = :chapter"); q.bindValue(":catalogSetID", m_setID); q.bindValue(":chapter", chap); q.bindValue(":sortKey", key); q.exec(); } int Katalog::chapterSortKey( const QString& chap ) { int key = -1; QSqlQuery q; q.prepare("SELECT sortKey FROM CatalogChapters WHERE chapter = :chapter"); q.bindValue(":chapter", chap); q.exec(); if(q.next()) { key = q.value(0).toInt(); } return key; } QDomDocument Katalog::toXML() { return QDomDocument(); } void Katalog::writeXMLFile() { } dbID Katalog::id() { return dbID( m_setID ); } kraft-0.97/src/katalog.h000066400000000000000000000060201410616450300151400ustar00rootroot00000000000000/*************************************************************************** katalog.h - ------------------- begin : Son Feb 8 2004 copyright : (C) 2004 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef KATALOG_H #define KATALOG_H #include #include #include "floskeltemplate.h" #include "catalogchapter.h" #include "dbids.h" /** *@author Klaas Freitag */ typedef enum {UnspecCatalog, MaterialCatalog, TemplateCatalog, PlantCatalog } KatalogType; class QDomDocument; class KRAFTCAT_EXPORT Katalog { public: Katalog(); Katalog(const QString& ); virtual ~Katalog(); virtual int load(); /** * reload the item with the given id or the entire catalog in case * the id is not valid. */ virtual void reload( dbID ) = 0; virtual void setName( const QString& ); virtual QString getName( ) const; /** find the ID for the corresponding chapter */ virtual dbID chapterID(const QString&); /** get a list of all existing chapters of this catalog */ virtual QList getKatalogChapters( bool freshup = false ); /** get the chapter name for the given ID */ virtual QString chapterName(const dbID&); /** set the sortkey for a chapter. Note: The organisation of the sortkeys * between the different chapters is up to the caller of this method. */ virtual void setChapterSortKey( const QString&, int ); virtual int chapterSortKey( const QString& ); /** * returns the KatalogType. */ virtual KatalogType type(); /** get the amount of entries in a chapter or the entire catalog */ virtual int getEntriesPerChapter( const CatalogChapter& ) = 0; bool isReadOnly() { return m_readOnly; } void setReadOnly( bool state ) { m_readOnly = state; } void refreshChapterList(); virtual QDomDocument toXML(); virtual void writeXMLFile(); virtual void recordUsage(int id) = 0; dbID id(); QLocale *locale() { return mLocale; } protected: QList mChapters; QString m_name; QString m_description; int m_setID; bool m_readOnly; bool mChapterListNeedsRefresh; QLocale *mLocale; private: void init(); }; #endif kraft-0.97/src/kataloglistview.cpp000066400000000000000000000406721410616450300172750ustar00rootroot00000000000000/*************************************************************************** kataloglistview.cpp - ------------------- begin : Son Feb 8 2004 copyright : (C) 2004 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include #include #include #include #include "kraftglobals.h" #include "katalog.h" #include "katalogman.h" #include "kataloglistview.h" #include "defaultprovider.h" #include "materialcalcpart.h" #include "stockmaterial.h" #include "templkatalog.h" #include "timecalcpart.h" #include "dbids.h" #include "catalogchapter.h" #include "addeditchapterdialog.h" KatalogListView::KatalogListView( QWidget *parent ) : QTreeWidget(parent), mCheckboxes( false ), m_root(0), mSortChapterItem(0), mMenu(0) { setSelectionMode(QAbstractItemView::SingleSelection ); setAlternatingRowColors( true ); //Initialize common style options QPalette palette; palette.setColor( QPalette::AlternateBase, QColor("#e0fdd1") ); setPalette( palette ); setRootIsDecorated(false); setAnimated(true); // header()->setResizeMode(QHeaderView::ResizeToContents); // custom style const QString style = DefaultProvider::self()->getStyleSheet( "templcatalog"); setStyleSheet( style ); // Drag and Drop for normal mode, is changed in setSelectFromMode setSelectionMode( QAbstractItemView::SingleSelection ); setDragDropMode( QAbstractItemView::InternalMove ); setDragEnabled( true ); setAcceptDrops( true ); // currently only internal moves setDropIndicatorShown( true ); // setSorting(-1); mMenu = new QMenu( this ); mChapterFont = font(); mChapterFont.setBold( true ); connect( this, SIGNAL(itemActivated( QTreeWidgetItem*,int )), this, SLOT( slotItemEntered( QTreeWidgetItem*, int ))); } KatalogListView::~KatalogListView() { } QMenu *KatalogListView::contextMenu() { return mMenu; } void KatalogListView::addCatalogDisplay( const QString& name) { m_catalogName = name; } void KatalogListView::contextMenuEvent( QContextMenuEvent * event ) { mMenu->popup( event->globalPos() ); } Katalog* KatalogListView::catalog() { return KatalogMan::self()->getKatalog( m_catalogName ); } void KatalogListView::setSelectFromMode() { setSelectionMode( QAbstractItemView::SingleSelection /* NoSelection */ ); // FIXME: Allow multiple selections later setDragDropMode( QAbstractItemView::NoDragDrop ); setDragEnabled( false ); setAcceptDrops( false ); // currently only internal moves setDropIndicatorShown( false ); setCheckboxes( true ); } void KatalogListView::setupChapters() { Katalog *cat = catalog(); if( ! cat ) return; if( m_root ) { delete m_root; mChapterDict.clear(); } // qDebug () << "Creating root item!" << endl; QStringList list; list << cat->getName(); m_root = new QTreeWidgetItem( this, list ); m_root->setIcon( 0, QIcon("kraft")); m_root->setExpanded(true); m_root->setFont( 0, mChapterFont ); m_root->setToolTip( 0, QString() ); repaint(); const QList chapters = cat->getKatalogChapters( true ); // qDebug () << "Have count of chapters: " << chapters.size() << endl; QList strayCats; foreach( CatalogChapter chapter, chapters ) { QTreeWidgetItem *item = tryAddingCatalogChapter( chapter ); if( ! item ) { strayCats.append( chapter ); } else { // qDebug () << "Creating katalog chapter item for " << chapter.name() << endl; } } int oldStrayCatCount = strayCats.count() + 1; // to survive the first while condition while( strayCats.count() && strayCats.count() < oldStrayCatCount ) { QList newStrayCats; oldStrayCatCount = strayCats.count(); // loop as long as the overall number of straycats goes down in every round foreach( CatalogChapter chapter, strayCats ) { QTreeWidgetItem *katItem = tryAddingCatalogChapter( chapter ); if( katItem ) { // qDebug () << "Successfully added catalog chapter from strayCats"; } else { newStrayCats.append( chapter ); // qDebug () << "Failed to add a catalog chapter from stryCats"; } } strayCats = newStrayCats; } } QTreeWidgetItem *KatalogListView::tryAddingCatalogChapter( const CatalogChapter& chapter ) { int parentChapter = chapter.parentId().toInt(); int id = chapter.id().toInt(); QTreeWidgetItem *katItem = 0; if( parentChapter == 0 ) { katItem = new QTreeWidgetItem( m_root, QStringList( chapter.name() ) ); } else { if( mChapterDict.contains( parentChapter ) ) { katItem = new QTreeWidgetItem( mChapterDict[parentChapter], QStringList( chapter.name() ) ); katItem->setToolTip( 0, chapter.description() ); } } if( katItem ) { mChapterDict.insert( id, katItem ); katItem->setToolTip( 0, chapter.description() ); // katItem->setIcon( 0, chapter.icon() ); katItem->setFont( 0, mChapterFont ); // Store the parent-ID in the item data m_dataDict[katItem] = new CatalogChapter( chapter ); if ( mOpenChapters.contains( chapter.name() ) ) { katItem->setExpanded( true ); } } return katItem; } CatalogTemplateList KatalogListView::selectedTemplates() { CatalogTemplateList templates; if( mCheckboxes ) { // checkbox mode // add the checkboxed items. QTreeWidgetItemIterator it( this, QTreeWidgetItemIterator::Checked ); while (*it) { QTreeWidgetItem *item = *it; if( ! (isChapter( item ) || isRoot(item )) ) { // a template, not a chapter. void *data = itemData( item ); if( data ) templates.append( static_cast( data )); } item->setCheckState( 0, Qt::Unchecked ); ++it; } } // if no items were added yet, lets go for the selected ones. if( ! mCheckboxes || templates.isEmpty() ) { QList items = selectedItems(); foreach( QTreeWidgetItem* item, items ) { if( isChapter(item) && !isRoot(item) ) { // for chapters, the children are lined up. int kidCnt = item->childCount(); for( int i=0; i < kidCnt; i++ ) { QTreeWidgetItem *kid = item->child(i); if( kid && !isChapter(kid) ) { // only add normal templates. void *data = itemData(kid); if( data ) templates.append( static_cast(data)); } } } if( !(isChapter(item) || isRoot(item))) { void *data = itemData( item ); if( data ) templates.append( static_cast(data) ); } } } return templates; } QString KatalogListView::selectedCatalogChapter() { QList items = selectedItems(); QString chap; if (items.size() == 1) { QTreeWidgetItem *item = items.first(); if (!isChapter(item) && !isRoot(item)) { item = item->parent(); } if (isChapter(item)) { chap = static_cast(itemData(item))->name(); } } return chap; } void* KatalogListView::itemData( QTreeWidgetItem *item ) { if ( item && m_dataDict.contains( item ) ) { return m_dataDict[item]; } return 0; } void* KatalogListView::currentItemData() { return itemData( currentItem() ); } void KatalogListView::removeTemplateItem( QTreeWidgetItem *item ) { if( item == mSortChapterItem ) mSortChapterItem = 0; QHashIterator it( mChapterDict ); while( it.hasNext() ) { it.next(); if ( it.value() == item ) { mChapterDict.remove(it.key()); break; } } m_dataDict.remove( item ); delete item; } bool KatalogListView::isChapter( QTreeWidgetItem *item ) { QHashIterator it( mChapterDict ); while( it.hasNext() ) { it.next(); if ( it.value() == item ) return true; } return false; } bool KatalogListView::isRoot( QTreeWidgetItem *item ) { return (item == m_root ); } void KatalogListView::setCheckboxes( bool cb ) { mCheckboxes = cb; } void KatalogListView::slotFreshupItem( QTreeWidgetItem*, void *, bool ) { } void KatalogListView::slotEditCurrentChapter() { QTreeWidgetItem *item = currentItem(); if( ! isChapter( item )) { // qDebug () << "Can only edit chapters!" << endl; return; } CatalogChapter *chap = static_cast( itemData( item ) ); AddEditChapterDialog dia( this ); dia.setEditChapter( *chap ); if( dia.exec() ) { QString name = dia.name(); QString desc = dia.description(); if( name != chap->name() || desc != chap->description() ) { chap->setName( name ); chap->setDescription( desc ); chap->saveNameAndDesc(); item->setText( 0, name); item->setToolTip( 0, desc ); catalog()->refreshChapterList(); } } } void KatalogListView::slotRemoveCurrentChapter() { QTreeWidgetItem *item = currentItem(); if( ! isChapter( item )) { // qDebug () << "Can only remove chapters here!" << endl; } if( item->childCount() > 0 ) { QMessageBox msgBox; msgBox.setText(i18n( "A catalog chapter can not be deleted as long it has children." )); msgBox.setInformativeText(i18n("Chapter can not be deleted")); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setDefaultButton(QMessageBox::Ok); msgBox.exec(); return; } else { CatalogChapter *chap = static_cast( itemData( item ) ); if( chap ) { int id = chap->id().toInt(); if( chap->removeFromDB() ) { delete item; mChapterDict.remove(id); delete chap; } } } } void KatalogListView::slotCreateNewChapter() { QTreeWidgetItem *parentItem = currentItem(); if( ! (isChapter( parentItem ) || isRoot( parentItem ) ) ) { // qDebug () << "Not an chapter item selected, returning"; return; } AddEditChapterDialog dia( this ); dbID parentId = 0; if( ! isRoot( parentItem ) ) { CatalogChapter *parentChapter = static_cast(currentItemData()); dia.setParentChapter( *parentChapter ); parentId = parentChapter->id(); } if( dia.exec() ) { QString name = dia.name(); QString desc = dia.description(); CatalogChapter c; c.setName( name ); c.setDescription( desc ); c.setCatalogSetId( catalog()->id() ); c.setParentId( parentId ); c.save(); catalog()->refreshChapterList(); QTreeWidgetItem *newItem = tryAddingCatalogChapter( c ); if( newItem ) { this->scrollToItem( newItem ); this->setCurrentItem( newItem ); } } } void KatalogListView::dropEvent( QDropEvent *event ) { if (event->source() == this && (event->dropAction() == Qt::MoveAction || dragDropMode() == QAbstractItemView::InternalMove)) { QModelIndex topIndex; int col = -1; int row = -1; QModelIndex dropIndx = indexAt( event->pos() ); QTreeWidgetItem *droppedOnItem = itemFromIndex( dropIndx ); if( ! droppedOnItem ) { event->ignore(); return; } row = dropIndx.row(); col = dropIndx.column(); topIndex = dropIndx.parent(); QList idxs = selectedIndexes(); QList indexes; for (int i = 0; i < idxs.count(); i++) indexes.append(idxs.at(i)); if (indexes.contains(topIndex)) return; // When removing items the drop location could shift QPersistentModelIndex dropRow = model()->index(row, col, topIndex); // Remove the items QList taken; for (int i = indexes.count() - 1; i >= 0; --i) { QTreeWidgetItem *parent = itemFromIndex(indexes.at(i)); if (!parent || !parent->parent()) { taken.append(takeTopLevelItem(indexes.at(i).row())); } else { taken.append(parent->parent()->takeChild(indexes.at(i).row())); } } // insert them back in at their new positions for (int i = 0; i < indexes.count(); ++i) { // Either at a specific point or appended QTreeWidgetItem *parent = itemFromIndex(topIndex); if (row == -1) { if( isChapter( droppedOnItem ) || isRoot( droppedOnItem )) { parent = droppedOnItem; parent->insertChild(parent->childCount(), taken.takeFirst()); } // the parent chap has changed. } else { int r = 1+(dropRow.row() >= 0 ? dropRow.row() : row); // insert behind the row element dbID newParentId; // The item was dropped on a chapter item or root. That causes a change of the // parent chapter. if( isChapter( droppedOnItem )|| isRoot( droppedOnItem )) { parent = droppedOnItem; // the parent id has to be updated for all inserted items CatalogChapter *parentChap = static_cast(itemData(parent)); if( parentChap ) { newParentId = parentChap->id(); } else { newParentId = 0; } // New chapter is inserted after the other subcatalogs, compute the index int cnt = 0; while( cnt < parent->childCount() && isChapter(parent->child(cnt))) { cnt++; } r = cnt; // the parent chapter has changed. } else { // the item was dropped on another item. Still the parent might have changed. CatalogTemplate *tmpl = static_cast(itemData(droppedOnItem)); newParentId = tmpl->chapterId(); } if( parent ) { QTreeWidgetItem *dropItem = taken.takeFirst(); if( newParentId.isOk() ) { if( isChapter( dropItem ) ) { CatalogChapter* chapDrop = static_cast(itemData(dropItem)); chapDrop->reparent( newParentId ); } else if( isRoot( dropItem )) { CatalogChapter* chapDrop = static_cast(itemData(dropItem)); chapDrop->reparent( 0 ); } else { // ordinary template, set a new parent chapter CatalogTemplate *tmpl = static_cast(itemData(dropItem)); if( tmpl && tmpl->chapterId() != newParentId ) { tmpl->setChapterId( newParentId, true ); } } } parent->insertChild( qMin(r, parent->childCount()), dropItem ); mSortChapterItem = parent; } } event->accept(); // Don't want QAbstractItemView to delete it because it was "moved" we already did it event->setDropAction(Qt::CopyAction); } } QTreeView::dropEvent(event); QTimer::singleShot( 0, this, SLOT( slotUpdateSequence() ) ); } void KatalogListView::slotUpdateSequence() { // check the detail implementations in inherited classes // qDebug () << "Updating sequence"; if( mSortChapterItem ) mSortChapterItem->setExpanded( true ); mSortChapterItem = 0; } void KatalogListView::slotItemEntered( QTreeWidgetItem *item, int ) { if( !item ) return; if( isRoot( item )) { // qDebug () << "Is a root item "; } else if( isChapter(item )) { // qDebug () << "Is a chapter item "; } else { CatalogTemplate *tmpl = static_cast(itemData(item)); // qDebug () << "hoovering this template: " << tmpl; emit templateHoovered( tmpl ); } } void KatalogListView::slotRedraw() { // remember all currently open chapters QHashIterator it( mChapterDict ); while( it.hasNext() ) { it.next(); if ( it.value()->isExpanded() ) { // qDebug () << "Adding open Chapter " << it.value()->text( 0 ) << endl; mOpenChapters << it.value()->text( 0 ); } } clear(); m_root = 0; m_dataDict.clear(); mChapterDict.clear(); addCatalogDisplay( m_catalogName ); mOpenChapters.clear(); } kraft-0.97/src/kataloglistview.h000066400000000000000000000060171410616450300167350ustar00rootroot00000000000000/*************************************************************************** floskellistview.h - ------------------- begin : Son Feb 8 2004 copyright : (C) 2004 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef KATALOGLISTVIEW_H #define KATALOGLISTVIEW_H #include #include #include #include #include "kraftcat_export.h" #include "catalogtemplate.h" /** *@author Klaas Freitag */ class TemplKatalog; class QPixmap; class DocPosition; class Katalog; class CatalogChapter; class KRAFTCAT_EXPORT KatalogListView : public QTreeWidget { Q_OBJECT public: KatalogListView( QWidget *parent = 0 ); ~KatalogListView(); virtual void addCatalogDisplay( const QString& ); virtual void* currentItemData(); virtual void* itemData( QTreeWidgetItem* ); CatalogTemplateList selectedTemplates(); bool isChapter(QTreeWidgetItem*); bool isRoot(QTreeWidgetItem*); QString selectedCatalogChapter(); virtual void setupChapters(); QMenu *contextMenu(); // virtual DocPosition itemToDocPosition( QListViewItem *it = 0 ) = 0; // Save the header state of the tree view virtual void saveState() = 0; signals: void templateHoovered( CatalogTemplate* ); void sequenceUpdateProgress( int ); void sequenceUpdateMaximum( int ); public slots: virtual void setCheckboxes( bool ); virtual void slotFreshupItem( QTreeWidgetItem*, void*, bool remChildren = false ); virtual void slotCreateNewChapter(); virtual void slotEditCurrentChapter(); virtual void slotRemoveCurrentChapter(); virtual void contextMenuEvent( QContextMenuEvent* ); virtual void slotRedraw(); virtual void setSelectFromMode(); virtual void removeTemplateItem( QTreeWidgetItem* ); protected slots: virtual void slotUpdateSequence(); virtual void slotItemEntered( QTreeWidgetItem*, int); protected: virtual Katalog* catalog(); void dropEvent( QDropEvent* ); bool mCheckboxes; QTreeWidgetItem* tryAddingCatalogChapter( const CatalogChapter& ); QTreeWidgetItem *m_root; QHash m_dataDict; QHash mChapterDict; QString m_catalogName; QStringList mOpenChapters; QTreeWidgetItem *mSortChapterItem; QMenu *mMenu; QFont mChapterFont; }; #endif kraft-0.97/src/katalogman.cpp000066400000000000000000000114101410616450300161660ustar00rootroot00000000000000/*************************************************************************** katalogman - Catalog manager ------------------- begin : 2004-12-09 copyright : (C) 2004 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ // include files for Qt #include #include #include #include "kraftdb.h" #include "katalogman.h" #include "katalog.h" #include "templkatalog.h" #include "materialkatalogview.h" Q_GLOBAL_STATIC(KatalogMan, mSelf) KatalogMan *KatalogMan::self() { return mSelf; } KatalogMan::KatalogMan( ) { } KatalogMan::~KatalogMan( ) { } QStringList KatalogMan::allKatalogNames() { QStringList list; QSqlQuery q( "SELECT name FROM CatalogSet ORDER BY sortKey, name" ); while( q.next() ) { list << q.value( 0 ).toString(); } return list; } QString KatalogMan::catalogTypeString( const QString& catName ) { QString res; if ( !catName.isEmpty() ) { QSqlQuery q; q.prepare( "SELECT catalogType FROM CatalogSet where name=:name" ); q.bindValue( ":name", catName ); if ( q.exec() && q.next() ) { res = q.value( 0 ).toString(); } } return res; } void KatalogMan::registerKatalog( Katalog *k ) { Katalog* kat = m_katalogDict[k->getName()]; if( kat ) { qWarning() << "Katalog with same name already here -> deleting!" << endl; delete kat; } else { // not found, try to open it // qDebug () << "Katalog " << k->getName() << " registered and loading..." << endl; m_katalogDict.insert( k->getName(), k ); k->load (); } } Katalog *KatalogMan::getKatalog(const QString& name) { Katalog* kat = m_katalogDict[name]; if( !kat ) { // qDebug () << "No katalog " << name << " found" << endl; } else { // qDebug() << "Returning existing katalog " << name << endl; } return kat; } // this is called after an template has been changed in the database. void KatalogMan::notifyKatalogChange( Katalog* k, dbID ) { // FIXME: More efficient catalog reloading. if ( k ) { const QString name = k->getName(); k->reload( dbID() ); QList< QPointer > views = mKatalogListViews.values(name); KatalogListView *view; QListIterator< QPointer > i( views ); while ( i.hasNext() ) { view = i.next(); if( view ) { view->slotRedraw(); } } } } void KatalogMan::registerKatalogListView( const QString& name, KatalogListView *view ) { QList< QPointer > views = mKatalogListViews.values(name); if ( ! views.contains( view ) ) { mKatalogListViews.insert(name, QPointer(view)); } } /* * currently, there is only one catalog of type Template by design, see * for example in templatesaverdb.cpp or the database design where only * one template catalog is in use. */ Katalog* KatalogMan::defaultTemplateCatalog() { QHashIterator it( m_katalogDict ); // See QDictIterator while ( it.hasNext() ) { it.next(); Katalog *k = it.value(); if ( k->type() == TemplateCatalog ) { // qDebug () << "Found default template catalog: " << k->getName() << endl; return k; } } return 0; } KatalogMan::CatalogDetails KatalogMan::catalogDetails( const QString& catName ) { KatalogMan::CatalogDetails details; QString sql; QString catTypeString = KatalogMan::catalogTypeString( catName ); if( catTypeString == QLatin1String("MaterialCatalog") ) { sql = "SELECT count(matID), COUNT(distinct chapterID), MAX(modifyDate) FROM stockMaterial"; } else if( catTypeString == QLatin1String("TemplCatalog") ) { sql = "SELECT count(TemplID), COUNT(distinct chapterID), MAX(modifyDatum) FROM Catalog"; } QSqlQuery q; q.prepare( sql ); if ( !sql.isEmpty() && q.exec() && q.next() ) { details.countEntries = q.value( 0 ).toInt(); details.countChapters = q.value( 1 ).toInt(); details.maxModDate = q.value( 2 ).toDateTime(); } return details; } /* END */ kraft-0.97/src/katalogman.h000066400000000000000000000040321410616450300156350ustar00rootroot00000000000000/*************************************************************************** katalogman - ------------------- begin : 2004-12-09 copyright : (C) 2004 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef _KATALOGMAN_H #define _KATALOGMAN_H #include #include "katalog.h" #include "kataloglistview.h" #include "kraftcat_export.h" // include files /** * */ class QStringList; class KRAFTCAT_EXPORT KatalogMan : public QObject { public: ~KatalogMan(); static KatalogMan *self(); struct CatalogDetails { int countEntries; int countChapters; QDateTime maxModDate; }; QStringList allKatalogNames(); Katalog* getKatalog(const QString&); Katalog* defaultTemplateCatalog(); void registerKatalog( Katalog* ); QString catalogTypeString( const QString& catName ); void notifyKatalogChange( Katalog*, dbID ); CatalogDetails catalogDetails( const QString& catName ); // register a view for a catalog identified by its name. void registerKatalogListView( const QString&, KatalogListView* ); // static KatalogMan *mSelf; KatalogMan(); private: QHash m_katalogDict; QMultiMap< QString, QPointer > mKatalogListViews; }; #endif /* END */ kraft-0.97/src/katalogview.cpp000066400000000000000000000306171410616450300163770ustar00rootroot00000000000000/*************************************************************************** katalogview.cpp ------------------- begin : 2005 copyright : (C) 2005 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include // include files for QT #include #include #include #include #include #include // application specific includes #include "katalogview.h" #include "katalog.h" #include "floskeltemplate.h" #include "kataloglistview.h" #include "flostempldialog.h" #include "templkatalog.h" #include "filterheader.h" #include "docposition.h" #include "katalogman.h" #include "defaultprovider.h" #include "format.h" #include "kraftsettings.h" #define ID_STATUS_MSG 1 KatalogView::KatalogView( QWidget* parent, const char* ) : QMainWindow(parent, nullptr), m_acEditChapter(nullptr), m_acEditItem(nullptr), m_acNewItem(nullptr), m_acDeleteItem(nullptr), m_acExport(nullptr), m_filterHead(nullptr), m_editListViewItem(nullptr), mTemplateText(nullptr), mTemplateStats(nullptr) { setObjectName( "catalogeview" ); //We don't want to delete this view when we close it! setAttribute(Qt::WA_DeleteOnClose, false); } void KatalogView::init(const QString& katName ) { m_katalogName = katName; initActions(); /////////////////////////////////////////////////////////////////// // set up a vertical layout box QWidget *w = new QWidget(this); QBoxLayout *box = new QVBoxLayout(w); // start to set up the listview createCentralWidget(box, w); KatalogListView *listview = getListView(); if( ! listview ) { // qDebug () << "ERROR: No listview created !!!" << endl; } else { QHBoxLayout *horizLay = new QHBoxLayout; m_filterHead = new FilterHeader(w, listview); horizLay->insertWidget(0, m_filterHead, 2); horizLay->addStretch(1); box->insertLayout(0, horizLay); connect( listview, SIGNAL(currentItemChanged ( QTreeWidgetItem*, QTreeWidgetItem*)), this, SLOT(slTreeviewItemChanged( QTreeWidgetItem*, QTreeWidgetItem*)) ); connect( listview, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), this, SLOT(slEditTemplate())); connect( listview, SIGNAL(templateHoovered(CatalogTemplate*)), this, SLOT(slotShowTemplateDetails( CatalogTemplate*))); // Populate the context Menu (listview->contextMenu())->addAction( m_acEditItem ); (listview->contextMenu())->addAction( m_acNewItem ); (listview->contextMenu())->addAction( m_acDeleteItem ); (listview->contextMenu())->addSeparator(); (listview->contextMenu())->addAction( m_acAddChapter ); (listview->contextMenu())->addAction( m_acEditChapter ); (listview->contextMenu())->addAction( m_acRemChapter ); getKatalog( katName ); listview->addCatalogDisplay( katName ); } setCentralWidget(w); m_editListViewItem = nullptr; // qDebug () << "Getting katalog!" << katName << endl; // setAutoSaveSettings( QString::fromLatin1( "CatalogWindow" ), true ); } void KatalogView::createCentralWidget(QBoxLayout *box, QWidget* ) { mTemplateText = new QLabel( "Nothing selected."); box->addWidget( mTemplateText ); QHBoxLayout *hb = new QHBoxLayout; box->addLayout( hb ); mTemplateStats = new QLabel( ); mProgress = new QProgressBar; hb->addWidget( mTemplateStats ); hb->addStretch(); hb->addWidget( mProgress ); connect( getListView(), SIGNAL( sequenceUpdateMaximum( int )), mProgress, SLOT( setMaximum(int) ) ); connect( getListView(), SIGNAL( sequenceUpdateProgress( int ) ), this, SLOT( setProgressValue(int) ) ); const QByteArray state = windowState(); restoreState(state); const QByteArray geo = windowGeo(); restoreGeometry(geo); } void KatalogView::setProgressValue( int val ) { if( ! mProgress ) return; mProgress->setValue( val ); if( val == mProgress->maximum() ) { QTimer::singleShot( 3000, mProgress, SLOT(reset())); } } KatalogView::~KatalogView() { } Katalog* KatalogView::getKatalog( const QString& name ) { KatalogMan::self()->registerKatalogListView( name, getListView() ); return nullptr; } void KatalogView::initActions() { QIcon newIcon = QIcon::fromTheme( "folder-documents"); m_acEditChapter = new QAction(newIcon, i18n("Edit Sub chapter"), this); m_acEditChapter->setShortcut( Qt::CTRL + Qt::Key_S); m_acEditChapter->setStatusTip(i18n("Edit a catalog sub chapter")); connect(m_acEditChapter, &QAction::triggered, this, &KatalogView::slEditSubChapter); newIcon = QIcon::fromTheme( "document-edit"); m_acAddChapter = new QAction(newIcon, i18n("Add a sub chapter"), this); m_acAddChapter->setShortcut( Qt::CTRL + Qt::Key_A); m_acAddChapter->setStatusTip(i18n("Add a sub chapter below the selected one")); connect(m_acAddChapter, &QAction::triggered, this, &KatalogView::slAddSubChapter); newIcon = QIcon::fromTheme( "document-edit"); m_acRemChapter = new QAction(newIcon, i18n("Remove a sub chapter"), this); m_acRemChapter->setShortcut( Qt::CTRL + Qt::Key_R); m_acRemChapter->setStatusTip(i18n("Remove a sub chapter")); connect(m_acRemChapter, &QAction::triggered, this, &KatalogView::slRemoveSubChapter); newIcon = QIcon::fromTheme( "document-edit"); m_acEditItem = new QAction(newIcon, i18n("Edit Template"), this); m_acEditItem->setShortcut( Qt::CTRL + Qt::Key_T); m_acEditItem->setStatusTip(i18n("Opens the editor window for templates to edit the selected one")); m_acEditItem->setEnabled(false); connect(m_acEditItem, &QAction::triggered, this, &KatalogView::slEditTemplate); newIcon = QIcon::fromTheme( "document-new"); m_acNewItem = new QAction(newIcon, i18n("New template"), this); m_acNewItem->setShortcut( Qt::CTRL + Qt::Key_N); m_acNewItem->setStatusTip(i18n("Opens the editor window for templates to enter a new template")); connect(m_acNewItem, &QAction::triggered, this, &KatalogView::slNewTemplate); m_acNewItem->setEnabled(true); newIcon = QIcon::fromTheme( "document-delete"); m_acDeleteItem = new QAction(newIcon, i18n("Delete template"), this); m_acDeleteItem->setShortcut( QKeySequence::Delete); m_acDeleteItem->setStatusTip(i18n("Deletes the template")); connect(m_acDeleteItem, &QAction::triggered, this, &KatalogView::slDeleteTemplate); m_acDeleteItem->setEnabled(true); newIcon = QIcon(); // QIcon::fromTheme( "document-delete"); m_acExport = new QAction(newIcon, i18n("Export catalog"), this); m_acExport->setShortcut( Qt::Key_E); m_acExport->setStatusTip(i18n("Export the whole catalog as XML encoded file")); connect(m_acExport, &QAction::triggered, this, &KatalogView::slExport); m_acExport->setEnabled(false); newIcon = QIcon(); // QIcon::fromTheme( "document-delete"); m_acImport = new QAction(newIcon, i18n("Import catalog"), this); m_acExport->setShortcut( Qt::Key_I); m_acExport->setStatusTip(i18n("Import a catalog from a XML file")); connect(m_acExport, &QAction::triggered, this, &KatalogView::slImport); m_acExport->setEnabled(false); QMenu *catalogMenu = menuBar()->addMenu(i18n("&Catalog")); catalogMenu->addAction(m_acAddChapter); catalogMenu->addAction(m_acEditChapter); catalogMenu->addAction(m_acRemChapter); catalogMenu->addSeparator(); catalogMenu->addAction(m_acNewItem); catalogMenu->addAction(m_acEditItem); catalogMenu->addAction(m_acDeleteItem); #ifdef HAVE_EXPORT catalogMenu->addSeparator(); catalogMenu->addAction(m_acExport); catalogMenu->addAction(m_acImport); #endif } void KatalogView::openDocumentFile(const QUrl& ) { slotStatusMsg(i18n("Opening file...")); slotStatusMsg(i18n("Ready.")); } void KatalogView::closeEvent( QCloseEvent *event ) { slotStatusMsg(i18n("Exiting...")); if( event ) event->accept(); } void KatalogView::slotSaveState() { getListView()->saveState(); // saves the header state const QByteArray state = saveState().toBase64(); saveWindowState(state); const QByteArray geo = saveGeometry().toBase64(); saveWindowGeo(geo); } void KatalogView::slotStatusMsg(const QString &text) { if( text.isEmpty() ) { statusBar()->clearMessage(); } else { statusBar()->showMessage(text, 30*1000 /* milliseconds timeout */ ); } } void KatalogView::slTreeviewItemChanged( QTreeWidgetItem *newItem, QTreeWidgetItem * /* prevItem */ ) { KatalogListView *listview = getListView(); if( !listview ) return; if( ! newItem ) return; bool itemEdit = true; bool itemNew = true; bool chapterNew = false; bool chapterEdit = false; if( listview->isRoot(newItem) ) { // we have the root item, not editable itemEdit = false; itemNew = false; chapterNew = true; } else if( listview->isChapter(newItem) ) { itemEdit = false; chapterNew = true; chapterEdit = true; } m_acEditItem->setEnabled(itemEdit); m_acDeleteItem->setEnabled(itemEdit); m_acNewItem->setEnabled( itemNew ); m_acAddChapter->setEnabled( chapterNew ); m_acEditChapter->setEnabled( chapterEdit ); m_acRemChapter->setEnabled( chapterEdit ); } void KatalogView::slExport() { slotStatusMsg(i18n("Exporting file...")); Katalog *k = getKatalog(m_katalogName); if(k) k->writeXMLFile(); slotStatusMsg(i18n("Ready.")); } void KatalogView::slImport() { slotStatusMsg(i18n("Importfile... (not yet implemented)")); } void KatalogView::slAddSubChapter() { slotStatusMsg( i18n("Creating a new sub chapter...")); KatalogListView *listview = getListView(); if( listview ) listview->slotCreateNewChapter(); slotStatusMsg( i18n("Ready.")); } void KatalogView::slEditSubChapter() { slotStatusMsg( i18n("Editing a sub chapter...")); KatalogListView *listview = getListView(); if( listview ) listview->slotEditCurrentChapter(); slotStatusMsg( i18n("Ready.")); } void KatalogView::slRemoveSubChapter() { slotStatusMsg( i18n("Removing a sub chapter...")); KatalogListView *listview = getListView(); if( listview ) listview->slotRemoveCurrentChapter(); slotStatusMsg( i18n("Ready.")); } void KatalogView::slotShowTemplateDetails( CatalogTemplate *tmpl ) { if( ! (mTemplateText && mTemplateStats) ) { // qDebug () << "Hoover-Text: No label ready."; return; } if( ! tmpl ) { mTemplateText->setText( QString() ); mTemplateStats->setText( QString() ); return; } QString t; QString flos = tmpl->getText(); QFontMetrics fm( mTemplateText->font() ); int w = mTemplateText->width() - 30; t = QString( "%1").arg( fm.elidedText(flos, Qt::ElideMiddle, w ) ); mTemplateText->setText( t ); int useCount = tmpl->useCounter(); t = ""; t += i18n("") .arg( Format::toDateTimeString(tmpl->enterDate(), KraftSettings::self()-> dateFormat()) ); if (useCount > 0) { t += i18n("" ) .arg( Format::toDateTimeString(tmpl->lastUsedDate(), KraftSettings::self()-> dateFormat())); } t += QStringLiteral(""); const QDateTime dt = tmpl->modifyDate(); if (dt.isValid()) { t += i18n("") .arg( Format::toDateTimeString( dt, KraftSettings::self()-> dateFormat()) ); if (useCount > 0) { t += i18n("" ) .arg(useCount); } t += QStringLiteral(""); } t += "
            Created at:%1  Last used:%1
            Modified at:%1  Use Count:%1
            "; // qDebug() << "Hoover-String: " << t; mTemplateStats->setText( t ); } kraft-0.97/src/katalogview.h000066400000000000000000000112241410616450300160350ustar00rootroot00000000000000/*************************************************************************** katalogview.h ------------------- begin : 2005 copyright : (C) 2005 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef KATALOGVIEW_H #define KATALOGVIEW_H #include #include #include #include "kraftcat_export.h" class KatalogListView; class Katalog; class FilterHeader; class CatalogWidget; class QBoxLayout; class QActionMenu; class DocPosition; class CalcPartList; class QTreeWidgetItem; class QLabel; class QProgressBar; class CatalogTemplate; /** * The base class for Kraft katalog view. * * @author Klaas Freitag * @version $Id$ */ class KRAFTCAT_EXPORT KatalogView : public QMainWindow { Q_OBJECT public: /** construtor of a catalog view. * Note: init must be called immediately after instanciating a inherited * class of KatalogView */ KatalogView(QWidget* parent=0, const char* name=0); virtual ~KatalogView(); /** * create a special listview for the kind of catalog. This method must * be overwritten in inherited catalog view classes. */ virtual void createCentralWidget(QBoxLayout*, QWidget*); virtual KatalogListView* getListView(){return 0;}; virtual void init( const QString& ); protected: virtual Katalog* getKatalog( const QString& ); public slots: /** clears the document in the current view to reuse it as the new document */ void openDocumentFile(const QUrl &url); /** changes the statusbar contents for the standard label permanently, used to indicate current actions. * @param text the text that is displayed in the statusbar */ void slotStatusMsg(const QString &text); virtual void slTreeviewItemChanged( QTreeWidgetItem *, QTreeWidgetItem *); void slExport(); void slImport(); // virtual void slEditChapters(); virtual void slAddSubChapter(); virtual void slEditSubChapter(); virtual void slRemoveSubChapter(); virtual void slNewTemplate() = 0; virtual void slEditTemplate() = 0; virtual void slDeleteTemplate() = 0; void slotShowTemplateDetails( CatalogTemplate*); void setProgressValue( int ); void closeEvent( QCloseEvent *event ); protected slots: void slotSaveState(); protected: /** the configuration object of the application */ QAction* m_acEditChapter; QAction* m_acAddChapter; QAction* m_acRemChapter; QAction* m_acEditItem; QAction* m_acNewItem; QAction* m_acDeleteItem; QAction* m_acExport; QAction* m_acImport; // KToggleAction* viewToolBar; // KToggleAction* viewStatusBar; QString m_katalogName; FilterHeader *m_filterHead; QTreeWidgetItem *m_editListViewItem; QLabel *mTemplateText; QLabel *mTemplateStats; QProgressBar *mProgress; // Fills the DocPosition with the data from the currently selected item in the view virtual bool currentItemToDocPosition( DocPosition& ){ return false; } /** initializes the QActions of the application */ void initActions(); /** sets up the statusbar for the main window by initialzing a statuslabel. */ /** initializes the document object of the main window that is connected to the view in initView(). * @see initView(); */ void initView(); /** Save the state of the window to the right settings value. Needs to be reimplemented in the special window implementation, * thus virtual here. */ virtual void saveWindowState( const QByteArray& arr ) = 0; virtual QByteArray windowState() = 0; /** Save the geomentry of the window to the right settings value. Needs to be reimplemented in the special window implementation, * thus virtual here. */ virtual void saveWindowGeo( const QByteArray& arr ) = 0; virtual QByteArray windowGeo() = 0; }; #endif // KATALOGVIEW_H kraft-0.97/src/katalogview.rc000066400000000000000000000013341410616450300162130ustar00rootroot00000000000000 &File &Catalog kraft-0.97/src/kraftcat_export.h000066400000000000000000000017251410616450300167250ustar00rootroot00000000000000/* This file is part of the kraft catalog library. Copyright (C) 2009 Klaas Freitag This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KRAFTCAT_EXPORT_H #define KRAFTCAT_EXPORT_H #define KRAFTCAT_EXPORT #endif kraft-0.97/src/kraftdb.cpp000066400000000000000000000512241410616450300154740ustar00rootroot00000000000000/*************************************************************************** KraftDB.cpp - ------------------- begin : Die Feb 3 2004 copyright : (C) 2004 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include "version.h" #include "kraftdb.h" #include "doctype.h" #include "dbids.h" #include "defaultprovider.h" #include "archiveman.h" #include "documentsaverdb.h" Q_GLOBAL_STATIC(KraftDB, mSelf) SqlCommand::SqlCommand() { } SqlCommand::SqlCommand(const QString& cmd, const QString& msg, bool mayfail) : mSql(cmd), mMessage(msg), mMayFail(mayfail) { if( !mMessage.isEmpty() && !mMessage.endsWith(';') ) { mMessage.append(';'); } if( !mSql.isEmpty() && !mSql.endsWith(';') ) { mSql.append(';'); } } QString SqlCommand::message() { return mMessage; } QString SqlCommand::command() { return mSql; } bool SqlCommand::mayfail() { return mMayFail; } // ============================ SqlCommandList::SqlCommandList() :QList(), _number(0) { } void SqlCommandList::setNumber(int no) { _number = no; } int SqlCommandList::number() { return _number; } void SqlCommandList::setMetaAddDocTypeList( QList list ) { _docTypeMetaList = list; } QList SqlCommandList::metaAddDocTypeList() const { return _docTypeMetaList; } // ========================================================================== KraftDB::KraftDB() :QObject (), mParent(nullptr), mSuccess( true ), EuroTag( QString::fromLatin1( "%EURO" ) ), mInitDialog(nullptr), _amountOfDocs(-1), _amountOfArchs(-1), _emitDBChangeSignal(true) { // Attention: Before setup assistant rewrite, dbConnect() was called here. // Keep that in mind, maybe the auto connect to the DB now misses somewhere. // dbConnect(); } bool KraftDB::dbConnect( const QString& driver, const QString& dbName, const QString& dbUser, const QString& dbHost, const QString& dbPasswd ) { mSuccess = true; mDatabaseDriver = driver; mDatabaseName = dbName; if( mDatabaseDriver.isEmpty() ) { // qDebug () << "Database Driver is not specified, check katalog settings"; mSuccess = false; return false; } else { // qDebug () << "Using database Driver " << mDatabaseDriver; } QStringList list = QSqlDatabase::drivers(); if( list.size() == 0 ) { // qDebug () << "Database Drivers could not be loaded." << endl; mSuccess = false ; } else { if( list.indexOf( mDatabaseDriver ) == -1 ) { // qDebug () << "Database Driver " << mDatabaseDriver << " could not be loaded!" << endl; mSuccess = false; } } if( mSuccess && m_db.isValid() ) { m_db.close(); } if( mSuccess ) { m_db = QSqlDatabase::addDatabase( mDatabaseDriver ); if ( ! m_db.isValid() || m_db.isOpenError() ) { qDebug() << "Failed to connect to the database driver: " << m_db.lastError().text() << endl; mSuccess = false; } } if ( mSuccess ) { int re = 0; if(mDatabaseDriver == "QMYSQL") { int port = -1; // use the default port so far // FIXME: get port from user interface // qDebug () << "Try to open MySQL database " << name << endl; re = checkConnect( dbHost, dbName , dbUser, dbPasswd, port); } else if(mDatabaseDriver == "QSQLITE") { // SqlLite only requires a valid file name which comes in as Database Name // qDebug () << "Try to open SqLite database " << name << endl; re = checkConnect( QString(), dbName, QString(), QString(), -1); } if ( re == 0 ) { // Database successfully opened; we can now issue SQL commands. // qDebug () << "** Database opened successfully" << endl; } else { // qDebug () << "## Could not open database" << endl; mSuccess = false; } } bool detectChanges {true}; connect( &_timer, &QTimer::timeout, this, &KraftDB::slotCheckDocDatabaseChanged); if (mSuccess && detectChanges) { _timer.start(10*1000); } return mSuccess; } KraftDB *KraftDB::self() { return mSelf; } void KraftDB::close() { QString name = m_db.connectionName(); // qDebug () << "Database connection name to close: " << name; m_db.close(); } bool KraftDB::isSqlite() { const QString dbDriver = qtDriver().toUpper(); return (dbDriver.startsWith("QSQLITE")); } int KraftDB::checkConnect( const QString& host, const QString& dbName, const QString& user, const QString& pwd, int port ) { mDatabaseName = dbName; // works for both mysql and sqlite if the filename for sqlite comes in // as parameter two if ( dbName.isEmpty() || !(m_db.isValid()) ) return false; m_db.setHostName( host ); m_db.setDatabaseName( dbName ); m_db.setUserName( user ); m_db.setPassword( pwd ); if( port > -1 ) { m_db.setPort(port); } int re = 0; m_db.open(); if ( m_db.isOpenError() ) { qDebug () << "ERR opening the db: " << m_db.lastError().text() << ", type is " << m_db.lastError().type() << endl; re = m_db.lastError().type(); } return re; } QSqlError KraftDB::lastError() { return m_db.lastError(); } dbID KraftDB::getLastInsertID() { if(! ( m_db.isValid()) ) return 0; QSqlQuery query; if( mDatabaseDriver.toLower() == "qmysql" ) { query.prepare("SELECT LAST_INSERT_ID()"); query.exec(); } else if( mDatabaseDriver.toLower() == "qsqlite") { query.prepare( "SELECT last_insert_rowid()"); query.exec(); } else { // qDebug () << "############# FATAL ERROR: Unknown database driver " << mDatabaseDriver; } int id = -1; if( query.next() ) { id = query.value(0).toInt(); } else { // qDebug () << "############# FATAL ERROR: Query for last insert id is invalid!"; } // qDebug () << "Last Insert ID: " << id; return dbID(id); } QString KraftDB::databaseName() const { return mDatabaseName; } bool KraftDB::databaseExists() { bool re = false; if(!m_db.isOpen()) { m_db.open(); } if(m_db.isOpen()) { const QStringList t = m_db.tables(); re = t.contains( "kraftsystem"); } return re; } void KraftDB::setSchemaVersion( const QString& versionStr ) { QSqlQuery q; q.prepare( "UPDATE kraftsystem SET dbSchemaVersion=:id" ); q.bindValue(":id", versionStr ); q.exec(); } SqlCommandList KraftDB::parseCommandFile( int currentVersion ) { SqlCommandList list; const QString& file = QString("%1_dbmigrate.sql").arg(currentVersion); list = parseCommandFile(file); list.setMetaAddDocTypeList( parseMetaFile(currentVersion) ); list.setNumber(currentVersion); return list; } SqlCommandList KraftDB::parseCommandFile( const QString& file ) { QString sqlFile; QString env = QString::fromUtf8( qgetenv( "KRAFT_HOME" ) ); if( !env.isEmpty() && env.right(1) != QDir::separator () ) { env += QDir::separator (); } QString driverPrefix = "mysql"; // Default on mysql if( mDatabaseDriver.toLower() == "qsqlite") { driverPrefix = "sqlite3"; } // qDebug() << "XXXXXXXXXX: " << stdDirs.resourceDirs("data"); if( env.isEmpty() ) { // Environment-Variable is empty, search in KDE paths QString fragment = QString("kraft/dbmigrate/%1/%2").arg(driverPrefix).arg(file ); sqlFile = QStandardPaths::locate(QStandardPaths::GenericDataLocation, fragment ); // qDebug () << "Searching for this fragment: " << fragment; // search in dbcreate as well. if ( sqlFile.isEmpty() ) { fragment = QString("kraft/dbinit/%1/%2").arg(driverPrefix).arg(file ); // qDebug () << "Also searching in " << fragment; sqlFile = QStandardPaths::locate(QStandardPaths::GenericDataLocation, fragment ); } } else { // read from environment variable path QString envPath = QString( "%1/database/%2/%3").arg(env).arg(driverPrefix).arg(file); // qDebug () << "Environment variable KRAFT_HOME set, searching for DB setup files in " << envPath; if( QFile::exists( envPath ) ) { sqlFile = envPath; } else if( QFile::exists( QString( "%1/database/%2/migration/%3").arg(env).arg(driverPrefix).arg(file ) ) ){ sqlFile = QString( "%1/database/%2/migration/%3").arg(env).arg(driverPrefix).arg(file ); } } SqlCommandList retList; if ( ! sqlFile.isEmpty() ) { // qDebug () << "Opening migration file " << sqlFile << endl; QFile f( sqlFile ); if ( !f.exists() ) { qDebug() << "FATAL: File" << sqlFile << "does not exist!"; } if ( !f.open( QIODevice::ReadOnly ) ) { qDebug () << "FATAL: Could not open " << sqlFile << endl; } else { QTextStream ts( &f ); ts.setCodec("UTF-8"); QString allSql = ts.readAll(); //Not sure of this one! QStringList sqlList = allSql.split(";"); QRegExp reg( "\\s*(#|--)\\s*message:? ?(.*)\\s*\\n" ); QRegExp failreg( "\\s*(#|--)\\s*mayfail\\s*\\n" ); reg.setMinimal( true ); QListIterator it(sqlList); while( it.hasNext() ) { QString msg, command; QString sqlFragment = it.next().trimmed(); int pos = reg.indexIn( sqlFragment.toLower(), 0 ); if ( pos > -1 ) { msg = reg.cap( 2 ); // qDebug() << "SQL-Commands-Parser: Msg: >" << msg << "<" << endl; } bool mayfail = false; pos = failreg.indexIn( sqlFragment.toLower(), 0 ); if( pos > -1 ) { mayfail = true; } bool clean = false; while( ! clean ) { if( sqlFragment.startsWith("#") || sqlFragment.startsWith("--") ) { // remove the comment line. int newLinePos = sqlFragment.indexOf('\n'); // qDebug() << "Found newline in <" << sqlFragment << ">:" << newLinePos; if(newLinePos > 0) { sqlFragment = sqlFragment.remove( 0, 1+sqlFragment.indexOf('\n') ); } else { sqlFragment = QString(); } // qDebug() << "Left over SQL Fragment:" << sqlFragment; } else { clean = true; } } if( !sqlFragment.isEmpty() ) { if( sqlFragment.startsWith( "CREATE TRIGGER", Qt::CaseInsensitive )) { // Triggers contain a ; which scares the parser. In case of triggers we pull // the next item in the list which should be the END; keyword. command = sqlFragment + ";"; if( it.hasNext()) command += it.next(); } else { // ordinary command, we take it as it is. command = sqlFragment; } if( !command.isEmpty() ) { retList.append( SqlCommand( command, msg, mayfail ) ); } } } } } else { qDebug () << "ERR: Can not find sql file " << file; } return retList; } QList KraftDB::parseMetaFile( int currentVersion ) { const QString fileName = QString("%1_meta.xml").arg(currentVersion); QString env = QString::fromUtf8( qgetenv( "KRAFT_HOME" ) ); if( !env.isEmpty() && env.right(1) != QDir::separator () ) { env += QDir::separator (); } QString xmlFile; if( !env.isEmpty() ) { xmlFile = env + QLatin1String("database/meta/") + fileName; } else { const QString fragment = QString("kraft/meta/") + fileName; xmlFile = QStandardPaths::locate(QStandardPaths::GenericDataLocation, fragment ); } QFile f( xmlFile ); MetaXMLParser parser; if( f.exists() ) { if ( !f.open( QIODevice::ReadOnly ) ) { qDebug () << "FATAL: Could not open " << xmlFile << endl; } else { QTextStream ts( &f ); ts.setCodec("UTF-8"); parser.parse( &f ); } } else { qDebug() << "XML Metafile" << xmlFile << "does not exist!"; } return parser.metaDocTypeAddList(); } int KraftDB::processSqlCommands( const SqlCommandList& commands ) { int cnt = 0; // first do the doctype definitions QList newDocTypes = commands.metaAddDocTypeList(); // loop over all doctypes first, later loop again to create the followers. // The followers might reference each other and thus must exist. for( auto newDocType : newDocTypes ) { const QString name = newDocType.name(); DocType type(name, true); for( QString attr : newDocType._attribs.keys() ) { type.setAttribute(attr, newDocType._attribs[attr]); } type.save(); } // now loop again to process the followers for( auto newDocType : newDocTypes ) { const QString name = newDocType.name(); if( newDocType._follower.count() > 0 ) { DocType type(name, true); type.setAllFollowers(newDocType._follower); type.save(); } } foreach( SqlCommand cmd, commands ) { if( !cmd.message().isEmpty() ) { emit statusMessage( cmd.message() ); } if( !cmd.command().isEmpty() ) { bool res = true; QSqlQuery q; q.clear(); res = q.exec(cmd.command()) || cmd.mayfail(); if ( res ) { // qDebug () << "Successful SQL Command: " << cmd.command() << endl; cnt ++; } else { QSqlError err = q.lastError(); res = false; qDebug () << "###### Failed SQL Command " << cmd.command() << ": " << err.text() << endl; } q.clear(); emit processedSqlCommand( res ); } } return cnt; } int KraftDB::requiredSchemaVersion() { return KRAFT_REQUIRED_SCHEMA_VERSION; } int KraftDB::currentSchemaVersion() { QSqlQuery query; query.exec("SELECT dbschemaversion FROM kraftsystem"); //We'll retrieve every record int re = -1; if ( query.next() ) { re = query.value(0).toInt(); } return re; } QString KraftDB::qtDriver() { return mDatabaseDriver; } QString KraftDB::currentTimeStamp( const QDateTime& dt ) { QString dateStr; if( dt.isValid() ) { dateStr = dt.toString(Qt::ISODate); } else { dateStr = QDateTime::currentDateTime().toString(Qt::ISODate); } return dateStr; } QString KraftDB::mysqlEuroEncode( const QString& str ) const { QChar euro( 0x20ac ); QString restr( str ); return restr.replace( euro, EuroTag ); } QString KraftDB::mysqlEuroDecode( const QString& str ) const { QChar euro( 0x20ac ); QString restr( str ); return restr.replace( EuroTag, euro ); } QStringList KraftDB::wordList( const QString& selector, StringMap replaceMap ) { QStringList re; QSqlQuery query; query.prepare("SELECT category, word FROM wordLists WHERE category=:cat"); query.bindValue(":cat", selector); query.exec(); while ( query.next() ) { re << replaceTagsInWord( query.value(1).toString(), replaceMap ); } re.sort(); return re; } QString KraftDB::replaceTagsInWord( const QString& w, StringMap replaceMap ) const { QString re( w ); QMap reMap; StringMap::Iterator it; for ( it = replaceMap.begin(); it != replaceMap.end(); ++it ) { reMap[it.key().length()] << it.key(); } QMap::Iterator reIt; for ( reIt = reMap.end(); reIt != reMap.begin(); ) { --reIt; QStringList keys = reIt.value(); // qDebug () << "PP: " << keys; for ( QStringList::Iterator dtIt = keys.begin(); dtIt != keys.end(); ++dtIt ) { QString repKey = *dtIt; re.replace( repKey, replaceMap[repKey] ); } } // qDebug () << "Adding to wordlist <" << re << ">"; return re; } void KraftDB::writeWordList( const QString& listName, const QStringList& list ) { // qDebug () << "Saving " << list[0] << " into list " << listName << endl; QSqlQuery qd; qd.prepare( "DELETE FROM wordLists WHERE category=:catName" ); qd.bindValue( ":catName", listName ); qd.exec(); QSqlQuery qi; qi.prepare( "INSERT INTO wordLists (category, word) VALUES( :category, :entry )" ); qi.bindValue( ":category", listName ); for ( QStringList::ConstIterator it = list.begin(); it != list.end(); ++it ) { qi.bindValue( ":entry", *it ); qi.exec(); } } bool KraftDB::checkTableExistsSqlite(const QString& name, const QStringList& lookupCols) { const QString query = QString("PRAGMA table_info(%1)").arg(name); QSqlQuery q(query); QStringList cols = lookupCols; q.exec(); QSqlError err = q.lastError(); if( err.isValid() ) { qDebug() << "Error: " << err.text(); } while( q.next() ) { const QString colName = q.value(1).toString(); qDebug() << "checking colum" << colName; cols.removeAll(colName); } return cols.isEmpty(); } KraftDB::~KraftDB() { } void KraftDB::slotCheckDocDatabaseChanged() { bool changed{false}; { QSqlQuery q("SELECT count(*) FROM document"); q.exec(); QSqlError err = q.lastError(); if( err.isValid() ) { qDebug() << "Error: " << err.text(); return; } if ( q.next() ) { bool ok; int cnt = q.value(0).toInt(&ok); if (_amountOfDocs != -1 && cnt != _amountOfDocs ) { qDebug() << "Docs from" << _amountOfDocs << "to" << cnt; changed = true; } _amountOfDocs = cnt; } } { QSqlQuery qArch("SELECT count(*) FROM archdoc"); qArch.exec(); QSqlError err = qArch.lastError(); if( err.isValid() ) { qDebug() << "Error: " << err.text(); return; } if ( qArch.next() ) { bool ok; int cnt = qArch.value(0).toInt(&ok); if (_amountOfArchs != -1 && cnt != _amountOfArchs) { qDebug() << "Arched docs from" << _amountOfArchs << "to" << cnt; changed = true; } _amountOfArchs = cnt; } } if (changed && _emitDBChangeSignal) emit docDatabaseChanged(); } dbID KraftDB::archiveDocument( KraftDoc *docPtr ) { dbID archID = ArchiveMan::self()->archiveDocument( docPtr ); if (archID.isOk()) { _emitDBChangeSignal = false; // block sending of the signal slotCheckDocDatabaseChanged(); _emitDBChangeSignal = true; } return archID; } void KraftDB::loadDocument(const QString& id, KraftDoc *docPtr) { DocumentSaverDB loader; loader.load(id, docPtr); } bool KraftDB::saveDocument(KraftDoc *docPtr) { bool res {false}; DocumentSaverDB saver; if (docPtr) { res = saver.saveDocument(docPtr); if (res) { _emitDBChangeSignal = false; // block sending of the signal slotCheckDocDatabaseChanged(); _emitDBChangeSignal = true; } } return res; } kraft-0.97/src/kraftdb.h000066400000000000000000000110661410616450300151410ustar00rootroot00000000000000/*************************************************************************** kraftdb.h - ------------------- begin : Die Feb 3 2004 copyright : (C) 2004 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef KRAFTDB_H #define KRAFTDB_H #include #include #include #include #include #include "metaxmlparser.h" class dbID; class DbInitDialog; class SetupAssistant; class KraftDoc; /** *@author Klaas Freitag */ class SqlCommand { public: SqlCommand(); SqlCommand( const QString&, const QString&, bool ); QString message(); QString command(); bool mayfail(); private: QString mSql; QString mMessage; bool mMayFail; }; class SqlCommandList: public QList { public: SqlCommandList(); QList metaAddDocTypeList() const; void setMetaAddDocTypeList( QList list ); void setNumber(int no); int number(); private: QList _docTypeMetaList; int _number; }; class KraftDB : public QObject { Q_OBJECT public: ~KraftDB(); static KraftDB *self(); dbID getLastInsertID(); QSqlDatabase *getDB(){ return &m_db; } QString qtDriver(); typedef QMap StringMap; QStringList wordList( const QString&, StringMap replaceMap = StringMap() ); void writeWordList( const QString&, const QStringList& ); QString databaseName() const; QSqlError lastError(); bool isSqlite(); bool isOk() { return mSuccess; } bool dbConnect( const QString& driver, const QString& dbName, const QString& dbUser, const QString& dbHost, const QString& dbPasswd ); /** * check if the database is open and contains the table kraftsystem. Still * the Schema version can be invalid, check currentSchemaVersion(). */ bool databaseExists(); /* * required and current schema versions. Must be equal for a healty * Kraft database. If currentSchemaVersion is smaller than requiredSchemaVersion, * the db needs an update. */ int currentSchemaVersion(); int requiredSchemaVersion(); void setSchemaVersion( const QString& ); // database aware current time stamp QString currentTimeStamp( const QDateTime& dt = QDateTime() ); /** * Euro sign encoding to work around a problem with mysql */ QString mysqlEuroEncode( const QString& ) const; QString mysqlEuroDecode( const QString& ) const; QString replaceTagsInWord( const QString& w, StringMap replaceMap ) const; // void checkDatabaseSetup( QWidget* ); SqlCommandList parseCommandFile( int currentVersion ); SqlCommandList parseCommandFile( const QString& file ); QList parseMetaFile( int currentVersion ); int processSqlCommands( const SqlCommandList& ); bool checkTableExistsSqlite(const QString& name, const QStringList& lookupCols); KraftDB(); dbID archiveDocument( KraftDoc *docPtr ); void loadDocument(const QString& id, KraftDoc *docPtr); bool saveDocument(KraftDoc *docPtr); private slots: void slotCheckDocDatabaseChanged(); signals: void statusMessage( const QString& ); void processedSqlCommand( bool ); void docDatabaseChanged(); private: // Private attributes void close(); int checkConnect(const QString&, const QString&, const QString&, const QString& , int port); /** The default database */ QSqlDatabase m_db; QWidget *mParent; bool mSuccess; const QString EuroTag; QString mDatabaseDriver; QString mDatabaseName; DbInitDialog *mInitDialog; SetupAssistant *mSetupAssistant; QTimer _timer; int _amountOfDocs, _amountOfArchs; // if this is set to false, the slotCheckDatabaseChanged() can be called // to update the members that hold the amount of docs, but the update signal // is not sent out. bool _emitDBChangeSignal; }; #endif kraft-0.97/src/kraftdoc.cpp000066400000000000000000000177621410616450300156650ustar00rootroot00000000000000/*************************************************************************** KraftDoc.cpp - Kraft document class ------------------- begin : Mit Dez 31 19:24:05 CET 2003 copyright : (C) 2003 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ // include files for Qt #include #include #include // application specific includes #include "kraftsettings.h" #include "kraftdoc.h" #include "portal.h" #include "kraftview.h" #include "docposition.h" #include "documentsaverdb.h" #include "defaultprovider.h" #include "documentman.h" #include "doctype.h" #include "documentman.h" #include "kraftdb.h" // FIXME: Make KraftDoc inheriting DocDigest! KraftDoc::KraftDoc(QWidget *parent) : QObject(parent), _modified(false), mIsNew(true), mDocTypeChanged(false) { } KraftDoc::~KraftDoc() { } KraftDoc& KraftDoc::operator=( KraftDoc& origDoc ) { if ( this == &origDoc ) return *this; DocPositionListIterator it( origDoc.mPositions ); while ( it.hasNext() ) { DocPosition *dp = static_cast( it.next() ); DocPosition *newPos = new DocPosition(); *newPos = *dp; newPos->setDbId( -1 ); mPositions.append( newPos ); // qDebug () << "Appending position " << dp->dbId().toString() << endl; } _modified = origDoc._modified; mIsNew = true; mAddressUid = origDoc.mAddressUid; mProjectLabel = origDoc.mProjectLabel; mPredecessor = origDoc.mPredecessor; mPredecessorDbId = origDoc.mPredecessorDbId; mAddress = origDoc.mAddress; mPreText = origDoc.mPreText; mPostText = origDoc.mPostText; mDocType = origDoc.mDocType; mDocTypeChanged = false; mSalut = origDoc.mSalut; mGoodbye = origDoc.mGoodbye; mIdent = origDoc.mIdent; mWhiteboard = origDoc.mWhiteboard; // Two qualifiers for the locale settings. mCountry = origDoc.mCountry; mLanguage = origDoc.mLanguage; mDate = origDoc.mDate; mLastModified = origDoc.mLastModified; // setPositionList( origDoc.mPositions ); mRemovePositions = origDoc.mRemovePositions; // mDocID = origDoc.mDocID; return *this; } void KraftDoc::closeDocument() { deleteItems(); } void KraftDoc::setPredecessor( const QString& w ) { mPredecessor = w; } bool KraftDoc::openDocument(const QString& id ) { KraftDB::self()->loadDocument(id, this); mDocTypeChanged = false; _modified=false; mIsNew = false; return true; } bool KraftDoc::reloadDocument() { mPositions.clear(); mRemovePositions.clear(); return openDocument( mDocID.toString() ); } bool KraftDoc::saveDocument( ) { bool result = false; result = KraftDB::self()->saveDocument(this); if(result) { if ( isNew() ) { setLastModified( QDateTime::currentDateTime() ); } // Go through the whole document and remove the positions // that are to delete because they now were deleted in the // database. DocPositionListIterator it( mPositions ); while( it.hasNext() ) { DocPositionBase *dp = it.next(); if( dp->toDelete() ) { // qDebug () << "Removing pos " << dp->dbId().toString() << " from document object" << endl; mPositions.removeAll( dp ); } } _modified = false; } return result; } QString KraftDoc::docIdentifier() const { const QString id = ident(); if( id.isEmpty() ) { return docType(); } return i18nc("First argument is the doctype, like Invoice, followed by the ID", "%1 (Id %2)", docType(), id ); } void KraftDoc::deleteItems() { qDeleteAll(mPositions); mPositions.clear(); } void KraftDoc::setDocType( const QString& s ) { if( s != mDocType ) { mDocType = s; mDocTypeChanged = true; } } void KraftDoc::setPositionList( DocPositionList newList, bool isNew) { mPositions.clear(); DocPositionListIterator it( newList ); while ( it.hasNext() ) { DocPositionBase *dpb = it.next(); DocPosition *dp = static_cast( dpb ); DocPosition *newDp = createPosition( dp->type() ); *newDp = *dp; if(isNew) { newDp->setDbId(-1); } } } DocPosition* KraftDoc::createPosition( DocPositionBase::PositionType t ) { DocPosition *dp = new DocPosition( t ); mPositions.append( dp ); return dp; } void KraftDoc::slotRemovePosition( int pos ) { // qDebug () << "Removing position " << pos << endl; foreach( DocPositionBase *dp, mPositions ) { // qDebug () << "Comparing " << pos << " with " << dp->dbId().toString() << endl; if( dp->dbId() == pos ) { if( ! mPositions.removeAll( dp ) ) { // qDebug () << "Could not remove!" << endl; } else { // qDebug () << "Successfully removed the position " << dp << endl; mRemovePositions.append( dp->dbId() ); // remember to delete } } } } void KraftDoc::slotMoveUpPosition( int dbid ) { // qDebug () << "Moving position " << dbid << " up" << endl; if( mPositions.count() < 1 ) return; int curPos = -1; // Search the one to move up for( int i = 0; curPos == -1 && i < mPositions.size(); i++ ) { if( (mPositions.at(i))->dbId() == dbid ) { curPos = i; // get out of the loop } } // qDebug () << "Found: "<< curPos << ", count: " << mPositions.count() << endl; if( curPos < mPositions.size()-1 ) { mPositions.swap( curPos, curPos+1 ); } } void KraftDoc::slotMoveDownPosition( int dbid ) { // qDebug () << "Moving position " << dbid << " down" << endl; if( mPositions.count() < 1 ) return; int curPos = -1; // Search the one to move up for( int i = 0; curPos == -1 && i < mPositions.size(); i++ ) { if( (mPositions.at(i))->dbId() == dbid ) { curPos = i; // get out of the loop } } // qDebug () << "Found: "<< curPos << ", count: " << mPositions.count(); if( curPos > 0 ) { mPositions.swap( curPos, curPos-1 ); } } int KraftDoc::slotAppendPosition( const DocPosition& pos ) { DocPosition *dp = createPosition(); *dp = pos; // FIXME: Proper assignment operator return mPositions.count(); } Geld KraftDoc::nettoSum() const { return positions().nettoPrice(); } Geld KraftDoc::bruttoSum() const { Geld g = nettoSum(); g += vatSum(); return g; } Geld KraftDoc::fullTaxSum() const { return positions().fullTaxSum(DocumentMan::self()->tax(date())); } Geld KraftDoc::reducedTaxSum() const { return positions().reducedTaxSum(DocumentMan::self()->reducedTax(date())); } Geld KraftDoc::vatSum() const { return positions().taxSum( DocumentMan::self()->tax( date() ), DocumentMan::self()->reducedTax( date() ) ); // return Geld( nettoSum() * DocumentMan::self()->vat()/100.0 ); } QString KraftDoc::country() const { QLocale *loc = DefaultProvider::self()->locale(); return loc->countryToString(loc->country()); } QString KraftDoc::language() const { QLocale *loc = DefaultProvider::self()->locale(); return loc->languageToString(loc->language()); } QString KraftDoc::partToString( Part p ) { if ( p == Header ) return i18nc( "Document part header", "Header" ); else if ( p == Footer ) return i18nc( "Document part footer", "Footer" ); else if ( p == Positions ) return i18nc( "Document part containing the items", "Items" ); return i18n( "Unknown document part" ); } kraft-0.97/src/kraftdoc.h000066400000000000000000000160161410616450300153210ustar00rootroot00000000000000/*************************************************************************** kraftdoc.h - Kraft document class ------------------- begin : Mit Dez 31 19:24:05 CET 2003 copyright : (C) 2003 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef KRAFTDOC_H #define KRAFTDOC_H // include files for QT #include #include #include #include "docposition.h" #include "dbids.h" #include "docguardedptr.h" // forward declaration of the Kraft classes class DocumentSaverBase; class Geld; class KraftView; class KraftDoc : public QObject { Q_OBJECT Q_PROPERTY(QString docType READ docType) Q_PROPERTY(QString address READ address) Q_PROPERTY(QString clientUid READ addressUid) Q_PROPERTY(QString ident READ ident) Q_PROPERTY(QString salut READ salut) Q_PROPERTY(QString goodbye READ goodbye) Q_PROPERTY(QString preText READ preText) Q_PROPERTY(QString postText READ postText) Q_PROPERTY(QString projectLabel READ projectLabel) Q_PROPERTY(QString docIDStr READ docIdStr) Q_PROPERTY(QString docIdentifier READ docIdentifier) Q_PROPERTY(QString postText READ postText) Q_PROPERTY(QString nettoSumStr READ nettoSumStr) Q_PROPERTY(QString bruttoSumStr READ bruttoSumStr) Q_PROPERTY(QString taxSumStr READ vatSumStr) Q_PROPERTY(QString fullTaxSumStr READ fullTaxSumStr) Q_PROPERTY(QString reducedTaxSumStr READ reducedTaxSumStr) public: enum Part { Header, Positions, Footer, Unknown }; static QString partToString( Part ); /** Constructor for the fileclass of the application */ KraftDoc(QWidget *parent = nullptr); /** Destructor for the fileclass of the application */ ~KraftDoc(); KraftDoc& operator=( KraftDoc& ); /** sets the modified flag for the document after a modifying action * on the view connected to the document.*/ void setModified(bool _m=true){ _modified=_m; } /** returns if the document is modified or not. Use this to determine * if your document needs saving by the user on closing.*/ bool isModified(){ return _modified; } /** deletes the document's contents */ void deleteItems(); /** closes the current document */ void closeDocument(); /** loads the document by filename and format and emits the updateViews() signal */ bool openDocument(const QString& ); /** fetch the document from database back */ bool reloadDocument(); /** saves the document under filename and format.*/ bool saveDocument( ); QLocale* locale(); DocPosition* createPosition( DocPositionBase::PositionType t = DocPositionBase::Position ); DocPositionList positions() const { return mPositions; } void setPositionList(DocPositionList , bool isNew = false); QDate date() const { return mDate; } void setDate( QDate d ) { mDate = d; } QDateTime lastModified() const { return mLastModified; } void setLastModified( QDateTime d ) { mLastModified = d; } QString docType() const { return mDocType; } void setDocType( const QString& s ); bool docTypeChanged() { return mDocTypeChanged; } QString addressUid() const { return mAddressUid; } void setAddressUid( const QString& id ) { mAddressUid = id; } QString address() const { return mAddress; } void setAddress( const QString& adr ) { mAddress = adr; } bool isNew() const { return mIsNew; } QString ident() const { return mIdent; } void setIdent( const QString& str ) { mIdent = str; } QString salut() const { return mSalut; } void setSalut( const QString& str ) { mSalut = str; } QString goodbye() const { return mGoodbye; } void setGoodbye( const QString& str ) { mGoodbye = str; } QString preText() const { return mPreText; } void setPreText( const QString& str ) { mPreText = str; } QString postText() const { return mPostText; } void setPostText( const QString& str ) { mPostText = str; } QString whiteboard() const { return mWhiteboard; } void setWhiteboard( const QString& w ) { mWhiteboard = w; } QString projectLabel() const { return mProjectLabel; } void setProjectLabel( const QString& w ) { mProjectLabel = w; } QString predecessor() const { return mPredecessor; } void setPredecessor( const QString& w ); QString predecessorDbId() const { return mPredecessorDbId; } void setPredecessorDbId( const QString& pId ) { mPredecessorDbId = pId; } void setDocID( dbID id ) { mDocID = id; } dbID docID() const { return mDocID; } QString docIdStr() const { return docID().toString(); } QString docIdentifier() const; DBIdList removePositionList() { return mRemovePositions; } Geld nettoSum() const; QString nettoSumStr() const { return nettoSum().toString(); } Geld bruttoSum() const; QString bruttoSumStr() const { return bruttoSum().toString(); } Geld fullTaxSum() const; QString fullTaxSumStr() const { return fullTaxSum().toString(); } Geld reducedTaxSum() const; QString reducedTaxSumStr() const { return reducedTaxSum().toString(); } Geld vatSum() const; QString vatSumStr() const { return vatSum().toString(); } QString country() const; QString language() const; public slots: /** calls redrawDocument() on all views connected to the document object and is * called by the view by which the document has been changed. * As this view normally repaints itself, it is excluded from the paintEvent. */ int slotAppendPosition( const DocPosition& ); // The following slots take get the db id as argument void slotRemovePosition( int ); void slotMoveUpPosition( int ); void slotMoveDownPosition( int ); private: /** the modified flag of the current document */ bool _modified; bool mIsNew; QString mAddressUid; QString mProjectLabel; QString mAddress; QString mPreText; QString mPostText; QString mDocType; bool mDocTypeChanged; QString mSalut; QString mGoodbye; QString mIdent; QString mWhiteboard; QString mPredecessor; QString mPredecessorDbId; // Two qualifiers for the locale settings. QString mCountry; QString mLanguage; QDate mDate; QDateTime mLastModified; DocPositionList mPositions; DBIdList mRemovePositions; dbID mDocID; }; #endif // KraftDoc_H kraft-0.97/src/kraftdocedit.cpp000066400000000000000000000006711410616450300165220ustar00rootroot00000000000000#include "kraftdocedit.h" #include KraftDocEdit::KraftDocEdit( QWidget *parent ) : QWidget( parent ) { } void KraftDocEdit::setTitle( const QString &title ) { mTitle = title; } QString KraftDocEdit::title() const { return mTitle; } void KraftDocEdit::setColor( const QColor &color ) { mColor = color; } QColor KraftDocEdit::color() const { return mColor; } void KraftDocEdit::slotModified() { emit modified(); } kraft-0.97/src/kraftdocedit.h000066400000000000000000000006371410616450300161710ustar00rootroot00000000000000#ifndef KRAFTDOCEDIT_H #define KRAFTDOCEDIT_H #include "ui_docheader.h" class KraftDocEdit : public QWidget { Q_OBJECT public: KraftDocEdit( QWidget *parent ); void setTitle( const QString & ); QString title() const; void setColor( const QColor & ); QColor color() const; signals: void modified(); public slots: void slotModified(); private: QString mTitle; QColor mColor; }; #endif kraft-0.97/src/kraftdocfooteredit.cpp000066400000000000000000000063461410616450300177460ustar00rootroot00000000000000/*************************************************************************** kraftdocfooteredit.cpp - inherited class from designer generated class ------------------- begin : Sept. 2006 copyright : (C) 2006 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include "kraftdocfooteredit.h" #include "kraftdb.h" #include #include #include #include #include KraftDocFooterEdit::KraftDocFooterEdit( QWidget *parent ) : KraftDocEdit( parent ), mDocFooterEdit( 0 ), mCustomGreetingIndex(-1) { QVBoxLayout *topLayout = new QVBoxLayout; Q_ASSERT( parent ); setLayout( topLayout ); mDocFooterEdit = new Ui::DocFooterEdit; QWidget *w = new QWidget; mDocFooterEdit->setupUi(w); topLayout->addWidget(w); mDocFooterEdit->m_cbGreeting->insertItems(-1, KraftDB::self()->wordList( "greeting" ) ); connect( mDocFooterEdit->m_cbGreeting, SIGNAL( activated( int ) ), SLOT( slotModified() ) ); connect( mDocFooterEdit->m_cbGreeting, SIGNAL(currentIndexChanged(int)), this, SLOT(slotGreeterIndexChanged(int))); connect( mDocFooterEdit->m_cbGreeting, SIGNAL(editTextChanged(QString)), this, SLOT(slotGreeterEditTextChanged(QString))); connect( mDocFooterEdit->m_teSummary, SIGNAL( textChanged() ), SLOT( slotModified() ) ); setTitle( i18n( "Document Footer" ) ); setColor( "#f0ff9a" ); } void KraftDocFooterEdit::slotSetGreeting( const QString& newText ) { slotGreeterEditTextChanged(newText); } QString KraftDocFooterEdit::greeting() { return mGreeting; } void KraftDocFooterEdit::slotGreeterIndexChanged(int newIndex) { mGreeting = mDocFooterEdit->m_cbGreeting->itemText(newIndex); slotModified(); } void KraftDocFooterEdit::slotGreeterEditTextChanged(const QString& newText) { QComboBox *greeterCombo = qobject_cast(mDocFooterEdit->m_cbGreeting); if( !greeterCombo ) return; // qDebug () << "II Combo box text changed to" << newText << "in" << greeterCombo; const QStringList texts = KraftDB::self()->wordList("greeting"); int indx = greeterCombo->currentIndex(); if( !texts.contains(newText)) { if( mCustomGreetingIndex == -1 ) { // no custom Entry yet greeterCombo->insertItem(0, newText); mCustomGreetingIndex = 0; } indx = mCustomGreetingIndex; } greeterCombo->setItemText(indx, newText); greeterCombo->setCurrentIndex(indx); mGreeting = newText; slotModified(); } kraft-0.97/src/kraftdocfooteredit.h000066400000000000000000000032201410616450300173770ustar00rootroot00000000000000/*************************************************************************** kraftdocfooteredit.h - inherited class from designer generated class ------------------- begin : Sept. 2006 copyright : (C) 2006 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef KRAFTDOCFOOTEREDIT_H #define KRAFTDOCFOOTEREDIT_H #include "ui_docfooter.h" #include "kraftdocedit.h" class KraftDocFooterEdit : public KraftDocEdit { Q_OBJECT public: KraftDocFooterEdit( QWidget *parent=0 ); // FIXME: Remove access to internal widgets Ui::DocFooterEdit *ui() { return mDocFooterEdit; } QString greeting(); public slots: void slotGreeterIndexChanged(int newIndex); void slotGreeterEditTextChanged(const QString& newText); void slotSetGreeting( const QString& newText ); private: Ui::DocFooterEdit *mDocFooterEdit; QString mGreeting; int mCustomGreetingIndex; }; #endif kraft-0.97/src/kraftdocheaderedit.cpp000066400000000000000000000054101410616450300176670ustar00rootroot00000000000000/*************************************************************************** kraftdocheaderview.cpp - inherited class from designer generated class ------------------- begin : Sept. 2006 copyright : (C) 2006 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include "kraftdocheaderedit.h" #include #include #include #include #include #include "addressprovider.h" KraftDocHeaderEdit::KraftDocHeaderEdit( QWidget *parent ) : KraftDocEdit( parent ) { QVBoxLayout *topLayout = new QVBoxLayout; setLayout( topLayout ); mDocHeaderEdit = new Ui::DocHeaderEdit; QWidget *w = new QWidget; mDocHeaderEdit->setupUi( w ); topLayout->addWidget( w ); mDocHeaderEdit->mButtLang->setIcon(QIcon::fromTheme("preferences-desktop-locale")); connect( mDocHeaderEdit->m_cbType, SIGNAL( currentIndexChanged(int)), SLOT( slotModified() ) ); connect( mDocHeaderEdit->m_dateEdit, SIGNAL( dateChanged( QDate ) ), SLOT( slotModified() ) ); connect( mDocHeaderEdit->m_postAddressEdit, SIGNAL( textChanged() ), SLOT( slotModified() ) ); connect( mDocHeaderEdit->m_letterHead, SIGNAL( currentTextChanged(QString)), SLOT(slotModified() ) ); connect( mDocHeaderEdit->m_teEntry, SIGNAL( textChanged() ), SLOT( slotModified() ) ); connect( mDocHeaderEdit->m_whiteboardEdit, SIGNAL( textChanged() ), SLOT( slotModified() ) ); connect( mDocHeaderEdit->mProjectLabelEdit, SIGNAL( textChanged(QString) ), SLOT(slotModified() ) ); connect( mDocHeaderEdit->pb_pickAddressee, SIGNAL(clicked()), SIGNAL(pickAddressee())); setTitle( i18n( "Document Header" ) ); setColor( "#9af0ff" ); // if the Akonadi-Backend is down, just show a text QScopedPointer addressProvider; addressProvider.reset(new AddressProvider); if( !addressProvider->backendUp() ) { mDocHeaderEdit->pb_pickAddressee->hide(); mDocHeaderEdit->m_labName->setText( i18n("Manually set in address field.")); } } kraft-0.97/src/kraftdocheaderedit.h000066400000000000000000000026501410616450300173370ustar00rootroot00000000000000/*************************************************************************** kraftdocheaderview.h - inherited class from designer generated class ------------------- begin : Sept. 2006 copyright : (C) 2006 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef KRAFTDOCHEADEREDIT_H #define KRAFTDOCHEADEREDIT_H #include "ui_docheader.h" #include "kraftdocedit.h" class KraftDocHeaderEdit : public KraftDocEdit { Q_OBJECT public: KraftDocHeaderEdit( QWidget* ); // FIXME: Remove access to internal widgets Ui::DocHeaderEdit *docHeaderEdit() { return mDocHeaderEdit; } signals: void pickAddressee(); private: Ui::DocHeaderEdit *mDocHeaderEdit; }; #endif kraft-0.97/src/kraftdocpositionsedit.cpp000066400000000000000000000105021410616450300204640ustar00rootroot00000000000000/*************************************************************************** kraftdocpositionsedit.cpp - Doc item editor widget ------------------- begin : copyright : (C) 2003 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include "kraftdocpositionsedit.h" #include #include #include #include #include #include #include #include #include #include "kraftview.h" KraftViewScroll::KraftViewScroll( QWidget *parent ): QScrollArea( parent ) { myWidget = new QWidget; myWidget->setAutoFillBackground(false); layout = new QVBoxLayout; layout->setAlignment(Qt::AlignTop); layout->setSizeConstraint( QLayout::SetMinAndMaxSize ); layout->setContentsMargins( 0,0,0,0 ); layout->setSpacing(0); myWidget->setLayout(layout); setWidget(myWidget); setWidgetResizable(true); myWidget->resize(0,0); myWidget->setMinimumHeight(0); myWidget->setMaximumHeight(0); myWidget->setContentsMargins(0, 0, 0, 0); } void KraftViewScroll::addChild( QWidget *child, int index ) { int y1 = myWidget->height(); layout->insertWidget(index, child); int y2 = y1+child->height(); myWidget->resize( child->width(), y2); myWidget->setMinimumHeight(y2); myWidget->setMaximumHeight(y2); } void KraftViewScroll::removeChild( PositionViewWidget *child ) { layout->removeWidget( child ); // from the scrollview } void KraftViewScroll::moveChild( PositionViewWidget *child, int index) { layout->removeWidget(child); layout->insertWidget(index, child); } int KraftViewScroll::indexOf(PositionViewWidget *child) { return layout->indexOf(child); } // ######################################################### KraftDocPositionsEdit::KraftDocPositionsEdit( QWidget *parent ) : KraftDocEdit( parent ) { QBoxLayout *topLayout = new QVBoxLayout(); topLayout->setMargin( 0 ); //TODO PORT QT5 topLayout->setSpacing( 0 ); // QDialog::spacingHint() ); QHBoxLayout *upperHBoxLayout = new QHBoxLayout; //upperHBoxLayout->setFrameStyle( QFrame::Box + QFrame::Sunken ); //TODO PORT QT5 upperHBoxLayout->setMargin( QDialog::marginHint()/2 ); topLayout->addLayout( upperHBoxLayout ); QPushButton *button = new QPushButton( i18n("Add Item...") ); connect( button, SIGNAL( clicked() ), SIGNAL( addPositionClicked() ) ); button->setToolTip( i18n( "Add a normal item to the document manually." ) ); upperHBoxLayout->addWidget(button); upperHBoxLayout->setSpacing( 3 ); m_discountBtn = new QPushButton( i18n("Add Discount Item") ); connect( m_discountBtn, SIGNAL( clicked() ), SIGNAL( addExtraClicked() ) ); upperHBoxLayout->addWidget(m_discountBtn); m_discountBtn->setToolTip( i18n( "Adds an item to the document that allows discounts on other items in the document" ) ); #if 0 // commented, rarely used feature. button = new QPushButton( i18n("Import Items...") ); connect( button, SIGNAL( clicked() ), SIGNAL( importItemsClicked() ) ); upperHBoxLayout->addWidget(button); button->setToolTip( i18n( "Opens a dialog where multiple items can be imported from a text file." ) ); #endif QWidget *spaceEater = new QWidget( ); spaceEater->setSizePolicy( QSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Minimum ) ); upperHBoxLayout->addWidget(spaceEater); m_positionScroll = new KraftViewScroll( this ); topLayout->addWidget( m_positionScroll ); setTitle( i18n( "Document Items" ) ); setColor( "#9affa9" ); setLayout(topLayout); } void KraftDocPositionsEdit::setDiscountButtonVisible( bool visible ) { m_discountBtn->setVisible( visible ); } kraft-0.97/src/kraftdocpositionsedit.h000066400000000000000000000040721410616450300201360ustar00rootroot00000000000000/*************************************************************************** kraftdocpositionsedit.h - Doc item editor widget ------------------- begin : copyright : (C) 2003 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef KRAFTDOCPOSITIONSEDIT_H #define KRAFTDOCPOSITIONSEDIT_H #include "ui_docheader.h" #include "kraftdocedit.h" #include class KraftViewScroll; class PositionViewWidget; class KraftViewScroll : public QScrollArea { Q_OBJECT public: KraftViewScroll( QWidget* ); ~KraftViewScroll() { } void addChild( QWidget *child, int index ); void removeChild( PositionViewWidget *child ); void moveChild( PositionViewWidget *child, int index); int indexOf( PositionViewWidget *child); private: QWidget *myWidget; QVBoxLayout *layout; }; // ########################################################################### class KraftDocPositionsEdit : public KraftDocEdit { Q_OBJECT public: KraftDocPositionsEdit( QWidget* ); // FIXME: Remove access to internal widgets KraftViewScroll *positionScroll() { return m_positionScroll; } void setDiscountButtonVisible( bool visible ); signals: void addPositionClicked(); void addExtraClicked(); void importItemsClicked(); private: KraftViewScroll *m_positionScroll; QPushButton *m_discountBtn; }; #endif kraft-0.97/src/kraftglobals.h000066400000000000000000000026121410616450300161740ustar00rootroot00000000000000/*************************************************************************** kraftglobals.h - some global defines ------------------- begin : Don Jan 1 2004 copyright : (C) 2004 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef KRAFT_GLOBALS #define KRAFT_GLOBALS #include #include "geld.h" // schluessel um alle kalk-typen kostenPerKalcPart zurueckzugeben. #define ALL_KALKPARTS QLatin1String("all_calculation_parts") #define KALKPART_TIME QLatin1String("Time") #define KALKPART_FIX QLatin1String("Fix") #define KALKPART_MATERIAL QLatin1String("Material") // typedef long Geld; #endif kraft-0.97/src/kraftsettings.kcfg000066400000000000000000000150101410616450300170700ustar00rootroot00000000000000 true true thunderbird 10.0 trml2pdf %y%w-%i false 3 kraft-0.97/src/kraftsettings.kcfgc000066400000000000000000000001161410616450300172340ustar00rootroot00000000000000File=kraftsettings.kcfg ClassName=KraftSettings Singleton=true Mutators=true kraft-0.97/src/krafttemplate.cpp000066400000000000000000000001001410616450300167050ustar00rootroot00000000000000#include "krafttemplate.h" KraftTemplate::KraftTemplate() { } kraft-0.97/src/krafttemplate.h000066400000000000000000000002031410616450300163560ustar00rootroot00000000000000#ifndef KRAFTTEMPLATE_H #define KRAFTTEMPLATE_H class KraftTemplate { public: KraftTemplate(); }; #endif // KRAFTTEMPLATE_H kraft-0.97/src/kraftui.rc000066400000000000000000000015441410616450300153460ustar00rootroot00000000000000 &Document Settings Document Actions kraft-0.97/src/kraftview.cpp000066400000000000000000001355551410616450300160730ustar00rootroot00000000000000/*************************************************************************** kraftview.cpp - Interactive document view ------------------- begin : Mit Dez 31 19:24:05 CET 2003 copyright : (C) 2003 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ // include files for Qt #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // application specific includes #include "kraftdb.h" #include "kraftsettings.h" #include "kraftview.h" #include "kraftdoc.h" #include "ui_docheader.h" #include "documentman.h" #include "docassistant.h" #include "positionviewwidget.h" #include "ui_docfooter.h" #include "docposition.h" #include "unitmanager.h" #include "docpostcard.h" #include "kataloglistview.h" #include "katalogman.h" #include "templkatalog.h" #include "templkataloglistview.h" #include "catalogselection.h" #include "kraftdocheaderedit.h" #include "kraftdocpositionsedit.h" #include "kraftdocfooteredit.h" #include "inserttempldialog.h" #include "defaultprovider.h" #include "stockmaterial.h" #include "templtopositiondialogbase.h" #include "doctype.h" #include "catalogtemplate.h" #include "importitemdialog.h" #include "addressprovider.h" #include "addressselectordialog.h" #include "format.h" #define NO_TAX 0 #define RED_TAX 1 #define FULL_TAX 2 #define INDI_TAX 3 KraftView::KraftView(QWidget *parent) : KraftViewBase( parent ), mHelpLabel(nullptr), mRememberAmount( -1 ), mModified( false ), mTaxBefore( -1 ), mDocPosEditorIndx( -1 ) { setWindowTitle( i18n("Document" ) ); setModal( false ); QVBoxLayout *mainLayout = new QVBoxLayout; setLayout(mainLayout); mDetailHeader = new QLabel; mDetailHeader->setSizePolicy( QSizePolicy( QSizePolicy::Minimum, QSizePolicy::Fixed ) ); mDetailHeader->setFrameStyle( QFrame::Box + QFrame::Plain ); mDetailHeader->setLineWidth( 1 ); mDetailHeader->setAutoFillBackground(true); mAddressProvider = new AddressProvider( this ); connect( mAddressProvider, SIGNAL(lookupResult(QString,KContacts::Addressee)), this, SLOT( slotAddresseeFound(QString,KContacts::Addressee))); QPalette palette; palette.setColor(mDetailHeader->backgroundRole(), QColor( "darkBlue" )); palette.setColor(mDetailHeader->foregroundRole(), QColor( "white ")); mDetailHeader->setPalette( palette ); mDetailHeader->setTextFormat( Qt::PlainText ); mDetailHeader->setFixedHeight( 40 ); // FIXME QFont f = mDetailHeader->font(); f.setPointSize( qRound( 1.4 * f.pointSize() ) ); f.setBold( true ); mDetailHeader->setFont( f ); mainLayout->addWidget( mDetailHeader ); mCSplit = new QSplitter(this); mainLayout->addWidget( mCSplit ); mViewStack = new QStackedWidget; mCSplit->addWidget( mViewStack ); // qDebug () << "mViewSTack height is " << mViewStack->height() << endl; mAssistant = new DocAssistant( mCSplit ); mCSplit->addWidget( mAssistant ); /* catalog template selection signal */ connect(mAssistant, &DocAssistant::templatesToDocument, this, &KraftView::slotAddItems); /* signal to toggle the visibility of the template section in the assistant */ connect( mAssistant, SIGNAL( toggleShowTemplates( bool ) ), this, SLOT( slotShowTemplates( bool ) ) ); /* signal that brings a new address to the document */ connect( mAssistant, SIGNAL( headerTextTemplate( const QString& ) ), this, SLOT( slotNewHeaderText( const QString& ) ) ); connect( mAssistant, SIGNAL( footerTextTemplate( const QString& ) ), this, SLOT( slotNewFooterText( const QString& ) ) ); connect( mAssistant, SIGNAL( selectPage( int ) ), this, SLOT( slotSwitchToPage( int ) ) ); mAssistant->slotSelectDocPart( KraftDoc::Header ); setupMappers(); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); mainLayout->addWidget(buttonBox); } KraftView::~KraftView() { // qDebug () << "KRAFTVIEW going away." << endl; } void KraftView::setupMappers() { mDeleteMapper = new QSignalMapper( this ); connect( mDeleteMapper, SIGNAL( mapped(int)), this, SLOT( slotDeletePosition( int ) ) ); mMoveUpMapper = new QSignalMapper( this ); connect( mMoveUpMapper, SIGNAL( mapped(int)), this, SLOT( slotMovePositionUp( int ) ) ); mMoveDownMapper = new QSignalMapper( this ); connect( mMoveDownMapper, SIGNAL( mapped(int)), this, SLOT( slotMovePositionDown( int ) ) ); mLockPositionMapper = new QSignalMapper( this ); connect( mLockPositionMapper, SIGNAL( mapped( int )), this, SLOT( slotLockPosition( int ) ) ); mUnlockPositionMapper = new QSignalMapper( this ); connect( mUnlockPositionMapper, SIGNAL( mapped( int )), this, SLOT( slotUnlockPosition( int ) ) ); mModifiedMapper = new QSignalMapper( this ); connect( mModifiedMapper, SIGNAL( mapped( int ) ), this, SLOT( slotPositionModified( int ) ) ); // block signals as long as the widget is built up. // unblocking happens in redrawDocument() mModifiedMapper->blockSignals(true); } void KraftView::setup( DocGuardedPtr doc ) { KraftViewBase::setup(doc); if ( KraftSettings::self()->docViewSplitter().count() == 2 ) { mCSplit->setSizes( KraftSettings::self()->docViewSplitter() ); } setupDocHeaderView(); setupItems(); setupFooter(); setWindowTitle( m_doc->docIdentifier() ); slotSwitchToPage( KraftDoc::Header ); if( doc->isNew() ) { mModified = true; } } void KraftView::slotSwitchToPage( int id ) { // check if the wanted part is already visible if ( mViewStack->currentWidget() == mViewStack->widget( id ) ) return; mViewStack->setCurrentIndex( id ); KraftDocEdit *edit = static_cast( mViewStack->currentWidget() ); mDetailHeader->setText( edit->title() ); QPalette palette; palette.setColor(mDetailHeader->backgroundRole(), edit->color()); // FIXME: color palette.setColor(mDetailHeader->foregroundRole(), QColor( "#00008b" )); mDetailHeader->setPalette( palette ); mAssistant->slotSelectDocPart( mViewStack->currentIndex() ); } void KraftView::slotShowTemplates( bool ) { } void KraftView::setupDocHeaderView() { KraftDocHeaderEdit *edit = new KraftDocHeaderEdit(this); mHeaderId = mViewStack->addWidget( edit ); // , KraftDoc::Header ); m_headerEdit = edit->docHeaderEdit(); m_headerEdit->m_cbType->clear(); // m_headerEdit->m_cbType->insertStringList( DefaultProvider::self()->docTypes() ); m_headerEdit->m_cbType->insertItems(-1, DocType::allLocalised() ); m_headerEdit->mButtLang->hide(); const QString predecessorDbId = m_doc->predecessorDbId(); bool predecIsVisible = false; if( !predecessorDbId.isEmpty() ) { DocGuardedPtr predecDoc = DocumentMan::self()->openDocument(predecessorDbId); if( predecDoc ) { const QString id = predecDoc->docIdentifier(); const QString link = QString("%2").arg(predecessorDbId).arg(id); m_headerEdit->_labFollowup->setText( i18n("Successor of %1", link)); predecIsVisible = true; connect( m_headerEdit->_labFollowup, SIGNAL(linkActivated(QString)), this, SLOT(slotLinkClicked(QString))); delete predecDoc; } } m_headerEdit->_labFollowup->setVisible(predecIsVisible); connect( m_headerEdit->m_cbType, SIGNAL( activated( const QString& ) ), this, SLOT( slotDocTypeChanged( const QString& ) ) ); connect( m_headerEdit->mButtLang, SIGNAL( clicked() ), this, SLOT( slotLanguageSettings() ) ); connect( edit, SIGNAL( modified() ), this, SLOT( slotModifiedHeader() ) ); connect( edit, SIGNAL(pickAddressee()), this, SLOT(slotPickAddressee()) ); } void KraftView::slotLinkClicked(const QString& link) { QUrl u(link); if( u.scheme() == "doc" && u.host() == "show" ) { const QString ident = u.queryItemValue("id"); qDebug() << "Link clicked to open document " << ident; emit openROView( ident ); } } void KraftView::setupItems() { KraftDocPositionsEdit *edit = new KraftDocPositionsEdit(this); mDocPosEditorIndx = mViewStack->addWidget( edit ); // , KraftDoc::Positions ); m_positionScroll = edit->positionScroll(); connect( edit, SIGNAL( addPositionClicked() ), SLOT( slotAddNewItem() ) ); connect( edit, SIGNAL( addExtraClicked() ), SLOT( slotAddExtraPosition() ) ); connect( edit, SIGNAL( importItemsClicked() ), SLOT( slotImportItems() ) ); } void KraftView::redrawDocument( ) { KraftDoc *doc = getDocument(); if( !doc ) { // qDebug () << "ERR: No document available in view, return!" << endl; } else { // qDebug () << "** Starting redraw of document " << doc << endl; } /* header: date and document type */ QDate date = doc->date(); m_headerEdit->m_dateEdit->setDate( date ); m_headerEdit->m_cbType->setCurrentIndex(m_headerEdit->m_cbType->findText( doc->docType() )); /* header: address */ mContactUid = doc->addressUid(); QString address = doc->address(); // qDebug () << "Loaded address uid from database " << mContactUid << endl; if( ! mContactUid.isEmpty() ) { mAddressProvider->lookupAddressee( mContactUid ); } if( !address.isEmpty() ) { // custom address stored in the document. // qDebug() << "custom address came in: " << address << endl; m_headerEdit->m_postAddressEdit->setText( address ); } if( !doc->salut().isEmpty() ) { m_headerEdit->m_letterHead->insertItem(-1, doc->salut() ); m_headerEdit->m_letterHead->setCurrentIndex(m_headerEdit->m_letterHead->findText( doc->salut() )); } /* pre- and post text */ m_headerEdit->m_teEntry->setText( doc->preText() ); m_headerEdit->m_whiteboardEdit->setText( doc->whiteboard() ); m_headerEdit->mProjectLabelEdit->setText( doc->projectLabel() ); m_footerEdit->ui()->m_teSummary->setText( doc->postText() ); const QString goodbye = doc->goodbye(); m_footerEdit->slotSetGreeting(goodbye); mAssistant->slotSetDocType( doc->docType() ); redrawDocPositions( ); slotDocTypeChanged( doc->docType() ); refreshPostCard(); mModifiedMapper->blockSignals(false); // mModified = false; } void KraftView::slotPickAddressee() { AddressSelectorDialog dialog(this); if( dialog.exec() ) { const KContacts::Addressee contact = dialog.addressee(); slotNewAddress( contact ); } } void KraftView::slotAddresseeFound( const QString& uid, const KContacts::Addressee& contact ) { if( contact.isEmpty() ) { const QString err = mAddressProvider->errorMsg(uid); if( !err.isEmpty() ) { qDebug () << "Error while looking up address for uid" << uid << ":" << err; } } else { slotNewAddress( contact, false ); qDebug () << "No contact found for uid " << uid; } } void KraftView::slotNewAddress( const KContacts::Addressee& contact, bool interactive ) { Addressee adr( contact ); if( contact.isEmpty() ) { return; } QString newAddress = mAddressProvider->formattedAddress( contact ); const QString currAddress = m_headerEdit->m_postAddressEdit->toPlainText(); bool replace = true; m_headerEdit->m_labName->setText( contact.realName() ); if( currAddress.isEmpty() ) { replace = true; } else if( currAddress != newAddress ) { // non empty and current different from new address // need to ask first if we overwrite if( interactive ) { QMessageBox msgBox; msgBox.setText(i18n( "The address label is not empty and different from the selected one.
            " "Do you really want to replace it with the text shown below?
            %1
            ", newAddress)); msgBox.setStandardButtons(QMessageBox::Yes| QMessageBox::No); msgBox.setDefaultButton(QMessageBox::Yes); int ret = msgBox.exec(); if ( ret != QMessageBox::Yes) { replace = false; } } else { // this happens when the document is loaded and the address arrives from addressbook replace = false; } } else if( currAddress == newAddress ) { // both are equal, no action needed replace = false; } if( replace ) { mContactUid = contact.uid(); m_headerEdit->m_postAddressEdit->setText( newAddress ); // Generate the welcome m_headerEdit->m_letterHead->clear(); QStringList li = generateLetterHead(adr.familyName(), adr.givenName()); m_headerEdit->m_letterHead->insertItems(-1, li ); m_headerEdit->m_letterHead->setCurrentIndex( KraftSettings::self()->salut() ); } } void KraftView::redrawDocPositions( ) { // If there is no position yet, come up with a help message. KraftDoc *doc = getDocument(); if ( ! doc ) return; DocPositionList list = doc->positions(); if ( list.count() == 0 ) { // the doc has no positions yet. Let's show a help page if ( ! mHelpLabel ) { mHelpLabel = new QLabel(this); mHelpLabel->setTextFormat(Qt::RichText); // mHelpLabel->setMinimumHeight(400); //TODO PORT QT5 mHelpLabel->setMargin( QDialog::marginHint() ); mHelpLabel->setText( i18n( "

            The Document Items List is still empty, but Items " "can be added now.

            " "To add items to the document either " "
              " "
            • Press the 'Add item' button above.
            • " "
            • Open the template catalog by clicking on the 'show Template' " "button on the right and pick one of the available templates.
            • " "
            " ) ); mHelpLabel->setWordWrap(true); mHelpLabel->setMinimumHeight(200); m_positionScroll->addChild( mHelpLabel, 0); } return; } else { if ( mHelpLabel ) { delete mHelpLabel; mHelpLabel = nullptr; } } // qDebug () << "starting to redraw " << list.count() << " positions!"; int cnt = 0; DocPositionListIterator it( list ); while( it.hasNext() ) { DocPositionBase *dp = it.next(); PositionViewWidget *w = mPositionWidgetList.widgetFromPosition( dp ); if( !w ) { w = createPositionViewWidget( dp, cnt); w->slotAllowIndividualTax( currentTaxSetting() == DocPositionBase::TaxIndividual ); } cnt++; // qDebug () << "now position " << dp->positionNumber() << endl; } // now go through the positionWidgetMap and check if it contains elements // that are not any longer in the list of positions QMutableListIterator it2( mPositionWidgetList ); while( it2.hasNext() ) { PositionViewWidget *w = it2.next(); if( w && (list.contains( w->position() ) == 0) ) { // qDebug () << "Removing this one: " << w << endl; m_positionScroll->removeChild( w ); it2.remove(); // mPositionWidgetList.erase( it2 ); break; } } } void KraftView::setMappingId( QWidget *widget, int pos ) { mDeleteMapper->setMapping( widget, pos ); mMoveUpMapper->setMapping( widget, pos ); mMoveDownMapper->setMapping( widget, pos ); mLockPositionMapper->setMapping( widget, pos ); mUnlockPositionMapper->setMapping( widget, pos ); mModifiedMapper->setMapping( widget, pos ); } // // create a new position widget. // The position parameter comes in as list counter, starting at 0 PositionViewWidget *KraftView::createPositionViewWidget( DocPositionBase *dp, int pos ) { PositionViewWidget *w = new PositionViewWidget( ); int cw = m_positionScroll->width(); if ( cw < 400 ) cw = 400; w->resize( cw, w->height() ); connect( w, SIGNAL( deletePosition() ), mDeleteMapper, SLOT( map() ) ); connect( w, SIGNAL( moveUp() ), mMoveUpMapper, SLOT( map() ) ); connect( w, SIGNAL( moveDown() ), mMoveDownMapper, SLOT( map() ) ); connect( w, SIGNAL( lockPosition() ), mLockPositionMapper, SLOT( map() ) ); connect( w, SIGNAL( unlockPosition() ), mUnlockPositionMapper, SLOT( map() ) ); connect( w, SIGNAL( positionModified()), mModifiedMapper, SLOT( map() ) ); setMappingId( w, pos ); QStringList units = UnitManager::self()->allUnits(); units.sort(); w->m_cbUnit->addItems(units); if( dp->dbId().toInt() < 0 ) { w->slotSetState( PositionViewWidget::New ); } /* do resizing and add the widget to the scrollview and move it to the final place */ if ( mHelpLabel ) { mHelpLabel->hide(); } mPositionWidgetList.insert( pos, w ); m_positionScroll->addChild( w, pos ); //Set the correct indexes when the widget is not appended if(pos < mPositionWidgetList.count()) { for(int i = pos+1; i < mPositionWidgetList.count(); ++i) { mPositionWidgetList.at(i)->setOrdNumber(i+1); setMappingId(mPositionWidgetList.at(i), i); } } w->setDocPosition(dp); w->setOrdNumber( pos+1 ); w->slotSetTax( dp->taxType() ); return w; } DocPositionBase::TaxType KraftView::currentTaxSetting() { // add 1 to the currentItem since that starts with zero. int taxKind = 1+( m_footerEdit->ui()->mTaxCombo->currentIndex() ); DocPositionBase::TaxType tt = DocPositionBase::TaxInvalid; if ( taxKind == 1 ) { // No Tax at all tt = DocPositionBase::TaxNone; } else if ( taxKind == 2 ) { // Reduced tax for all items tt = DocPositionBase::TaxReduced; } else if ( taxKind == 3 ) { // Full tax for all items tt = DocPositionBase::TaxFull; } else { // individual level tt = DocPositionBase::TaxIndividual; } return tt; } void KraftView::refreshPostCard() { DocPositionList positions = currentPositionList(); if( !getDocument() ) return; if ( mAssistant->postCard() ) { QDate d = m_headerEdit->m_dateEdit->date(); const QString dStr = Format::toDateString( d, KraftSettings::self()->dateFormat() ); mAssistant->postCard()->setHeaderData( m_headerEdit->m_cbType->currentText(), dStr, m_headerEdit->m_postAddressEdit->toPlainText(), getDocument()->ident(), m_headerEdit->m_teEntry->toPlainText() ); mAssistant->postCard()->setPositions( positions, currentTaxSetting(), DocumentMan::self()->tax( d ), DocumentMan::self()->reducedTax( d ) ); mAssistant->postCard()->setFooterData( m_footerEdit->ui()->m_teSummary->toPlainText(), m_footerEdit->greeting() ); mAssistant->postCard()->renderDoc( mViewStack->currentIndex() ); // id( mViewStack->visibleWidget() ) ); } DocPositionListIterator it( positions ); DocPositionBase *dp; while( it.hasNext()) { dp = it.next(); if ( dp->type() == DocPositionBase::ExtraDiscount ) { PositionViewWidget *w = ( static_cast( dp ) )->associatedWidget(); if( w ) { w->slotSetOverallPrice( ( static_cast( dp ) )->overallPrice() ); } else { // qDebug () << "Warning: Position object has no associated widget!" << endl; } } } } void KraftView::setupFooter() { m_footerEdit = new KraftDocFooterEdit(this); mViewStack->addWidget( m_footerEdit ); // KraftDoc::Footer ); // ATTENTION: If you change the following inserts, make sure to check the code // in method currentPositionList! m_footerEdit->ui()->mTaxCombo->insertItem( NO_TAX, QIcon::fromTheme("kraft_notax"), i18n( "Display no tax at all" )); m_footerEdit->ui()->mTaxCombo->insertItem( RED_TAX, QIcon::fromTheme("kraft_redtax"), i18n( "Calculate reduced tax for all items" )); m_footerEdit->ui()->mTaxCombo->insertItem( FULL_TAX, QIcon::fromTheme("kraft_fulltax"), i18n( "Calculate full tax for all items" )); m_footerEdit->ui()->mTaxCombo->insertItem( INDI_TAX, i18n( "Calculate individual tax for each item")); // set the tax type combo correctly: If all items have the same tax type, take it. // If items have different, its the individual thing. DocPositionList list = m_doc->positions(); int tt = -1; DocPositionBase *dp = nullptr; DocPositionListIterator it( list ); int taxIndex = 0; while( it.hasNext()) { dp = it.next(); if ( tt == -1 ) tt = dp->taxTypeNumeric(); // store the first entry. else { if ( tt != dp->taxTypeNumeric() ) { taxIndex = INDI_TAX; } else { // old and new taxtype are the same. } } } if ( tt == -1 ) { // means that there is no item yet, the default tax type needs to be used. int deflt = KraftSettings::self()->defaultTaxType(); if ( deflt > 0 ) { deflt -= 1; } taxIndex = deflt; } else { if ( taxIndex == 0 ) { taxIndex = tt-1; } } connect( m_footerEdit->ui()->mTaxCombo,SIGNAL(currentIndexChanged(int)),this,SLOT(slotTaxComboChanged(int))); m_footerEdit->ui()->mTaxCombo->setCurrentIndex( taxIndex ); slotTaxComboChanged( taxIndex ); mTaxBefore = taxIndex; connect(m_footerEdit,SIGNAL(modified()),this,SLOT(slotModifiedFooter())); } void KraftView::slotTaxComboChanged(int newId) { bool allowTaxSetting = false; if( mTaxBefore == newId ) return; if( mTaxBefore == INDI_TAX ) { // we're changing away from individual settings. WARNING needed. // qDebug () << "You switch away from item individual tax settings."; if( QMessageBox::question( this, i18n("Tax Settings Overwrite"), i18n("Really overwrite all individual tax settings of the items?") ) == QMessageBox::No ) { m_footerEdit->ui()->mTaxCombo->setCurrentIndex( INDI_TAX ); return; } } DocPositionBase::TaxType tax = DocPositionBase::TaxFull; if( newId == INDI_TAX ) { allowTaxSetting = true; } else if( newId == RED_TAX ) { tax = DocPositionBase::TaxReduced; } else if( newId == NO_TAX ) { tax = DocPositionBase::TaxNone; } PositionViewWidgetListIterator it( mPositionWidgetList ); while( it.hasNext() ) { PositionViewWidget *w = it.next(); w->slotAllowIndividualTax( allowTaxSetting ); w->slotSetTax( tax ); } mTaxBefore = newId; slotModifiedFooter(); } /* This is the flow in the move up method: 0 Bla1 0 Bla1 0 Bla1 1 Bla2 <- w2 1 Bla2 -> insert at pos-1 => 1 Bla3 pos -> 2 Bla3 <- w1 2 Bla4 2 Bla2 3 Bla4 3 Bla4 */ void KraftView::slotMovePositionUp( int pos ) { PositionViewWidget *w1 = nullptr; PositionViewWidget *w2 = nullptr; // qDebug () << "Moving position up: " << pos << endl; if( pos < 1 || pos > mPositionWidgetList.count() ) { // qDebug () << "ERR: position out of range: " << pos << endl; return; } mPositionWidgetList.swap( pos, pos-1 ); w1 = mPositionWidgetList.at( pos-1 ); w2 = mPositionWidgetList.at( pos ); // Porting ATTENTION: check assignment of w1, w1 // qDebug () << "Found at pos " << pos << " the widgets " << w1 << " and " << w2 << endl; if( w1 && w2 ) { // qDebug () << "Setting ord number: " << pos << endl; w1->setOrdNumber( pos ); // note: ordnumbers start with 1, thus add one w2->setOrdNumber( pos+1 ); setMappingId( w1, pos-1 ); setMappingId( w2, pos ); m_positionScroll->moveChild( w2, m_positionScroll->indexOf(w1) ); mModified = true; QTimer::singleShot( 0, this, SLOT(refreshPostCard() ) ); } else { // qDebug () << "ERR: Did not find the two corresponding widgets!" << endl; } } /* 0 Bla1 0 Bla1 0 Bla1 pos -> 1 Bla2 <- w1 1 Bla3 1 Bla3 2 Bla3 <- w2 2 Bla4 -> insert at pos+1 => 2 Bla2 3 Bla4 3 Bla4 */ void KraftView::slotMovePositionDown( int pos ) { PositionViewWidget *w1 = nullptr; PositionViewWidget *w2 = nullptr; // qDebug () << "Moving position down: " << pos << endl; if( pos < 0 || pos >= mPositionWidgetList.count() -1 ) { // qDebug () << "ERR: position out of range: " << pos << endl; return; } mPositionWidgetList.swap( pos, pos+1); w1 = mPositionWidgetList.at( pos+1 ); w2 = mPositionWidgetList.at( pos ); // Porting ATTENTION: check assignment of w1, w1 if( w1 && w2 ) { w1->setOrdNumber( pos+2 ); w2->setOrdNumber( pos+1 ); setMappingId( w1, pos+1 ); setMappingId( w2, pos ); m_positionScroll->moveChild( w1, m_positionScroll->indexOf( w2 ) ); mModified = true; QTimer::singleShot( 0, this, SLOT( refreshPostCard() ) ); } else { // qDebug () << "ERR: Did not find the two corresponding widgets!" << endl; } } void KraftView::slotDeletePosition( int pos ) { PositionViewWidget *w1 = mPositionWidgetList.at( pos ); if( w1 ) { w1->slotSetState( PositionViewWidget::Deleted ); w1->slotModified(); } } void KraftView::slotLockPosition( int pos ) { // qDebug () << "Locking Position " << pos << endl; PositionViewWidget *w1 = mPositionWidgetList.at( pos ); if( w1 ) { w1->slotSetState( PositionViewWidget::Locked ); refreshPostCard(); } } void KraftView::slotUnlockPosition( int pos ) { // qDebug () << "Unlocking Position " << pos << endl; PositionViewWidget *w1 = mPositionWidgetList.at( pos ); if( w1 ) { w1->slotSetState( PositionViewWidget::Active ); refreshPostCard(); } } void KraftView::slotPositionModified( int ) { // qDebug () << "Modified Position " << pos << endl; mModified = true; QTimer::singleShot( 0, this, SLOT( refreshPostCard() ) ); } void KraftView::slotAddressFound(const QString& uid, const KContacts::Addressee& contact) { const QString currAddress = m_headerEdit->m_postAddressEdit->toPlainText(); const QString newAddress = mAddressProvider->formattedAddress(contact); bool replace = true; if( currAddress.isEmpty() ) { replace = true; } else if(currAddress != newAddress) { // non empty and current different from new address // need to ask first if we overwrite if( QMessageBox::question( this, i18n("Address Overwrite"), i18n("The address label is not empty and different from the selected one.
            " "Do you really want to replace it with the text shown below?
            %1
            ", newAddress) ) == QMessageBox::No ) { replace = false; } } else if( currAddress == newAddress ) { // both are equal, no action needed return; } if( replace ) { mContactUid = uid; m_headerEdit->m_labName->setText( contact.realName() ); m_headerEdit->m_postAddressEdit->setText( newAddress ); // Generate the welcome m_headerEdit->m_letterHead->clear(); QStringList li = generateLetterHead( contact.familyName(), contact.givenName() ); m_headerEdit->m_letterHead->insertItems(-1, li ); KraftDoc *doc = getDocument(); if( doc->isNew() ) { m_headerEdit->m_letterHead->setCurrentIndex( KraftSettings::self()->salut() ); } else { if(!doc->salut().isEmpty()) { m_headerEdit->m_letterHead->setCurrentText( doc->salut() ); } } } } void KraftView::slotDocTypeChanged( const QString& newType ) { // qDebug () << "Doc Type changed to " << newType << endl; mAssistant->slotSetDocType( newType ); DocType docType( newType ); PositionViewWidgetListIterator it( mPositionWidgetList ); while( it.hasNext() ) { PositionViewWidget *w = it.next(); w->slotEnableKindMenu( docType.allowAlternative() ); w->slotShowPrice(docType.pricesVisible()); } mAssistant->postCard()->slotShowPrices( docType.pricesVisible() ); m_footerEdit->ui()->_taxGroup->setVisible( docType.pricesVisible() ); if( mDocPosEditorIndx > -1 ) { KraftDocPositionsEdit *w = dynamic_cast(mViewStack->widget(mDocPosEditorIndx)); if(w) { w->setDiscountButtonVisible(docType.pricesVisible()); } } } void KraftView::slotLanguageSettings() { // qDebug () << "Language Settings" << endl; // FIXME } void KraftView::slotNewHeaderText( const QString& str ) { m_headerEdit->m_teEntry->setText( str ); slotModifiedHeader(); } void KraftView::slotNewFooterText( const QString& str ) { m_footerEdit->ui()->m_teSummary->setText( str ); slotModifiedFooter(); } // Add a new item. A katalog is required if user wants to store it in a // catalog immediately. FIXME - now the current active catalog in the // catalog selection is used. void KraftView::slotAddNewItem() { Katalog* kat = mAssistant->catalogSelection()->currentSelectedKat(); slotAddItem( kat, nullptr, QString() ); } void KraftView::slotAddItems( Katalog *kat, CatalogTemplateList templates, const QString& selectedChapter) { if(templates.count() == 0) { slotAddItem(kat, nullptr, selectedChapter); } else { for(CatalogTemplate *templ : templates ) { slotAddItem( kat, templ, selectedChapter ); } } } void KraftView::slotAddItem( Katalog *kat, CatalogTemplate *tmpl, const QString& selectedChapter ) { // newpos is a list position, starts counting at zero! int newpos = mPositionWidgetList.count(); // qDebug () << "Adding Position at list position " << newpos << endl; QScopedPointer dia; DocPosition *dp = new DocPosition(); dp->setPositionNumber( newpos +1 ); bool newTemplate = false; if ( !tmpl ) { newTemplate = true; } dia.reset(new InsertTemplDialog( this )); dia->setCatalogChapters( kat->getKatalogChapters(), selectedChapter); int tmplId = 0; if ( !newTemplate ) { // it's not a new template dp->setText(tmpl->getText()); dp->setUnit(tmpl->unit()); dp->setUnitPrice(tmpl->unitPrice()); } if ( mRememberAmount > 0 ) { dp->setAmount( mRememberAmount ); } KraftDoc *doc = getDocument(); if(doc) { DocType docType = doc->docType(); dia->setDocPosition( dp, newTemplate, docType.pricesVisible() ); } DocPositionList list = currentPositionList(); dia->setPositionList( list, newpos ); QSize s = KraftSettings::self()->templateToPosDialogSize(); dia->resize( s ); int execResult = dia->exec(); if ( execResult == QDialog::Accepted ) { DocPosition diaPos = dia->docPosition(); *dp = diaPos; // set the tax settings if( currentTaxSetting() == DocPositionBase::TaxIndividual ) { // FIXME: In case a new item is added, add the default tax type. // otherwise add the tax of the template dp->setTaxType( DocPositionBase::TaxFull ); } else { dp->setTaxType( currentTaxSetting() ); } // store the initial size of the template-to-doc-pos dialogs s = dia->size(); KraftSettings::self()->setTemplateToPosDialogSize( s ); if ( kat->type() == TemplateCatalog ) { // save the template if it is has a valid chapter. // the method chapter() considers the checkbox. If it is not checked to keep the template, // an empty chapter is returned. const QString chapter = dia->chapter(); if (!chapter.isEmpty()) { int chapterId = KatalogMan::self()->defaultTemplateCatalog()->chapterID(chapter).toInt(); FloskelTemplate *flos = new FloskelTemplate( -1, dp->text(), dp->unit().id(), chapterId, 1 /* CalcKind = Manual */ ); flos->setManualPrice( dp->unitPrice() ); flos->save(); tmplId = flos->getTemplID(); // reload the entire katalog Katalog *defaultKat = KatalogMan::self()->defaultTemplateCatalog(); if( defaultKat ) { defaultKat->load(); KatalogMan::self()->notifyKatalogChange( defaultKat , dbID() ); } } } if (!newTemplate){ tmplId = static_cast(tmpl)->getTemplID(); } } else if ( kat->type() == MaterialCatalog ) { if ( newTemplate ) { // FIXME } } if (execResult == QDialog::Accepted) { newpos = dia->insertAfterPosition(); mRememberAmount = dp->amount(); if (tmplId > 0) { kat->recordUsage(tmplId); } PositionViewWidget *widget = createPositionViewWidget( dp, newpos ); widget->slotModified(); widget->slotAllowIndividualTax( currentTaxSetting() == DocPositionBase::TaxIndividual ); // Check if the new widget is supposed to display prices, based on the doc type // FIXME: Shouldn't this be done by the positionViewWidget rather than here? const QString dt = getDocument()->docType(); if( !dt.isEmpty() ) { DocType docType(dt); widget->slotShowPrice(docType.pricesVisible()); } slotFocusItem( widget, newpos ); refreshPostCard(); } } void KraftView::slotImportItems() { ImportItemDialog dia( this ); DocPositionList list = currentPositionList(); int newpos = list.count(); dia.setPositionList( list, newpos ); if ( dia.exec() ) { DocPositionList list = dia.positionList(); if ( list.count() > 0 ) { // qDebug () << "Importlist amount of entries: " << list.count(); int cnt = 0; int newpos = dia.getPositionCombo()->currentIndex(); // qDebug () << "Newpos is " << newpos; DocPositionListIterator posIt( list ); while( posIt.hasNext() ) { DocPosition *dp_old = static_cast(posIt.next()); DocPosition *dp = new DocPosition( *(dp_old) ); dp->setTaxType( currentTaxSetting() ); PositionViewWidget *widget = createPositionViewWidget( dp, newpos + cnt++ ); widget->slotSetTax( DocPositionBase::TaxFull ); // FIXME: Value from Import? widget->slotModified(); } refreshPostCard(); } } } void KraftView::slotAddExtraPosition() { // newpos is a list position, starts counting at 0 int newpos = mPositionWidgetList.count(); // qDebug () << "Adding EXTRA Position at position " << newpos << endl; DocPosition *dp = new DocPosition( DocPosition::ExtraDiscount ); dp->setPositionNumber( newpos+1 ); dp->setText( i18n( "Discount" ) ); Einheit e = UnitManager::self()->getPauschUnit(); dp->setUnit(e); if( currentTaxSetting() == DocPositionBase::TaxIndividual ) { dp->setTaxType( DocPositionBase::TaxFull ); } else { dp->setTaxType( currentTaxSetting() ); } // qDebug () << "New Extra position is " << dp << endl; PositionViewWidget *widget = createPositionViewWidget( dp, newpos ); // qDebug () << "PositionViewWiget doc position is: " << widget->position() << endl; widget->slotModified(); slotFocusItem( widget, newpos ); refreshPostCard(); } DocPositionList KraftView::currentPositionList() { DocPositionList list; PositionViewWidget *widget = nullptr; int cnt = 1; PositionViewWidgetListIterator outerIt( mPositionWidgetList ); bool progress = true; while ( progress && ( list.count() != mPositionWidgetList.count() ) ) { // the loop runs until all positions have a valid price. int preListCnt = list.count(); // qDebug() << "# Pre List Count: " << preListCnt << endl; while ( outerIt.hasNext() ) { widget = outerIt.next(); DocPositionBase *dpb = widget->position(); KraftDB::StringMap replaceMap; if ( dpb ) { DocPosition *newDp = new DocPosition( dpb->type() ); newDp->setPositionNumber( cnt++ ); newDp->setAttributeMap( dpb->attributes() ); newDp->setDbId( dpb->dbId().toInt() ); newDp->setAssociatedWidget( widget ); bool calculatable = true; if ( dpb->type() == DocPosition::ExtraDiscount ) { double discount = widget->mDiscountPercent->value(); /* set Attributes with the discount percentage */ Attribute a( DocPosition::Discount ); a.setPersistant( true ); a.setValue( discount ); newDp->setAttribute( a ); QString tagRequired = widget->extraDiscountTagRestriction(); if ( !tagRequired.isEmpty() ) { Attribute tr( DocPosition::ExtraDiscountTagRequired ); tr.setValueRelation( "tagTemplates", "tagTmplID", "name" ); tr.setPersistant( true ); tr.setValue( QVariant( tagRequired ) ); newDp->setAttribute( tr ); } /* Calculate the current sum over all widgets */ PositionViewWidgetListIterator it( mPositionWidgetList ); PositionViewWidget *w1; Geld sum; // qDebug () << "Starting to loop over the items " << endl; while ( calculatable && it.hasNext() ) { w1 = it.next(); if ( widget != w1 ) { // ATTENTION Porting: do not take the own value into account if ( tagRequired.isEmpty() // means that all positions are to calculate || w1->tagList().contains( tagRequired ) ) { if ( w1->priceValid() ) { sum += w1->currentPrice(); // qDebug () << "Summing up pos with text " << w1->ordNumber() << " and price " // << w1->currentPrice().toLong() << endl; } else { calculatable = false; // give up, we continue in outerIt // qDebug () << "We skip pos " << w1->ordNumber() << endl; } } } else { // we can not calculate ourselves. // qDebug () << "Skipping pos " << w1->ordNumber() << " in summing up, thats me!" << endl; } } // qDebug () << "Finished loop over items with calculatable=" << calculatable << endl; if ( calculatable ) { sum = sum.percent( discount ); newDp->setUnitPrice( sum ); newDp->setAmount( 1.0 ); widget->setCurrentPrice( sum ); } // replace some tags in the text replaceMap["%DISCOUNT"] = DefaultProvider().self()->locale()->toString( discount ); replaceMap["%ABS_DISCOUNT"] = DefaultProvider().self()->locale()->toString( qAbs( discount ) ); } else { /* Type is ordinary position */ newDp->setUnitPrice( widget->unitPrice() ); double v = widget->m_sbAmount->value(); newDp->setAmount( v ); widget->setCurrentPrice( newDp->overallPrice() ); } if ( calculatable ) { // copy information from the widget newDp->setToDelete( widget->deleted() ); QString t = widget->m_teFloskel->toPlainText(); if ( !replaceMap.empty() ) { t = KraftDB::self()->replaceTagsInWord( t, replaceMap ); } newDp->setText( t ); QString h = widget->m_cbUnit->currentText(); int eId = UnitManager::self()->getUnitIDSingular( h ); Einheit e = UnitManager::self()->getUnit( eId ); newDp->setUnit( e ); PositionViewWidget::Kind k = widget->kind(); if ( k != PositionViewWidget::Normal ) { Attribute a( DocPosition::Kind ); a.setPersistant( true ); a.setValue( widget->kindString() ); newDp->setAttribute( a ); } else { newDp->removeAttribute( DocPosition::Kind ); } /* set Attribute with the tags */ QStringList tagStrings = widget->tagList(); if ( !tagStrings.isEmpty() ) { Attribute tags( DocPosition::Tags ); tags.setValueRelation( "tagTemplates", "tagTmplID", "name" ); tags.setPersistant( true ); tags.setListValue( true ); tags.setValue( QVariant( tagStrings ) ); newDp->setAttribute( tags ); // qDebug() << "============ " << tags.toString() << endl; } else { newDp->removeAttribute( DocPosition::Tags ); } // tax settings if( currentTaxSetting() == DocPositionBase::TaxIndividual ) { newDp->setTaxType( widget->taxType() ); } else { newDp->setTaxType( currentTaxSetting() ); } list.append( newDp ); } } else { qCritical() << "Fatal: Widget without position found!" << endl; } } // qDebug() << " Post List Count: " << list.count() << endl; if ( preListCnt == list.count() ) { qCritical() << "No progress in widget list processing - abort!" << endl; progress = false; } } return list; } void KraftView::slotShowCatalog( bool on ) { if ( on ) { mAssistant->slotShowCatalog(); } else { mAssistant->setFullPreview( true, KraftDoc::Positions ); } } void KraftView::slotModifiedPositions() { // As long as the modified mapper is blocked, ignore this. if( mModifiedMapper->signalsBlocked() ) { return; } qDebug () << "Position Modified" << endl; mModified = true; } void KraftView::slotModifiedHeader() { // qDebug () << "Modified the header!" << endl; // As long as the modified mapper is blocked, ignore this. if( mModifiedMapper->signalsBlocked() ) { return; } mModified = true; QTimer::singleShot( 0, this, SLOT( refreshPostCard() ) ); } void KraftView::slotModifiedFooter() { // qDebug () << "Modified the footer!" << endl; // As long as the modified mapper is blocked, ignore this. if( mModifiedMapper->signalsBlocked() ) { return; } mModified = true; QTimer::singleShot( 0, this, SLOT( refreshPostCard() ) ); } QStringList KraftView::generateLetterHead( const QString& familyName, const QString& givenName ) { QStringList s; KraftDB::StringMap m; m[ "%NAME"] = familyName; m[ "%GIVEN_NAME"] = givenName; return KraftDB::self()->wordList( "salut", m ); } void KraftView::done( int r ) { bool okToContinue = true; //Closed using the cancel button .. Check if we can close if(r == 0) { if( mModified ) { okToContinue = documentModifiedMessageBox(); if(!okToContinue) { return; } } } //Closed using the OK button .. it can be closed, but data needs saved if( mModified && r > 0 ) { saveChanges(); emit viewClosed( r == 1, m_doc ); } // remember the sizes of the docassistant splitter if visible. mAssistant->saveSplitterSizes(); KraftSettings::self()->setDocViewSplitter(mCSplit->sizes()); const QByteArray geo = saveGeometry().toBase64(); KraftSettings::self()->setDocEditGeometry(geo); QDialog::done( r ); } void KraftView::saveChanges() { // qDebug () << "Saving changes!" << endl; KraftDoc *doc = getDocument(); if( !doc ) { // qDebug () << "ERR: No document available in view, return!" << endl; return; } // transfer all values to the document doc->setDate( m_headerEdit->m_dateEdit->date() ); doc->setAddressUid( mContactUid ); doc->setAddress( m_headerEdit->m_postAddressEdit->toPlainText() ); doc->setDocType( m_headerEdit->m_cbType->currentText() ); doc->setPreText( m_headerEdit->m_teEntry->toPlainText() ); doc->setWhiteboard( m_headerEdit->m_whiteboardEdit->toPlainText() ); doc->setProjectLabel( m_headerEdit->mProjectLabelEdit->text() ); doc->setSalut( m_headerEdit->m_letterHead->currentText() ); doc->setPostText( m_footerEdit->ui()->m_teSummary->toPlainText() ); doc->setGoodbye( m_footerEdit->greeting() ); DocPositionList list = currentPositionList(); doc->setPositionList( list ); doc->saveDocument( ); if ( doc->isNew() ) { // For new documents the user had to select a greeting and we make this // default for the future KraftSettings::self()->setGreeting( m_footerEdit->greeting() ); KraftSettings::self()->setSalut( m_headerEdit->m_letterHead->currentIndex() ); } } void KraftView::slotFocusItem( PositionViewWidget *posWidget, int pos ) { if( posWidget && pos > 0) { int y = (1+pos)*posWidget->height(); m_positionScroll->ensureVisible(0, y); } else { m_positionScroll->ensureVisible( 0, 0 ); } // setting Focus within the item. if( posWidget ) { if( posWidget->m_teFloskel->toPlainText().isEmpty() ) { posWidget->m_teFloskel->setFocus(); } else { posWidget->m_sbAmount->setFocus(); } } } bool KraftView::documentModifiedMessageBox() { if ( mModified ) { QMessageBox msgBox; msgBox.setText(i18n("The document has been modified.")); msgBox.setInformativeText(i18n("Do you want to save your changes?")); msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); msgBox.setDefaultButton(QMessageBox::Save); int ret = msgBox.exec(); if( ret == QMessageBox::Cancel ) { return false; } } return true; } void KraftView::discardChanges() { // We need to reread the document KraftDoc *doc = getDocument(); if( doc && doc->isModified() ) { // qDebug () << "Document refetch from database" << endl; doc->reloadDocument(); } } kraft-0.97/src/kraftview.h000066400000000000000000000143261410616450300155300ustar00rootroot00000000000000/*************************************************************************** kraftview.h - view of kraft documents ------------------- begin : Mit Dez 31 19:24:05 CET 2003 copyright : (C) 2003 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef KRAFTVIEW_H #define KRAFTVIEW_H /** The KraftView class provides the view widget for the KraftApp instance. * The View instance inherits QWidget as a base class and represents * the view object of a KTMainWindow. As KraftView is part of the * docuement-view model, it needs a reference to the document object * connected with it by the KraftApp class to manipulate and display * the document structure provided by the KraftDoc class. * * @author Source Framework Automatically Generated by KDevelop, (c) The KDevelop Team. * @version KDevelop version 0.4 code generation */ // include files for Qt #include #include #include #include #include #include "kraftdoc.h" #include "positionviewwidget.h" #include "catalogtemplate.h" #include "ui_docheader.h" #include "ui_docfooter.h" class PositionViewWidget; class DocPosition; class QLabel; class QWidget; class QResizeEvent; class QSignalMapper; class QStackedWidget; class QSplitter; class DocPostCard; class QTimer; class DocAssistant; class CalcPartList; class AddressProvider; class KraftDocFooterEdit; class Katalog; class KraftViewScroll; class KraftViewBase: public QDialog { Q_OBJECT public: enum Type { ReadWrite, ReadOnly }; KraftViewBase(QWidget *parent) : QDialog(parent), m_type(ReadWrite) { } virtual ~KraftViewBase() { } Type type() { return m_type; } protected: KraftDoc *getDocument() const { return m_doc; } virtual void setup( DocGuardedPtr doc ) { m_doc = doc; } DocGuardedPtr m_doc; Type m_type; protected slots: virtual void slotLinkClicked(const QString& link) = 0; signals: void viewClosed( bool, DocGuardedPtr ); void openROView( QString docId ); private: }; class KraftView : public KraftViewBase { Q_OBJECT public: /** Constructor for the main view */ KraftView(QWidget *parent); /** Destructor for the main view */ virtual ~KraftView(); /** returns a pointer to the document connected to the view instance. Mind that this method requires a KraftApp instance as a parent * widget to get to the window document pointer by calling the KraftApp::getDocument() method. * * @see KraftApp#getDocument */ typedef QMap PositionMap; DocPositionList currentPositionList(); DocPositionBase::TaxType currentTaxSetting(); void setup( DocGuardedPtr doc ); public slots: void slotAddressFound(const QString& uid, const KContacts::Addressee &contact); void slotAddresseeFound( const QString& uid, const KContacts::Addressee& contact); void redrawDocument( ); void slotModifiedPositions(); void slotModifiedHeader(); void slotModifiedFooter(); void slotAddItem( Katalog*, CatalogTemplate*, const QString& selectedChapter); void slotAddNewItem(); void slotAddItems(Katalog*, CatalogTemplateList , const QString &selectedChapter); void slotAddExtraPosition(); void slotImportItems(); void slotFocusItem( PositionViewWidget*, int ); void slotNewHeaderText( const QString& ); void slotNewFooterText( const QString& ); void slotSwitchToPage( int ); protected slots: // void closeEvent(QCloseEvent *event); void redrawDocPositions( ); void done( int ); void slotMovePositionUp( int ); void slotMovePositionDown( int ); void slotDeletePosition( int ); void slotUnlockPosition( int ); void slotLockPosition( int ); void slotPositionModified( int ); void refreshPostCard( ); void slotShowCatalog( bool ); void slotShowTemplates( bool ); void slotDocTypeChanged( const QString& ); void slotLanguageSettings(); void slotPickAddressee(); void slotTaxComboChanged( int ); void slotNewAddress( const KContacts::Addressee& contact, bool interactive = true ); void slotLinkClicked(const QString& link); signals: void selectPage( int ); void positionSelected( Katalog*, void* ); private: void setupDocHeaderView(); void setupItems(); void setupFooter(); void setupTextsView(); void setMappingId( QWidget *, int ); void setupMappers(); void saveChanges(); void discardChanges(); bool documentModifiedMessageBox(); PositionViewWidget *createPositionViewWidget( DocPositionBase*, int ); QStringList generateLetterHead(const QString &familyName , const QString &givenName); KraftViewScroll *m_positionScroll; Ui::DocHeaderEdit *m_headerEdit; // Ui::DocFooterEdit *m_footerEdit; KraftDocFooterEdit *m_footerEdit; PositionViewWidgetList mPositionWidgetList; QString mContactUid; QSignalMapper *mDeleteMapper; QSignalMapper *mMoveUpMapper; QSignalMapper *mMoveDownMapper; QSignalMapper *mUnlockPositionMapper; QSignalMapper *mLockPositionMapper; QSignalMapper *mModifiedMapper; AddressProvider *mAddressProvider; QLabel *mDetailHeader; QSplitter *mCSplit; QPushButton *mCatalogToggle; QLabel *mHelpLabel; QWidget *mSumSpacer; QStackedWidget *mViewStack; int mHeaderId; DocAssistant *mAssistant; double mRememberAmount; QMap mCalculationsMap; bool mModified; int mTaxBefore; int mDocPosEditorIndx; }; #endif // KRAFTVIEW_H kraft-0.97/src/kraftview_ro.cpp000066400000000000000000000236751410616450300165720ustar00rootroot00000000000000/*************************************************************************** kraftview.cpp - ------------------- begin : Mit Dez 31 19:24:05 CET 2003 copyright : (C) 2003 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ // include files for Qt #include #include #include #include #include #include #include #include #include #include #include #include // application specific includes #include "kraftdb.h" #include "kraftsettings.h" #include "kraftview_ro.h" #include "kraftdoc.h" #include "portal.h" #include "ui_docheader.h" #include "docassistant.h" #include "positionviewwidget.h" #include "ui_docfooter.h" #include "docposition.h" #include "unitmanager.h" #include "docpostcard.h" #include "kataloglistview.h" #include "katalogman.h" #include "templkatalog.h" #include "templkataloglistview.h" #include "catalogselection.h" #include "kraftdocheaderedit.h" #include "kraftdocpositionsedit.h" #include "kraftdocfooteredit.h" #include "inserttempldialog.h" #include "defaultprovider.h" #include "stockmaterial.h" #include "templtopositiondialogbase.h" #include "doctype.h" #include "catalogtemplate.h" #include "format.h" #include #include #include #include "texttemplate.h" #include "documentman.h" // ######################################################### KraftViewRO::KraftViewRO(QWidget *parent, const char *name) : KraftViewBase( parent ) { setObjectName( name ); setModal( false ); setWindowTitle( i18n("Document" ) ); QWidget *mainWidget = new QWidget(this); QVBoxLayout *mainLayout = new QVBoxLayout; setLayout(mainLayout); mainLayout->addWidget(mainWidget); m_type = ReadOnly; mHtmlView = new HtmlView( this ); mainLayout->addWidget(mHtmlView); mHtmlView->setStylesheetFile( "docoverview_ro.css" ); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); mainLayout->addWidget(buttonBox); } KraftViewRO::~KraftViewRO() { } #define QL1(X) QStringLiteral(X) QString KraftViewRO::htmlify( const QString& str ) const { QStringList li = str.toHtmlEscaped().split( "\n" ); return QL1("

            ") + li.join( "

            " ) + QL1("

            "); } #define DOC_RO_TAG(X) QLatin1String(X) void KraftViewRO::setup( DocGuardedPtr doc ) { KraftViewBase::setup( doc ); if ( !doc ) return; QLocale *locale = DefaultProvider::self()->locale(); // do stuff like open a template and render values into it. QString tmplFile = DefaultProvider::self()->locateFile( "views/kraftdoc_ro.thtml" ); if( tmplFile.isEmpty() ) { // qDebug () << "Could not find template to render ro view of document."; return; } else { qDebug() << "Template file: " << tmplFile; } TextTemplate tmpl; tmpl.setTemplateFileName(tmplFile); if( !tmpl.isOk() ) { return; } tmpl.setValue( DOC_RO_TAG( "HEADLINE" ), doc->docType() + " " + doc->ident() ); tmpl.setValue( DOC_RO_TAG( "DATE" ), Format::toDateString(doc->date(), KraftSettings::self()->dateFormat())); tmpl.setValue( DOC_RO_TAG( "DOC_TYPE" ), doc->docType() ); QString address = doc->address(); address.replace( '\n', "
            " ); tmpl.setValue( DOC_RO_TAG( "ADDRESS" ), address ); tmpl.setValue( DOC_RO_TAG( "DOCNO" ), doc->ident() ); tmpl.setValue( DOC_RO_TAG( "PRETEXT" ), htmlify(doc->preText()) ); tmpl.setValue( DOC_RO_TAG( "POSTTEXT" ), htmlify(doc->postText()) ); tmpl.setValue( DOC_RO_TAG( "SALUT" ), doc->salut() ); tmpl.setValue( DOC_RO_TAG( "GOODBYE" ), doc->goodbye() ); DocPositionList positions = doc->positions(); // check the tax settings: If all items have the same settings, its not individual. bool individualTax = false; int ttype = -1; foreach( DocPositionBase *dp, positions ) { if( ttype == -1 ) { ttype = dp->taxType(); } else { if( ttype != dp->taxType() ) { // different from previous one? individualTax = true; break; } } } int pos = 1; int taxFreeCnt = 0; int reducedTaxCnt = 0; int fullTaxCnt = 0; QString docType = doc->docType(); DocType dt(docType); foreach( DocPositionBase *dpb, positions ) { DocPosition *dp = static_cast(dpb); tmpl.createDictionary( "ITEMS" ); tmpl.setValue( "ITEMS", "NUMBER", QString::number( pos++ ) ); tmpl.setValue( "ITEMS", "TEXT", htmlify(dp->text() )); tmpl.setValue( "ITEMS", "AMOUNT", locale->toString( dp->amount() ) ); tmpl.setValue( "ITEMS", "UNIT", dp->unit().einheit( dp->amount() ) ); double singlePrice = dp->unitPrice().toDouble(); if( dt.pricesVisible() ) { tmpl.createSubDictionary("ITEMS", "PRICE_DISPLAY"); tmpl.setValue( "PRICE_DISPLAY", "SINGLE_PRICE", locale->toCurrencyString( singlePrice ) ); QString style( "positive" ); if ( singlePrice < 0 ) { style = "negative"; } tmpl.setValue( "PRICE_DISPLAY", "PRICE_STYLE", style ); tmpl.setValue( "PRICE_DISPLAY", "PRICE", locale->toCurrencyString( dp->overallPrice().toDouble() ) ); QString taxType; if( individualTax ) { if( dp->taxType() == 1 ) { taxFreeCnt++; taxType = "TAX_FREE"; } else if( dp->taxType() == 2 ) { taxType = "REDUCED_TAX"; reducedTaxCnt++; } else { // ATTENTION: Default for all non known tax types is full tax. fullTaxCnt++; taxType = "FULL_TAX"; } } tmpl.createSubDictionary("PRICE_DISPLAY", taxType); } } if( dt.pricesVisible()) { tmpl.createDictionary("DISPLAY_SUM_BLOCK"); tmpl.setValue( "DISPLAY_SUM_BLOCK", DOC_RO_TAG( "TAXLABEL" ), i18n( "VAT" ) ); tmpl.setValue( "DISPLAY_SUM_BLOCK", DOC_RO_TAG( "REDUCED_TAXLABEL" ), i18n( "Reduced TAX" ) ); tmpl.setValue( "DISPLAY_SUM_BLOCK", DOC_RO_TAG( "NETTOSUM" ), locale->toCurrencyString( doc->nettoSum().toDouble() ) ); tmpl.setValue( "DISPLAY_SUM_BLOCK", DOC_RO_TAG( "BRUTTOSUM" ), locale->toCurrencyString( doc->bruttoSum().toDouble() ) ); if( individualTax ) { tmpl.createSubDictionary( "DISPLAY_SUM_BLOCK", "TAX_FREE_ITEMS" ); tmpl.setValue( "TAX_FREE_ITEMS", "COUNT", QString::number( taxFreeCnt )); tmpl.createSubDictionary( "DISPLAY_SUM_BLOCK", "REDUCED_TAX_ITEMS" ); tmpl.setValue( "REDUCED_TAX_ITEMS", "COUNT", QString::number( reducedTaxCnt )); tmpl.setValue( "REDUCED_TAX_ITEMS", "TAX", locale->toString( DocumentMan::self()->reducedTax( doc->date() ))); tmpl.createSubDictionary( "DISPLAY_SUM_BLOCK", "FULL_TAX_ITEMS" ); tmpl.setValue( "FULL_TAX_ITEMS", "COUNT", QString::number( fullTaxCnt )); tmpl.setValue( "FULL_TAX_ITEMS", "TAX", locale->toString( DocumentMan::self()->tax( doc->date() )) ); } double redTax = DocumentMan::self()->reducedTax( doc->date() ); double fullTax = DocumentMan::self()->tax( doc->date() ); QString h; if ( positions.reducedTaxSum( redTax ).toLong() > 0 ) { tmpl.createSubDictionary( "DISPLAY_SUM_BLOCK", "SECTION_REDUCED_TAX" ); tmpl.setValue( "SECTION_REDUCED_TAX", DOC_RO_TAG( "REDUCED_TAX_SUM" ), positions.reducedTaxSum( redTax ).toString() ); h.setNum( redTax, 'f', 1 ); tmpl.setValue( "SECTION_REDUCED_TAX", DOC_RO_TAG( "REDUCED_TAX" ), h ); tmpl.setValue( "SECTION_REDUCED_TAX", DOC_RO_TAG( "REDUCED_TAX_LABEL" ), i18n( "reduced VAT" ) ); } if ( positions.fullTaxSum( fullTax ).toLong() > 0 ) { tmpl.createSubDictionary( "DISPLAY_SUM_BLOCK", "SECTION_FULL_TAX" ); tmpl.setValue( "SECTION_FULL_TAX", DOC_RO_TAG( "FULL_TAX_SUM" ), positions.fullTaxSum( fullTax ).toString() ); h.setNum( fullTax, 'f', 1 ); tmpl.setValue( "SECTION_FULL_TAX", DOC_RO_TAG( "FULL_TAX" ), h ); tmpl.setValue( "SECTION_FULL_TAX", DOC_RO_TAG( "FULL_TAX_LABEL" ), i18n( "VAT" ) ); } tmpl.setValue( "DISPLAY_SUM_BLOCK", DOC_RO_TAG( "TAXSUM" ), locale->toCurrencyString( doc->vatSum().toDouble() ) ); } // Visible sum block setWindowTitle( m_doc->docIdentifier() ); mHtmlView->setTitle( doc->docIdentifier() ); mHtmlView->displayContent(tmpl.expand()); } void KraftViewRO::slotLinkClicked(const QString& link) { Q_UNUSED(link) // nothing we do here yet } void KraftViewRO::done( int r ) { // qDebug () << "View closed with ret value " << r; KraftDoc *doc = getDocument(); if( !doc ) { // qDebug () << "ERR: No document available in view, return!"; return; } emit viewClosed( true, m_doc ); KraftViewBase::done(r); } kraft-0.97/src/kraftview_ro.h000066400000000000000000000046211410616450300162250ustar00rootroot00000000000000/*************************************************************************** kraftview.h - view of kraft documents ------------------- begin : Mit Dez 31 19:24:05 CET 2003 copyright : (C) 2003 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef KRAFTVIEW_RO_H #define KRAFTVIEW_RO_H // include files for Qt #include #include #include #include #include #include #include "kraftdoc.h" /** The KraftViewRO class provides the read only view widget for Kraft documents. * The View instance inherits QWidget as a base class and represents * the view object of a KTMainWindow. As KraftView is part of the * docuement-view model, it needs a reference to the document object * connected with it by the KraftApp class to manipulate and display * the document structure provided by the KraftDoc class. * * @author Source Framework Automatically Generated by KDevelop, (c) The KDevelop Team. * @version KDevelop version 0.4 code generation */ #include "positionviewwidget.h" #include "catalogtemplate.h" #include "kraftview.h" class Katalog; class HtmlView; class KraftViewRO : public KraftViewBase { public: /** Constructor for the main view */ KraftViewRO(QWidget *parent, const char *name=0); /** Destructor for the main view */ virtual ~KraftViewRO(); void setup( DocGuardedPtr doc ); QString htmlify( const QString& str ) const; protected slots: void done( int ); void slotLinkClicked(const QString& link); signals: void positionSelected( Katalog*, void* ); private: HtmlView *mHtmlView; }; #endif // KRAFTVIEW_RO_H kraft-0.97/src/main.cpp000066400000000000000000000046661410616450300150130ustar00rootroot00000000000000/*************************************************************************** main.cpp - ------------------- begin : Mit Dez 31 19:24:05 CET 2003 copyright : (C) 2003 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "version.h" #include "portal.h" #include "defaultprovider.h" #include "archdocposition.h" int main(int argc, char *argv[]) { KLocalizedString::setApplicationDomain("kraft"); Q_INIT_RESOURCE(kraft); qRegisterMetaType("ArchDocPositionList"); QApplication app(argc, argv); app.setWindowIcon(QIcon(":/kraft/global/32-apps-kraft.png")); app.setApplicationName("kraft"); app.setApplicationDisplayName("Kraft"); app.setApplicationVersion(QString("version %1").arg(KRAFT_VERSION)); QCommandLineParser parser; parser.addVersionOption(); parser.addHelpOption(); parser.addOption(QCommandLineOption(QStringList() << QLatin1String("d"), i18n("Open document with arch doc number "), QLatin1String("number"))); parser.addOption(QCommandLineOption(QStringList() << QLatin1String("r"), i18n("Open Kraft in read only mode - document changes prohibited") )); parser.process(app); // Register the supported options QScopedPointer kraftPortal; kraftPortal.reset( new Portal( nullptr, &parser, "kraft main window" )); kraftPortal->show(); return app.exec(); } kraft-0.97/src/matcalcdialog.cpp000066400000000000000000000035601410616450300166430ustar00rootroot00000000000000/*************************************************************************** matcalcdialog - ------------------- begin : 2005-03-00 copyright : (C) 2005 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ // include files for Qt #include #include #include #include "matcalcdialog.h" #include "materialcalcpart.h" #include "stockmaterial.h" MatCalcDialog::MatCalcDialog( MaterialCalcPart *mc, QWidget *parent ) : CalcDialogBase( parent ), _matWidget(new Ui::calcdetailMat), m_mc(mc) { _matWidget->setupUi(_centralWidget); _matWidget->m_inpMenge->setValue(mc->getCalcAmount()); init(mc->getCalcAmount()); } void MatCalcDialog::init(double amount) { Einheit e = m_mc->getMaterial()->unit(); _matWidget->matLabel->setText( m_mc->getMaterial()->getText()); _matWidget->einheitLabel->setText( e.einheit(amount) ); } void MatCalcDialog::accept() { double val = _matWidget->m_inpMenge->value(); m_mc->setCalcAmount(val); emit( matCalcPartChanged(m_mc)); CalcDialogBase::accept(); } MatCalcDialog::~MatCalcDialog( ) { } /* END */ kraft-0.97/src/matcalcdialog.h000066400000000000000000000030401410616450300163010ustar00rootroot00000000000000/*************************************************************************** matcalcdialog - ------------------- begin : 2005-03-00 copyright : (C) 2005 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef _MATCALCDIALOG_H #define _MATCALCDIALOG_H // include files #include #include "ui_matpartui.h" #include "calcdialogbase.h" class StockMaterial; class MaterialCalcPart; /** * */ class MatCalcDialog : public CalcDialogBase { Q_OBJECT public: MatCalcDialog(MaterialCalcPart *mc, QWidget *parent=0); virtual ~MatCalcDialog(); protected slots: void accept(); signals: void matCalcPartChanged(MaterialCalcPart *mc); private: void init(double); Ui::calcdetailMat *_matWidget; MaterialCalcPart *m_mc; }; #endif /* END */ kraft-0.97/src/materialcalcpart.cpp000066400000000000000000000055171410616450300173730ustar00rootroot00000000000000/*************************************************************************** materialcalcpart - ------------------- begin : 2004-09-05 copyright : (C) 2004 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ // include files for Qt #include // include files for KDE #include #include "materialcalcpart.h" #include "materialkatalogview.h" #include "katalogman.h" #include "stockmaterial.h" #include "unitmanager.h" #include "matkatalog.h" MaterialCalcPart::MaterialCalcPart() : CalcPart(), m_calcID( 0 ) { } MaterialCalcPart::MaterialCalcPart( long mCalcID, long matID, int percent, double amount ) : CalcPart( percent), m_calcID( mCalcID ), m_calcAmount(amount), m_mat(nullptr) { getMatFromID(matID); // overwrites m_mat if( m_mat ) { setName(m_mat->getText()); } } MaterialCalcPart::MaterialCalcPart(long matID, int percent, double amount) : CalcPart( percent), m_calcID(0), m_calcAmount(amount) { getMatFromID(matID); if( m_mat ) { setName(m_mat->getText()); } } MaterialCalcPart::~MaterialCalcPart( ) { // do not delete m_mat because it comes straight from stockmaterialman } void MaterialCalcPart::getMatFromID(long matID) { MatKatalog *k = static_cast(KatalogMan::self()->getKatalog( MaterialKatalogView::MaterialCatalogName )); if( !k ) { k = new MatKatalog( MaterialKatalogView::MaterialCatalogName ); if( k ) { KatalogMan::self()->registerKatalog(k); } } if( k ) { m_mat = k->materialFromId(matID); } } QString MaterialCalcPart::getType() const { return KALKPART_MATERIAL; } Geld MaterialCalcPart::basisKosten() { Geld g; if( m_mat ) { g = m_mat->unitPrice() * m_calcAmount; } return g; } StockMaterial * MaterialCalcPart::getMaterial() { return m_mat; } bool MaterialCalcPart::setCalcAmount( double newAmount ) { // Check for a change first if( qAbs(newAmount - m_calcAmount) > 0.00001 ) { m_calcAmount = newAmount; setDirty(true); } return isDirty(); } /* END */ kraft-0.97/src/materialcalcpart.h000066400000000000000000000033621410616450300170340ustar00rootroot00000000000000/*************************************************************************** materialcalcpart - ------------------- begin : 2004-09-05 copyright : (C) 2004 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef _MATERIALCALCPART_H #define _MATERIALCALCPART_H // include files #include #include "calcpart.h" /** * */ class StockMaterial; class StockMaterialList; class QString; class QVariant; class MaterialCalcPart : public CalcPart { public: MaterialCalcPart(); MaterialCalcPart( long mCalcID, long matID, int procent, double amount ); MaterialCalcPart( long matID, int procent, double amount ); ~MaterialCalcPart(); virtual Geld basisKosten(); QString getType() const; StockMaterial* getMaterial(); bool setCalcAmount( double newAmount ); double getCalcAmount(){return m_calcAmount;}; protected: void getMatFromID(long matID); private: long m_calcID; double m_calcAmount; StockMaterial *m_mat; }; #endif /* END */ kraft-0.97/src/materialdialog.ui000066400000000000000000000173201410616450300166670ustar00rootroot00000000000000 MaterialDialogBase 0 0 562 313 Edit Material true Qt::Horizontal QSizePolicy::Expanding 65 16 0 0 Store in C&hapter Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter false mCbChapter Material Qt::Horizontal QSizePolicy::Expanding 50 16 Pac&kaged: false mDiPerPack 99999.000000000000000 1.000000000000000 1.000000000000000 3 per P&ackage false mCbUnit Prices Qt::Horizontal QSizePolicy::Expanding 240 11 = Price of &Sale: false mInSalePrice 999999.000000000000000 1.000000000000000 0 999999.000000000000000 1.000000000000000 0 pl&us false mInSaleAdd % 999.000000000000000 1 &Purchase Price: false mInPurchasePrice mEditMaterial mDiPerPack mCbUnit mInPurchasePrice mInSaleAdd mInSalePrice mCbChapter kraft-0.97/src/materialkataloglistview.cpp000066400000000000000000000113461410616450300210100ustar00rootroot00000000000000/*************************************************************************** materialkataloglistview - template katalog listview. ------------------- begin : 2005-07-26 copyright : (C) 2005 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include #include #include #include #include #include #include "matkatalog.h" #include "stockmaterial.h" #include "materialkataloglistview.h" #include "katalogman.h" #include "docposition.h" #include "kataloglistview.h" #include "kraftsettings.h" MaterialKatalogListView::MaterialKatalogListView(QWidget *parent ) : KatalogListView( parent ) { setColumnCount( 6 ); QStringList headers; headers << i18n("Material"); headers << i18n("Pack"); headers << i18n("Unit"); headers << i18n("Purchase"); headers << i18n("Sale"); headers << i18n("Last Modified"); setHeaderLabels( headers ); QByteArray headerState = QByteArray::fromBase64( KraftSettings::self()->materialCatViewHeader().toAscii() ); header()->restoreState(headerState); contextMenu()->setTitle(i18n("Material Catalog")); } MaterialKatalogListView::~MaterialKatalogListView() { } void MaterialKatalogListView::addCatalogDisplay( const QString& katName ) { KatalogListView::addCatalogDisplay(katName); Katalog *k = KatalogMan::self()->getKatalog( katName ); MatKatalog *catalog = static_cast( k ); if( ! catalog ) { // qDebug () << "No catalog in listview available!" << endl; return; } // qDebug () << "setting up meterial chapters --------*********************************+++!" << endl; setupChapters(); const QList chapters = catalog->getKatalogChapters(); foreach( CatalogChapter theChapter, chapters ) { if( mChapterDict.contains( theChapter.id().toInt() ) ) { QTreeWidgetItem *katItem = mChapterDict[theChapter.id().toInt()]; // hole alle Brunsrecords per Chapter und mach weiter.... StockMaterialList records = catalog->getRecordList( theChapter ); StockMaterialListIterator it( records ); while( it.hasNext() ) { StockMaterial *mat = it.next(); addMaterialToView( katItem, mat ); } } } } QTreeWidgetItem* MaterialKatalogListView::addMaterialToView( QTreeWidgetItem *parent, StockMaterial *mat ) { if ( !mat ) return 0; if ( !parent ) parent = m_root; QTreeWidgetItem *recItem = new QTreeWidgetItem( parent ); Qt::ItemFlags flags; if ( mCheckboxes ) { recItem->setCheckState(0, Qt::Unchecked); } //recItem->setFlags( flags ); recItem->setText( 0, mat->getText() ); // recItem->setMultiLinesEnabled( true ); FIXME slFreshupItem( recItem, mat, catalog()->locale() ); m_dataDict.insert( recItem, mat ); return recItem; } void MaterialKatalogListView::slFreshupItem( QTreeWidgetItem *item, void* templ, QLocale *loc ) { StockMaterial *mat = static_cast( templ ); if ( item && mat ) { Einheit e = mat->unit(); // qDebug () << "Setting material name " << e.einheitSingular() << endl; item->setText( 0, mat->getText() ); item->setText( 1, QString::number( mat->getAmountPerPack() ) ); item->setText( 2, e.einheit( mat->getAmountPerPack() ) ); item->setText( 3, mat->purchPrice().toString() ); item->setText( 4, mat->salesPrice().toString() ); item->setText( 5, mat->lastModified() ); } else { // qDebug () << "Unable to freshup item - data invalid" << endl; } } DocPosition MaterialKatalogListView::itemToDocPosition( QTreeWidgetItem *item ) { DocPosition pos; if ( ! item ) { item = currentItem(); } if ( ! item ) return pos; // FIXME StockMaterial *mat = static_cast( m_dataDict[item] ); if ( mat ) { pos.setText( mat->getText() ); pos.setUnit( mat->unit() ); pos.setUnitPrice( mat->salesPrice() ); } return pos; } void MaterialKatalogListView::saveState() { QByteArray state = this->header()->saveState(); KraftSettings::self()->setMaterialCatViewHeader(state.toBase64()); } kraft-0.97/src/materialkataloglistview.h000066400000000000000000000033041410616450300204500ustar00rootroot00000000000000/*************************************************************************** materialkataloglistview - material katalog listview. ------------------- begin : 2006-11-30 copyright : (C) 2006 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef MATERIALKATALOGLISTVIEW_H #define MATERIALKATALOGLISTVIEW_H #include #include class QTreeWidgetItem; class StockMaterial; class QLocale; /** A listview that presents the contents of the Bruns Catalog @author Klaas Freitag */ class MaterialKatalogListView : public KatalogListView { public: MaterialKatalogListView(QWidget *parent=0 ); ~MaterialKatalogListView(); void addCatalogDisplay( const QString& katName ); DocPosition itemToDocPosition( QTreeWidgetItem *it = 0 ); QTreeWidgetItem* addMaterialToView( QTreeWidgetItem*, StockMaterial* ); void saveState(); public slots: void slFreshupItem( QTreeWidgetItem *, void*, QLocale* = 0 ); }; #endif kraft-0.97/src/materialkatalogview.cpp000066400000000000000000000155561410616450300201230ustar00rootroot00000000000000/*************************************************************************** materialkatalogview.cpp ------------------- begin : 2005-07-26 copyright : (C) 2005 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include #include #include #include #include #include #include "katalogman.h" #include "materialkatalogview.h" #include "materialkataloglistview.h" #include "stockmaterial.h" #include "matkatalog.h" #include "materialtempldialog.h" #include "kraftsettings.h" const QString MaterialKatalogView::MaterialCatalogName( "Material" ); MaterialKatalogView::MaterialKatalogView() : KatalogView(), m_materialListView(nullptr), m_details(nullptr) { } MaterialKatalogView::~MaterialKatalogView() { slotSaveState(); } void MaterialKatalogView::createCentralWidget( QBoxLayout *box, QWidget *w ) { m_materialListView = new MaterialKatalogListView( w ); box->addWidget( m_materialListView ); KatalogView::createCentralWidget( box, w ); } Katalog* MaterialKatalogView::getKatalog( const QString& name ) { // qDebug () << "GetKatalog of material!" << endl; Katalog *k = KatalogMan::self()->getKatalog( name ); if( ! k ) { k = new MatKatalog( name ); KatalogMan::self()->registerKatalog( k ); } return k; } void MaterialKatalogView::slEditTemplate() { MaterialKatalogListView *listview = static_cast(getListView()); if( listview ) { QTreeWidgetItem *item = listview->currentItem(); if( listview->isChapter(item) ) { // check if the chapter is empty. If so, switch to slNewTempalte() // if there others, open the chapter. if( !listview->isRoot( item ) && item->childCount() == 0 ) { slNewTemplate(); } else { // do nothing. } } else { // qDebug () << "Editing the material" << endl; if( listview ) { StockMaterial *currTempl = static_cast ( listview->currentItemData() ); if( currTempl ) { QTreeWidgetItem *item = static_cast(listview->currentItem()); openDialog( item, currTempl, false ); } } } } } void MaterialKatalogView::slNewTemplate() { KatalogListView *listview = getListView(); if( !listview ) return; MaterialKatalogListView *matListView = static_cast(listview); StockMaterial *newMat = new StockMaterial(); newMat->setText( i18n( "" ) ); QTreeWidgetItem *parentItem = static_cast( listview->currentItem() ); if ( parentItem ) { if ( ! ( matListView->isRoot( parentItem ) || matListView->isChapter( parentItem ) ) ) { parentItem = static_cast(parentItem->parent()); } } if( parentItem && listview->isChapter( parentItem )) { // try to find out which catalog is open/current CatalogChapter *chap = static_cast(listview->itemData( parentItem )); newMat->setChapter( chap->id().toInt() ); } mNewItem = matListView->addMaterialToView( parentItem, newMat ); openDialog( mNewItem, newMat, true ); } void MaterialKatalogView::slDeleteTemplate() { // qDebug () << "delete template hit"; MaterialKatalogListView* listview = static_cast(getListView()); if( listview ) { StockMaterial *currTempl = static_cast (listview->currentItemData()); if( currTempl ) { int id = currTempl->getID(); QMessageBox msgBox; msgBox.setText(i18n( "Do you really want to delete the template from the catalog?")); msgBox.setStandardButtons(QMessageBox::Yes| QMessageBox::No); msgBox.setDefaultButton(QMessageBox::Yes); int ret = msgBox.exec(); if ( ret == QMessageBox::Yes) { // qDebug () << "Delete item with id " << id; MatKatalog *k = static_cast( getKatalog( m_katalogName ) ); if( k ) { k->deleteMaterial( id ); listview->removeTemplateItem( listview->currentItem()); } } } } } void MaterialKatalogView::openDialog( QTreeWidgetItem *listitem, StockMaterial *tmpl, bool isNew ) { mDialog = new MaterialTemplDialog( this ); mNewItem = listitem; listitem->setSelected( true ); // listitem->ensureItemVisible( true ); connect( mDialog, SIGNAL( editAccepted( StockMaterial* ) ), this, SLOT( slotEditOk( StockMaterial* ) ) ); connect( mDialog, SIGNAL( editRejected( ) ), this, SLOT( slotEditRejected() ) ); mDialog->setMaterial( tmpl, MaterialCatalogName, isNew ); mDialog->show(); } void MaterialKatalogView::slotEditRejected() { if ( mNewItem ) { delete mNewItem; mNewItem = nullptr; } } void MaterialKatalogView::slotEditOk( StockMaterial *mat ) { KatalogListView *listview = getListView(); if( !listview ) return; MaterialKatalogListView *templListView = static_cast(listview); // qDebug () << "****** slotEditOk for Material" << endl; if( mDialog ) { MatKatalog *k = static_cast( getKatalog( MaterialCatalogName ) ); if ( mDialog->templateIsNew() ) { QLocale *locale = nullptr; if ( k ) { k->addNewMaterial( mat ); locale = k->locale(); } if( mNewItem ) { mNewItem->setSelected( true ); templListView->slFreshupItem( mNewItem, mat, locale ); // templListView->ensureItemVisible( mNewItem ); } } } mNewItem = nullptr; } void MaterialKatalogView::saveWindowState( const QByteArray& arr ) { KraftSettings::self()->setMaterialCatViewState(arr); KraftSettings::self()->save(); } QByteArray MaterialKatalogView::windowState() { const QByteArray re = QByteArray::fromBase64( KraftSettings::self()->materialCatViewState().toAscii() ); return re; } void MaterialKatalogView::saveWindowGeo( const QByteArray& arr ) { KraftSettings::self()->setMaterialCatViewGeo(arr); KraftSettings::self()->save(); } QByteArray MaterialKatalogView::windowGeo() { const QByteArray re = QByteArray::fromBase64( KraftSettings::self()->materialCatViewGeo().toAscii() ); return re; } kraft-0.97/src/materialkatalogview.h000066400000000000000000000041771410616450300175650ustar00rootroot00000000000000/*************************************************************************** materialkatalogview.h ------------------- begin : 2006-11-30 copyright : (C) 2006 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef MATERIALKATALOGVIEW_H #define MATERIALKATALOGVIEW_H #include #include "materialkataloglistview.h" class QBoxLayout; class QTreeWidgetItem; class BrunsKatalogListView; class QLabel; class MaterialTemplDialog; /** @author Klaas Freitag */ class MaterialKatalogView : public KatalogView { Q_OBJECT public: MaterialKatalogView(); ~MaterialKatalogView(); void createCentralWidget(QBoxLayout*, QWidget *w); KatalogListView* getListView() { return m_materialListView; } static const QString MaterialCatalogName; protected slots: void slNewTemplate(); void slEditTemplate(); void slDeleteTemplate(); void openDialog( QTreeWidgetItem *, StockMaterial *, bool ); void slotEditRejected(); void slotEditOk( StockMaterial * ); protected: Katalog* getKatalog( const QString& ); void saveWindowState( const QByteArray& arr ); QByteArray windowState(); void saveWindowGeo( const QByteArray& arr ); QByteArray windowGeo(); MaterialKatalogListView *m_materialListView; QLabel *m_detailLabel; QTreeWidget *m_details; MaterialTemplDialog *mDialog; QTreeWidgetItem *mNewItem; }; #endif kraft-0.97/src/materialsaverbase.cpp000066400000000000000000000022471410616450300175520ustar00rootroot00000000000000/*************************************************************************** templatesaverbase - ------------------- begin : 2005-20-01 copyright : (C) 2005 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ // include files for Qt // include files for KDE #include "materialsaverbase.h" MaterialSaverBase::MaterialSaverBase( ) :QObject() { } MaterialSaverBase::~MaterialSaverBase( ) { } /* END */ kraft-0.97/src/materialsaverbase.h000066400000000000000000000025761410616450300172240ustar00rootroot00000000000000/*************************************************************************** materialsaverbase - Base class of a material saver ------------------- begin : 2006-12-07 copyright : (C) 2006 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef _MATERIALSAVERBASE_H #define _MATERIALSAVERBASE_H // include files #include /** * */ class StockMaterial; class MaterialSaverBase : public QObject { Q_OBJECT public: MaterialSaverBase(); ~MaterialSaverBase(); virtual bool saveTemplate( StockMaterial* ) = 0; virtual void saveTemplateChapter( StockMaterial* ) = 0; }; #endif /* END */ kraft-0.97/src/materialsaverdb.cpp000066400000000000000000000077761410616450300172410ustar00rootroot00000000000000/*************************************************************************** materialsaverdb - ------------------- begin : 2006-12-07 copyright : (C) 2005 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ // include files for Qt #include #include #include #include #include #include "kraftdb.h" #include "kraftglobals.h" #include "dbids.h" #include "materialsaverdb.h" #include "stockmaterial.h" MaterialSaverDB::MaterialSaverDB( ) : MaterialSaverBase() { } Q_GLOBAL_STATIC(MaterialSaverDB, mSelf) MaterialSaverBase *MaterialSaverDB::self() { return mSelf; } bool MaterialSaverDB::saveTemplate( StockMaterial *mat ) { bool res = true; // Transaktion ? QSqlTableModel model; model.setTable("stockMaterial"); QString templID = QString::number( mat->getID() ); model.setFilter( "matID=" + templID ); model.select(); QSqlRecord buffer = model.record(); if( model.rowCount() > 0) { // qDebug () << "Updating material " << mat->getID() << endl; // mach update buffer = model.record(0); fillMaterialBuffer( buffer, mat, false ); model.setRecord(0, buffer); model.submitAll(); } else { // insert // qDebug () << "Creating new material database entry" << endl; fillMaterialBuffer( buffer, mat, true ); model.insertRecord(-1, buffer); model.submitAll(); /* Jetzt die neue Template-ID selecten */ dbID id = KraftDB::self()->getLastInsertID(); // qDebug () << "New Database ID=" << id.toInt() << endl; if( id.isOk() ) { mat->setID( id.toInt() ); templID = id.toString(); } else { // qDebug () << "ERROR: Kann AUTOINC nicht ermitteln" << endl; res = false; } } return res; } void MaterialSaverDB::fillMaterialBuffer( QSqlRecord &rec, StockMaterial *mat, bool isNew ) { if( ! ( mat ) ) return; rec.setValue( "chapterID", mat->chapter() ); rec.setValue( "material", mat->getText() ); rec.setValue( "unitID", mat->unit().id() ); rec.setValue( "perPack", mat->getAmountPerPack() ); rec.setValue( "priceIn", mat->purchPrice().toDouble() ); rec.setValue( "priceOut", mat->salesPrice().toDouble() ); QString dtString = KraftDB::self()->currentTimeStamp(); if( isNew ) { rec.setValue( "enterDate", dtString); } rec.setValue("modifyDate", dtString ); } void MaterialSaverDB::saveTemplateChapter( StockMaterial* tmpl ) { if( ! tmpl ) { // qDebug () << "Parameter error, zero material!"; return; } dbID id = tmpl->getID(); dbID chapId = tmpl->chapterId(); QSqlTableModel model; model.setTable("stockMaterial"); QString templID = id.toString(); model.setFilter( "matID=" + templID ); model.select(); QSqlRecord buffer = model.record(); if( model.rowCount() > 0) { // qDebug () << "Updating material chapter " << templID << endl; buffer = model.record(0); buffer.setValue( "chapterID", chapId.toString() ); model.setRecord(0, buffer); model.submitAll(); } else { // qDebug () << "Could not update material chapter, not found with id " << templID; } } kraft-0.97/src/materialsaverdb.h000066400000000000000000000027071410616450300166730ustar00rootroot00000000000000/*************************************************************************** materialsaverdb - ------------------- begin : 2006-12-07 copyright : (C) 2005 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef _MATERIALSAVERDB_H #define _MATERIALSAVERDB_H // include files #include "materialsaverbase.h" /** * */ class QSqlRecord; class StockMaterial; class MaterialSaverDB : public MaterialSaverBase { public: static MaterialSaverBase* self(); MaterialSaverDB(); private: virtual bool saveTemplate( StockMaterial* ); virtual void fillMaterialBuffer( QSqlRecord &, StockMaterial* , bool ); virtual void saveTemplateChapter( StockMaterial* ); }; #endif /* END */ kraft-0.97/src/materialselectdialog.cpp000066400000000000000000000055661410616450300202450ustar00rootroot00000000000000/*************************************************************************** materialselectdialog - select material for inserting into template ------------------- begin : 2006-12-17 copyright : (C) 2006 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include #include #include #include #include #include #include #include "materialkatalogview.h" #include "materialselectdialog.h" #include "materialkataloglistview.h" #include "katalogman.h" #include "matkatalog.h" #include "katalog.h" #include "filterheader.h" MaterialSelectDialog::MaterialSelectDialog( QWidget *parent) : CalcDialogBase( parent ) { setWindowTitle( i18n("Add Material to Calculation" ) ); QVBoxLayout *mainLayout = new QVBoxLayout; _centralWidget->setLayout(mainLayout); QLabel *label = new QLabel( i18n( "

            Add Material to Calculation

            " )); mainLayout->addWidget(label); mKatalogListView = new MaterialKatalogListView; FilterHeader *filter = new FilterHeader(this, mKatalogListView); mainLayout->addWidget(filter); mainLayout->addWidget(mKatalogListView); mKatalogListView->setCheckboxes( true ); Katalog *kat = KatalogMan::self()->getKatalog( MaterialKatalogView::MaterialCatalogName ); if ( ! kat ) { kat = new MatKatalog( MaterialKatalogView::MaterialCatalogName ); KatalogMan::self()->registerKatalog( kat ); } mKatalogListView->addCatalogDisplay( MaterialKatalogView::MaterialCatalogName ); } MaterialSelectDialog::~MaterialSelectDialog() { } void MaterialSelectDialog::accept() { // qDebug () << "++ Material selected!" << endl; QTreeWidgetItemIterator it( mKatalogListView, QTreeWidgetItemIterator::Checked ); while (*it) { // qDebug () << "T: " << (*it)->text( 0 ) << endl; QTreeWidgetItem *item = *it; if( !( mKatalogListView->isChapter( item ) || mKatalogListView->isRoot( item ))) { StockMaterial *mat = static_cast( mKatalogListView->itemData( item ) ); if ( mat ) { emit materialSelected( mat->getID(), 1 ); } } ++it; } QDialog::accept(); } kraft-0.97/src/materialselectdialog.h000066400000000000000000000032421410616450300176770ustar00rootroot00000000000000/*************************************************************************** materialselectdialog - select material for inserting into template ------------------- begin : 2006-12-17 copyright : (C) 2006 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef _MATERIALSELECTDIALOG_H #define _MATERIALSELECTDIALOG_H #include "calcdialogbase.h" class MaterialKatalogListView; /* ******************************************************************************** * Editor that shows the MaterialKatalogListView * ********************************************************************************/ class MaterialSelectDialog : public CalcDialogBase { Q_OBJECT public: MaterialSelectDialog(QWidget * parent = 0); ~MaterialSelectDialog(); public slots: protected slots: void accept(); signals: void materialSelected( int, double ); private: MaterialKatalogListView *mKatalogListView; }; #endif /* END */ kraft-0.97/src/materialtempldialog.cpp000066400000000000000000000163051410616450300201000ustar00rootroot00000000000000/*************************************************************************** materialtempldialog - dialog to edit material templates ------------------- begin : 2006-12-03 copyright : (C) 2006 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ // include files for Qt #include #include // include files for KDE #include #include #include #include #include #include "materialtempldialog.h" #include "katalogman.h" #include "unitmanager.h" #include "geld.h" #include "kraftsettings.h" #include "defaultprovider.h" MaterialTemplDialog::MaterialTemplDialog( QWidget *parent, bool modal ) : QDialog( parent ), Ui::MaterialDialogBase(), Eta( 0.00000000001 ) { /* connect a value Changed signal of the manual price field */ QWidget *w = new QWidget(this); QVBoxLayout *mainLayout = new QVBoxLayout; setLayout(mainLayout); mainLayout->addWidget(w); setupUi( w ); setModal( modal ); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); mainLayout->addWidget(buttonBox); buttonBox->button(QDialogButtonBox::Ok)->setDefault(true); const QString currSymbol = DefaultProvider::self()->locale()->currencySymbol(); mInPurchasePrice->setPrefix( currSymbol + " " ); mInPurchasePrice->setMinimum( -999999.99 ); mInPurchasePrice->setMaximum( 999999.99 ); mInPurchasePrice->setDecimals( 2 ); mInSalePrice->setPrefix( currSymbol + " " ); mInSalePrice->setMinimum( -999999.99 ); mInSalePrice->setMaximum( 999999.99 ); mInSalePrice->setDecimals( 2 ); double m = KraftSettings::self()->materialAddOnPercent(); mInSaleAdd->setValue(m); mInSaleAdd->setMinimum(-99.0); mInSaleAdd->setMaximum(999.0); connect( mInSalePrice, SIGNAL( valueChanged( double ) ), SLOT( slSalePriceChanged( double ) ) ); connect( mInPurchasePrice, SIGNAL( valueChanged( double ) ), SLOT( slPurchPriceChanged( double ) ) ); connect( mInSaleAdd, SIGNAL( valueChanged( double ) ), SLOT( slSaleAddChanged( double ) ) ); } void MaterialTemplDialog::slSalePriceChanged( double sale ) { // change the percent val double purch = mInPurchasePrice->value(); double m = mInSaleAdd->value(); if ( m > Eta && purch < Eta ) { // recalc the purchase price purch = sale/( 1+ m / 100.0 ); } else if ( purch > Eta ) { // recalc the add-percentage m = 100 * ( ( sale-purch ) / purch ); } setPriceCalc( purch, m, sale ); } void MaterialTemplDialog::slPurchPriceChanged( double purch ) { // change the percent val double sale = mInSalePrice->value(); double m = mInSaleAdd->value(); if ( m > Eta ) { sale = ( 1+m/100 )*purch; } setPriceCalc( purch, m, sale ); } void MaterialTemplDialog::slSaleAddChanged( double m ) { // change the Sales Price double sale = mInSalePrice->value(); double purch = mInPurchasePrice->value(); if (purch < Eta && sale > Eta ) { // calc the purchase price purch = sale/( 1+ m/100 ); } else { sale = ( 1+ m/100.0 ) * purch; } setPriceCalc( purch, m, sale ); } void MaterialTemplDialog::setPriceCalc( double purch, double addPercent, double sale ) { mInPurchasePrice->blockSignals(true); mInSalePrice->blockSignals(true); mInSaleAdd->blockSignals(true); mInPurchasePrice->setValue( purch ); mInSalePrice->setValue( sale ); mInSaleAdd->setValue( addPercent ); mInPurchasePrice->blockSignals(false); mInSalePrice->blockSignals(false); mInSaleAdd->blockSignals(false); } void MaterialTemplDialog::setMaterial( StockMaterial *t, const QString& katalogname, bool newTempl ) { if( ! t ) return; mSaveMaterial = t; m_templateIsNew = newTempl; m_katalog = KatalogMan::self()->getKatalog(katalogname); if( m_katalog == 0 ) { // qDebug () << "ERR: Floskel Dialog called without valid Katalog!" << endl; return; } // chapter settings QStringList chapterNames; foreach( CatalogChapter chap, m_katalog->getKatalogChapters() ) { chapterNames.append( chap.name() ); } mCbChapter->insertItems(-1, chapterNames ); mChapId = t->chapter(); QString chap = m_katalog->chapterName(dbID( mChapId )); mCbChapter->setCurrentIndex(mCbChapter->findText( chap )); mCbChapter->setEnabled( false ); // unit settings mCbUnit->insertItems(-1, UnitManager::self()->allUnits() ); Einheit e = t->unit(); mCbUnit->setCurrentIndex(mCbUnit->findText( e.einheitSingular() )); // text mEditMaterial->setText( t->getText() ); double priceIn = t->purchPrice().toDouble(); double priceOut = t->salesPrice().toDouble(); mInPurchasePrice->setValue( priceIn ); mInSalePrice->setValue( priceOut ); mDiPerPack->setValue( t->getAmountPerPack() ); // user experience mEditMaterial->setFocus(); mEditMaterial->selectAll(); // percent add on sale price double percent = 10.0; if( priceIn > Eta ) { double diff = priceOut - priceIn; percent = diff / priceIn * 100.0; } mInSaleAdd->setValue( percent ); } MaterialTemplDialog::~MaterialTemplDialog( ) { } void MaterialTemplDialog::accept() { // qDebug () << "*** Saving finished " << endl; const QString newMat = mEditMaterial->toPlainText(); if ( newMat.isEmpty() ) { // qDebug () << "We do not want to store empty materials" << endl; } else { mSaveMaterial->setText( mEditMaterial->toPlainText() ); mSaveMaterial->setAmountPerPack( mDiPerPack->value() ); const QString str = mCbUnit->currentText(); int u = UnitManager::self()->getUnitIDSingular( str ); // qDebug () << "Setting unit id " << u << endl; mSaveMaterial->setUnitId( u ); // chapId = 0; // FIXME: get a chapter catalog Id of hirarchical mSaveMaterial->setChapter( mChapId ); double db = mInPurchasePrice->value(); mSaveMaterial->setPurchPrice( Geld( db ) ); db = mInSalePrice->value(); mSaveMaterial->setSalesPrice( Geld( db ) ); mSaveMaterial->save(); emit editAccepted( mSaveMaterial ); KatalogMan::self()->notifyKatalogChange( m_katalog, mSaveMaterial->getID() ); } QDialog::accept(); } void MaterialTemplDialog::reject() { if ( m_templateIsNew ) { // remove the listview item if it was created newly emit editRejected(); } QDialog::reject(); } kraft-0.97/src/materialtempldialog.h000066400000000000000000000037201410616450300175420ustar00rootroot00000000000000/*************************************************************************** materialtempldialog - ------------------- begin : 2006-12-04 copyright : (C) 2006 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef _MATERIALTEMPLDIALOG_H #define _MATERIALTEMPLDIALOG_H // include files #include "kraftglobals.h" #include "ui_materialdialog.h" #include "stockmaterial.h" /** * */ class Katalog; class MaterialTemplDialog : public QDialog, protected Ui::MaterialDialogBase { Q_OBJECT public: MaterialTemplDialog( QWidget *parent=0, bool modal=false ); ~MaterialTemplDialog(); void setMaterial( StockMaterial* t, const QString&, bool ); bool templateIsNew() { return m_templateIsNew; }; signals: void editAccepted( StockMaterial* ); void editRejected(); void chapterChanged(int); public slots: protected slots: virtual void accept(); virtual void reject(); void slSalePriceChanged( double ); void slPurchPriceChanged( double ); void slSaleAddChanged( double ); private: void setPriceCalc( double, double, double ); StockMaterial *mSaveMaterial; bool m_templateIsNew; Katalog *m_katalog; const double Eta; int mChapId; }; #endif /* END */ kraft-0.97/src/matkatalog.cpp000066400000000000000000000072451410616450300162070ustar00rootroot00000000000000/*************************************************************************** matkatalog - the material catalog ------------------- begin : 2004-19-10 copyright : (C) 2004 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ // include files for Qt #include // include files for KDE #include #include "matkatalog.h" #include "kraftdb.h" MatKatalog::MatKatalog( const QString& name) : Katalog(name) { } MatKatalog::MatKatalog() : Katalog( QLatin1String( "Material" )) { } void MatKatalog::reload( dbID ) { mAllMaterial.clear(); load(); } int MatKatalog::load() { Katalog::load(); int cnt = 0; QSqlQuery q(QLatin1String("SELECT matID, chapterID, material, unitID, perPack, priceIn, " "priceOut, modifyDate, enterDate FROM stockMaterial")); q.exec(); while ( q.next() ) { cnt++; int id = q.value( 0 ).toInt(); int chapterID = q.value( 1 ).toInt(); const QString material = q.value( 2 ).toString(); int unitID = q.value( 3 ).toInt(); double pPack = q.value( 4 ).toDouble(); double priceIn = q.value( 5 ).toDouble(); double priceOut = q.value(6 ).toDouble(); QDate lastMod = q.value( 7 ).toDate(); QDate entered = q.value( 8 ).toDate(); StockMaterial *mat = new StockMaterial( id, chapterID, material, unitID, pPack, Geld( priceIn ), Geld( priceOut ) ); mat->setEnterDate( entered ); mat->setLastModified( lastMod ); mAllMaterial.append( mat ); } return cnt; } void MatKatalog::recordUsage(int id) { Q_UNUSED(id); // FIXME: Implement this! } void MatKatalog::deleteMaterial( int id ) { StockMaterialListIterator it( mAllMaterial ); int cnt = 0; // qDebug () << "Deleting material id=" << id; while( it.hasNext() ) { StockMaterial *mat = it.next(); if( mat->getID() == id ) { break; } cnt++; } if( cnt < mAllMaterial.count() ) { mAllMaterial.removeAt( cnt ); } // remove from database. QSqlQuery q; q.prepare( QLatin1String("DELETE FROM stockMaterial WHERE matID=:Id")); q.bindValue( ":Id", id ); q.exec(); // qDebug () << "SQL Delete Success: " << q.lastError().text(); } StockMaterialList MatKatalog::getRecordList( const CatalogChapter& chapter ) { StockMaterialList list; int chapID = chapter.id().toInt(); StockMaterialListIterator it( mAllMaterial ); while( it.hasNext() ) { StockMaterial *mat = it.next(); if ( mat->chapter() == chapID ) { list.append( mat ); } } return list; } StockMaterial* MatKatalog::materialFromId( long id ) { StockMaterialListIterator it( mAllMaterial ); while( it.hasNext() ) { StockMaterial *mat = it.next(); if ( mat->getID() == id ) { return mat; } } return nullptr; } void MatKatalog::addNewMaterial( StockMaterial *mat ) { mAllMaterial.append( mat ); } MatKatalog::~MatKatalog( ) { } /* END */ kraft-0.97/src/matkatalog.h000066400000000000000000000033121410616450300156430ustar00rootroot00000000000000/*************************************************************************** matkatalog - Materialkatalogklasse ------------------- begin : 2004-19-10 copyright : (C) 2004 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef _MATKATALOG_H #define _MATKATALOG_H // include files #include #include "stockmaterial.h" #include "katalog.h" /** * */ class MatKatalog : public Katalog { public: MatKatalog( const QString& name ); MatKatalog(); ~MatKatalog(); int getEntriesPerChapter( const CatalogChapter& ) override { return 0; } // FIXME int load() override; void reload( dbID ) override; void deleteMaterial( int ); StockMaterial *materialFromId(long id); KatalogType type() override { return MaterialCatalog; } StockMaterialList getRecordList( const CatalogChapter& ); void addNewMaterial( StockMaterial* ); void recordUsage(int) override; private: StockMaterialList mAllMaterial; }; #endif /* END */ kraft-0.97/src/matpartui.ui000066400000000000000000000076571410616450300157330ustar00rootroot00000000000000 calcdetailMat 0 0 435 228 Calculation Item Material <h1>Calculation Part 'Material'</h1> false Add Material to the template calculation. Qt::AlignVCenter true QFrame::StyledPanel QFrame::Raised 0 0 material Qt::AlignVCenter true &Amount: false m_inpMenge Price for one piece 1.000000000000000 99999.000000000000000 unit false Qt::Horizontal QSizePolicy::Expanding 61 20 QDoubleSpinBox QWidget kraft-0.97/src/matpartui.ui.h000066400000000000000000000011161410616450300161410ustar00rootroot00000000000000/**************************************************************************** ** ui.h extension file, included from the uic-generated form implementation. ** ** If you want to add, delete, or rename functions or slots, use ** Qt Designer to update this file, preserving your code. ** ** You should not define a constructor or destructor in this file. ** Instead, write your code in functions called init() and destroy(). ** These will automatically be called by the form's constructor and ** destructor. *****************************************************************************/ kraft-0.97/src/metaxmlparser.cpp000066400000000000000000000064241410616450300167450ustar00rootroot00000000000000/*************************************************************************** metaxmlparser.cpp - Parser for Meta XML files ------------------- begin : Dec 29 2018 copyright : (C) 2018 by Klaas Freitag email : kraft@freisturz.de ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include #include #include #include #include #include #include #include "metaxmlparser.h" MetaXMLParser::MetaXMLParser() { } bool MetaXMLParser::parse( QIODevice *device ) { _docTypeAddList.clear(); if (!_domDocument.setContent(device, true, &_errorString, &_errorLine, &_errorColumn)) { qDebug() << "Not able to parse XML: " << _errorString << "Line"<< _errorLine << ", Colum" << _errorColumn; return false; } QDomElement root = _domDocument.documentElement(); if( root.tagName() != "kraftmeta" ) { qDebug() << "XML file parse error: Not a kraftmeta root"; } QDomElement migrateElem = root.firstChildElement("migrate"); QDomElement addDtXml = migrateElem.firstChildElement("doctype"); while( ! addDtXml.isNull() ) { MetaDocTypeAdd docTypeAdd; QDomElement elem = addDtXml.firstChildElement("name"); if( !elem.isNull() ) { docTypeAdd.setName(elem.text()); } elem = addDtXml.firstChildElement("numbercycle"); if( !elem.isNull() ) { docTypeAdd.setNumbercycle(elem.text()); } elem = addDtXml.firstChildElement("lang"); if( !elem.isNull() ) { docTypeAdd.setLang(elem.text()); } elem = addDtXml.firstChildElement("attrib"); while( !elem.isNull() ) { const QString key = elem.firstChildElement("key").text(); const QString val = elem.firstChildElement("value").text(); if( !key.isEmpty() ) docTypeAdd._attribs.insert(key, val); elem = elem.nextSiblingElement("attrib"); } elem = addDtXml.firstChildElement("follower"); while( !elem.isNull() ) { const QString fo = elem.text(); if( !fo.isEmpty() ) docTypeAdd._follower.append(fo); elem = elem.nextSiblingElement("follower"); } // save and move on to next element. if( !docTypeAdd.name().isNull() ) { _docTypeAddList.append(docTypeAdd); } addDtXml = addDtXml.nextSiblingElement("doctype"); } return true; } QList MetaXMLParser::metaDocTypeAddList() { return _docTypeAddList; } kraft-0.97/src/metaxmlparser.h000066400000000000000000000036271410616450300164140ustar00rootroot00000000000000/*************************************************************************** metaxmlparser.cpp - Parser for Meta XML files ------------------- begin : Dec 29 2018 copyright : (C) 2018 by Klaas Freitag email : kraft@freisturz.de ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef METAXMLPARSER_H #define METAXMLPARSER_H #include #include #include class MetaDocTypeAdd { public: void setName( const QString& name ) { _name = name; } QString name() const { return _name; } void setNumbercycle( const QString& name ) { _numbercycle = name; } QString numbercycle() const { return _numbercycle; } void setLang( const QString& name ) { _lang = name; } QString lang() const { return _lang; } QMap _attribs; QStringList _follower; private: QString _name; QString _lang; QString _numbercycle; }; class MetaXMLParser { public: MetaXMLParser(); bool parse( QIODevice *device ); QList metaDocTypeAddList(); private: QDomDocument _domDocument; QString _errorString; int _errorLine, _errorColumn; QList _docTypeAddList; }; #endif // METAXMLPARSER_H kraft-0.97/src/models/000077500000000000000000000000001410616450300146325ustar00rootroot00000000000000kraft-0.97/src/models/datemodel.cpp000066400000000000000000000261331410616450300173010ustar00rootroot00000000000000/*************************************************************************** datemodel.cpp ------------------- copyright : (C) 2017 by Klaas Freitag email : klaas@volle-kraft-voraus.de ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include "datemodel.h" #include "docdigest.h" #include #include #include #include class TreeItem { public: TreeItem(AbstractIndx *indx, TreeItem *parent = 0); ~TreeItem(); void appendChild(TreeItem *child); TreeItem *child(int row); int childCount() const; QVariant data(int column) const; int row() const; TreeItem *parent(); QList children() { return childItems; } AbstractIndx *payload() { return dataPtr; } private: QList childItems; AbstractIndx *dataPtr; TreeItem *parentItem; }; TreeItem::TreeItem(AbstractIndx *indx, TreeItem *parent) :dataPtr(indx), parentItem(parent) { // make sure the parents have the children registered if( parent ) { parent->appendChild(this); } } TreeItem::~TreeItem() { foreach( TreeItem *i, childItems ) { delete i; } } TreeItem* TreeItem::child(int row) { return childItems.value(row); } TreeItem* TreeItem::parent() { return parentItem; } int TreeItem::row() const { if (parentItem) return parentItem->childItems.indexOf(const_cast(this)); return 0; } int TreeItem::childCount() const { return childItems.count(); } void TreeItem::appendChild(TreeItem *child) { childItems.append(child); } /* ================================================================== */ AbstractIndx::AbstractIndx() :_type(Invalid) { } AbstractIndx::AbstractIndx(IndxType t) :_type(t) { } AbstractIndx::AbstractIndx(IndxType t, DocDigest(digest)) :_docDigest(digest), _type(t) { } AbstractIndx::IndxType AbstractIndx::type() { return _type; } DocDigest AbstractIndx::digest() const { return _docDigest; } int AbstractIndx::year() { return _docDigest.rawDate().year(); } int AbstractIndx::month() { return _docDigest.rawDate().month(); } /* ================================================================== */ class YearIndx : public AbstractIndx { public: explicit YearIndx(int year) :AbstractIndx(IndxType::YearType) { QDate d (year, 1, 1); _docDigest.setDate(d); } }; /* ================================================================== */ class MonthIndx : public AbstractIndx { public: explicit MonthIndx(int year, int month) :AbstractIndx(IndxType::MonthType) { QDate d(year, month, 1); _docDigest.setDate(d); } }; /* ================================================================== */ DateModel::DateModel(QObject *parent) :DocBaseModel(parent) { rootItem = new TreeItem(0); } QVariant DateModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); TreeItem *item = static_cast(index.internalPointer()); AbstractIndx *indx = item->payload(); #if 0 if( role == Qt::BackgroundColorRole ) { if( indx->type() == AbstractIndx::YearType ) { return QColor(0x80, 0xC8, 0xFE); } else if( indx->type() == AbstractIndx::MonthType ) { return QColor(0xBF, 0xE3, 0xFE); } return QVariant(); } #endif if( role == Qt::FontRole ) { QFont f; if( indx->type() == AbstractIndx::YearType ) { f.setPointSize(22); return f; } else if( indx->type() == AbstractIndx::MonthType ) { f.setPointSize(16); return f; } } if (role != Qt::DisplayRole) return QVariant(); int col = index.column(); if( indx->type() == AbstractIndx::YearType ) { if( col == 0 ) { return item->payload()->year(); } else if(col == Treestruct_Year) { return item->payload()->year(); } else if(col == Treestruct_Type) { return AbstractIndx::YearType; } #if 0 QList monthItems = item->children(); if( _yearExtra[col] == Sum ) { float sum = 0.0; foreach( TreeItem *month, monthItems) { foreach( TreeItem *docItem, month->children()) { sum += docItem->payload()->data(col).toFloat(); } } return sum; } else if( _monthExtra[col] == Count ) { int cnt = 0; foreach( TreeItem *month, monthItems) { cnt += month->children().count(); } return cnt; } else { return item->payload()->data(col); } #endif } if( indx->type() == AbstractIndx::MonthType ) { // there might be a special column type if( col == 0 ) { return QDate::shortMonthName(item->payload()->month()); } else if(col == Treestruct_Month) { return item->payload()->month(); } else if(col == Treestruct_Year) { return item->payload()->year(); } else if(col == Treestruct_Type) { return AbstractIndx::MonthType; } #if 0 QList childitems = item->children(); if( _monthExtra[col] == Sum ) { float sum = 0.0; foreach( TreeItem *child, childitems) { sum += child->payload()->data(col).toFloat(); } return sum; } else if( _monthExtra[col] == Count ) { return childitems.count(); } else { } #endif } if( indx->type() == AbstractIndx::DocumentType ) { DocDigest digest = item->payload()->digest(); if( index.column() == Document_Id ) { // In column zero the day has to be displayed here const QDate date = digest.rawDate(); return date.day(); } else { return columnValueFromDigest( digest, index.column() ); } } return QVariant(); } Qt::ItemFlags DateModel::flags(const QModelIndex &index) const { if (!index.isValid()) return 0; return Qt::ItemIsEnabled | Qt::ItemIsSelectable; } QModelIndex DateModel::index(int row, int column, const QModelIndex &parent) const { if (!hasIndex(row, column, parent)) return QModelIndex(); TreeItem *parentItem; if (!parent.isValid()) parentItem = rootItem; else parentItem = static_cast(parent.internalPointer()); TreeItem *childItem = parentItem->child(row); if (childItem) return createIndex(row, column, childItem); else return QModelIndex(); } QModelIndex DateModel::parent(const QModelIndex &index) const { if (!index.isValid()) return QModelIndex(); TreeItem *childItem = static_cast(index.internalPointer()); TreeItem *parentItem = childItem->parent(); if (parentItem == rootItem) return QModelIndex(); return createIndex(parentItem->row(), 0, parentItem); } int DateModel::rowCount(const QModelIndex &parent) const { TreeItem *parentItem; if (parent.column() > 0) return 0; if (!parent.isValid()) parentItem = rootItem; else parentItem = static_cast(parent.internalPointer()); return parentItem->childCount(); } void DateModel::setMonthSumColumn( int column ) { if(column < columnCount( QModelIndex() )) { _monthExtra[column] = Sum; } } void DateModel::setMonthCountColumn( int column ) { if( column < columnCount(QModelIndex()) ) { _monthExtra[column] = Count; } } void DateModel::setYearSumColumn( int column ) { if(column < columnCount(QModelIndex())) { _yearExtra[column] = Sum; } } void DateModel::setYearCountColumn( int column ) { if( column < columnCount(QModelIndex()) ) { _yearExtra[column] = Count; } } TreeItem *DateModel::findYearItem(int year) { TreeItem *yearItem = NULL; QList yearitems = rootItem->children(); foreach( TreeItem *item, yearitems ) { AbstractIndx *indx = item->payload(); if( indx->year() == year ) { yearItem = item; break; } } return yearItem; } TreeItem *DateModel::findMonthItem(int year, int month) { TreeItem *monthItem = NULL; TreeItem *yearItem = findYearItem( year ); if( yearItem ) { QList monthItems = yearItem->children(); foreach( TreeItem *item, monthItems ) { AbstractIndx *indx = static_cast(item->payload()); if( indx->month() == month ) { monthItem = item; break; } } } return monthItem; } bool DateModel::isDocument(const QModelIndex& indx) const { bool re = false; if( indx.isValid() ) { TreeItem *item = static_cast(indx.internalPointer()); if( item ) { AbstractIndx *abstractindx = item->payload(); re = !(abstractindx->type() == AbstractIndx::YearType || abstractindx->type() == AbstractIndx::MonthType ); } } return re; } DocDigest DateModel::digest(const QModelIndex& indx) const { DocDigest dig; TreeItem *item = static_cast(indx.internalPointer()); AbstractIndx *abstractindx = item->payload(); if( abstractindx->type() == AbstractIndx::YearType || abstractindx->type() == AbstractIndx::MonthType ) { // there is no digest } else { dig = abstractindx->digest(); } return dig; } void DateModel::removeAllData() { // the destructor of the TreeItem removes the entire tree recursivly delete rootItem; rootItem = new TreeItem(0); } void DateModel::addData( const DocDigest& digest ) // DocumentIndx doc ) { int month = digest.rawDate().month(); int year = digest.rawDate().year(); TreeItem *yearItem = NULL; TreeItem *monthItem = NULL; yearItem = findYearItem( year ); if( !yearItem ) { AbstractIndx *newIndx = new YearIndx(year); yearItem = new TreeItem( newIndx, rootItem ); } // ==== monthItem = findMonthItem( year, month ); if( !monthItem ) { AbstractIndx *newIndx = new MonthIndx(year, month); monthItem = new TreeItem( newIndx, yearItem); } DocumentIndx *itemIndx = new DocumentIndx(digest); TreeItem *newItem = new TreeItem( itemIndx, monthItem ); Q_UNUSED(newItem); } kraft-0.97/src/models/datemodel.h000066400000000000000000000057061410616450300167510ustar00rootroot00000000000000/*************************************************************************** datemodel.h ------------------- copyright : (C) 2017 by Klaas Freitag email : klaas@volle-kraft-voraus.de ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef DATEMODEL_H #define DATEMODEL_H #include #include #include #include #include #include #include "docbasemodel.h" #include "docdigest.h" class TreeItem; class AbstractIndx { public: enum IndxType { Invalid = 0, DocumentType, YearType, MonthType }; explicit AbstractIndx(); virtual ~AbstractIndx() { } explicit AbstractIndx(IndxType t); explicit AbstractIndx(IndxType t, DocDigest(digest)); virtual IndxType type(); DocDigest digest() const; int year(); int month(); protected: DocDigest _docDigest; private: IndxType _type; }; /* ================================================================== */ class DocumentIndx : public AbstractIndx { public: DocumentIndx(const DocDigest& digest) :AbstractIndx(IndxType::DocumentType, digest) { } }; /* ================================================================== */ class DateModel : public DocBaseModel { public: DateModel(QObject *parent = 0); enum CalcType { Zero = 0, Sum, Count }; TreeItem* findYearItem(int year); TreeItem* findMonthItem(int year, int month); void setMonthSumColumn( int column ); void setMonthCountColumn( int column ); void setYearSumColumn( int column ); void setYearCountColumn( int column ); QVariant data(const QModelIndex &index, int role) const; Qt::ItemFlags flags(const QModelIndex &index) const; QModelIndex index(int row, int column, const QModelIndex &parent) const; QModelIndex parent(const QModelIndex &index) const; DocDigest digest(const QModelIndex& indx) const; int rowCount(const QModelIndex &parent) const; void removeAllData(); void addData(const DocDigest& digest); bool isDocument(const QModelIndex& indx) const; private: TreeItem *rootItem; QVector _monthExtra; QVector _yearExtra; }; #endif // DATEMODEL_H kraft-0.97/src/models/docbasemodel.cpp000066400000000000000000000160441410616450300177640ustar00rootroot00000000000000/*************************************************************************** datemodel.cpp ------------------- copyright : (C) 2017 by Klaas Freitag email : klaas@volle-kraft-voraus.de ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include "docbasemodel.h" #include "kraftdb.h" #include #include #include #include DocBaseModel::DocBaseModel(QObject *parent) :QAbstractItemModel(parent) { _headers.resize(12); _headers[ Document_Id ] = i18n("Date"); // this is only displayed by the date model _headers[ Document_Ident ] = i18n("Doc. Number"); _headers[ Document_Type ] = i18n( "Doc. Type"); _headers[ Document_Whiteboard ] = i18n( "Whiteboard" ); _headers[ Document_ClientId ] = i18n( "Client Id" ); _headers[ Document_LastModified] = i18n( "Last modified" ); _headers[ Document_CreationDateRaw] = i18n( "Creation date" ); _headers[ Document_ProjectLabel] = i18n( "Project" ); _headers[ Document_ClientAddress ] = i18n( "Client Address" ); _headers[ Document_ClientName ] = i18n( "Client" ); mAddressProvider = new AddressProvider( this ); connect( mAddressProvider, SIGNAL(lookupResult(QString,KContacts::Addressee)), this, SLOT(slotAddresseeFound(QString, KContacts::Addressee))); } QString DocBaseModel::firstLineOf( const QString& str) const { QString var; if( !str.isEmpty() ) { QStringList li = str.split(QChar('\n')); var = QString( "> %1").arg(li[0]); } return var; } QVariant DocBaseModel::columnValueFromDigest( const DocDigest& digest, int col ) const { if( col < 0 || col >= Max_Column_Marker ) return QVariant(); QVariant var; QStringList li; QString help; switch(col) { case Document_Id: case Document_Id_Raw: var = digest.id(); break; case Document_Ident: var = digest.ident(); break; case Document_Type: var = digest.type(); break; case Document_Whiteboard: help = digest.whiteboard(); li = help.split(QChar('\n')); var = li[0]; break; case Document_ClientId: var = digest.clientId(); break; case Document_LastModified: var = digest.lastModified(); break; case Document_CreationDate: var = digest.date(); break; case Document_CreationDateRaw: var = digest.rawDate(); break; case Document_ProjectLabel: var = digest.projectLabel(); break; case Document_ClientAddress: { help = firstLineOf( digest.clientAddress()); break; } case Document_ClientName: { help = digest.clientId(); AddressProvider::LookupState state = mAddressProvider->lookupAddressee(help); if( state == AddressProvider::LookupFromCache ) { KContacts::Addressee addressee = mAddressProvider->getAddresseeFromCache(help); var = addressee.assembledName(); // qDebug() << "Address from Cache: " << var.toString(); } else if( state == AddressProvider::LookupOngoing ) { var = i18n("Looking up address"); } else if( state == AddressProvider::LookupStarted ) { var = i18n("Lookup started"); } else if( state == AddressProvider::LookupNotFound || state == AddressProvider::BackendError || state == AddressProvider::ItemError ) { var = firstLineOf(digest.clientAddress()); } break; } default: break; } return var; } int DocBaseModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return Max_Column_Marker; } QVariant DocBaseModel::headerData(int section, Qt::Orientation orientation, int role) const { if( role == Qt::DisplayRole && orientation == Qt::Horizontal && section < _headers.count() ) { return _headers.at(section); } return QVariant(); } Qt::ItemFlags DocBaseModel::flags(const QModelIndex &index) const { if (!index.isValid()) return 0; return Qt::ItemIsEnabled | Qt::ItemIsSelectable; } void DocBaseModel::resetData() { beginResetModel(); removeAllData(); loadFromTable(); endResetModel(); } void DocBaseModel::slotAddresseeFound( const QString& uid, const KContacts::Addressee& contact) { // FIXME: Update the data in the model and update the view accordingly. // Given that the view is updated so often, it does not seem to be neccessary // to do at all. Maybe later... Q_UNUSED(uid); Q_UNUSED(contact); } int DocBaseModel::loadFromTable() { int cnt = 0; QSqlQuery query; query.prepare("SELECT docID, ident, docType, docDescription, clientID, lastModified," "date, projectLabel, clientAddress " "FROM document ORDER BY date DESC"); query.exec(); /* enum Columns { Document_Id = 0, Document_Ident = 1, Document_Type = 2, Document_Whiteboard = 3, Document_ClientId = 4, Document_LastModified = 5, Document_CreationDate = 6, Document_ProjectLabel = 7, Document_ClientAddress = 8, Document_ClientName = 9, }; */ while (query.next()) { DocDigest digest(query.value(Document_Id).toInt(), query.value(Document_Type).toString(), query.value(Document_ClientId).toString()); digest.setDate( query.value( Document_CreationDate ).toDate() ); QDateTime dt = query.value(Document_LastModified).toDateTime(); if (KraftDB::self()->isSqlite()) { // The timestamps in Sqlite are in UTC dt.setTimeSpec(Qt::UTC); digest.setLastModified(dt.toLocalTime()); } else { digest.setLastModified(dt); } const QString clientAdr = query.value(Document_ClientAddress).toString(); digest.setClientAddress( clientAdr ); QString ident = query.value(Document_Ident).toString(); digest.setIdent( ident ); digest.setWhiteboard( query.value(Document_Whiteboard).toString() ); digest.setProjectLabel( query.value(Document_ProjectLabel).toString() ); const QString clientId = query.value(Document_ClientId).toString(); digest.setClientId( clientId ); this->addData( digest ); } return cnt; } kraft-0.97/src/models/docbasemodel.h000066400000000000000000000055171410616450300174340ustar00rootroot00000000000000/*************************************************************************** datemodel.h ------------------- copyright : (C) 2017 by Klaas Freitag email : klaas@volle-kraft-voraus.de ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef DOCBASEMODEL_H #define DOCBASEMODEL_H #include #include "addressprovider.h" #include "docdigest.h" #include #include #include #include #include #include class DocBaseModel : public QAbstractItemModel { Q_OBJECT public: DocBaseModel(QObject *parent = 0); virtual ~DocBaseModel() {} enum Columns { Document_Id = 0, Document_Ident = 1, Document_Type = 2, Document_Whiteboard = 3, Document_ClientId = 4, Document_LastModified = 5, Document_CreationDate = 6, Document_ProjectLabel = 7, Document_ClientAddress = 8, Document_ClientName = 9, Document_Id_Raw = 10, Document_CreationDateRaw = 11, Treestruct_Year = 12, Treestruct_Month = 13, Treestruct_Type = 14, Max_Column_Marker = 15 // leave this as last enum }; int columnCount(const QModelIndex &parent) const; virtual QVariant data(const QModelIndex &index, int role) const = 0; Qt::ItemFlags flags(const QModelIndex &index) const; virtual int rowCount(const QModelIndex &parent) const = 0; virtual void removeAllData() = 0; virtual void addData(const DocDigest& digest) = 0; virtual DocDigest digest(const QModelIndex& indx) const = 0; virtual bool isDocument(const QModelIndex& indx) const = 0; int loadFromTable(); void resetData(); protected: QVariant columnValueFromDigest( const DocDigest& digest, int col ) const; QVariant headerData(int section, Qt::Orientation orientation, int role) const; protected slots: void slotAddresseeFound(const QString &uid, const KContacts::Addressee &contact); private: QString firstLineOf( const QString& str) const; QVector _headers; AddressProvider *mAddressProvider; }; #endif // DATEMODEL_H kraft-0.97/src/models/documentmodel.cpp000066400000000000000000000056771410616450300202140ustar00rootroot00000000000000/*************************************************************************** documentmodel - the database model for documents ------------------- begin : 2010-01-11 copyright : (C) 2010 by Thomas Richard, 2011 by Klaas Freitag email : thomas.richard@proan.be ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ //QT includes #include #include #include #include #include #include //KDE includes #include //Kraft includes #include "documentmodel.h" #include "docdigest.h" #include "docbasemodel.h" #include "defaultprovider.h" DocumentModel::DocumentModel(QObject *parent) : DocBaseModel(parent) { } DocumentModel::~DocumentModel() { } int DocumentModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); return _digests.count(); } void DocumentModel::removeAllData() { _digests.clear(); } void DocumentModel::addData( const DocDigest& digest ) { _digests.append(digest); } QModelIndex DocumentModel::index(int row, int column, const QModelIndex &parent) const { if (!hasIndex(row, column, parent)) return QModelIndex(); return createIndex(row, column); } QModelIndex DocumentModel::parent(const QModelIndex &index) const { Q_UNUSED(index); return QModelIndex(); } bool DocumentModel::isDocument(const QModelIndex& indx) const { Q_UNUSED(indx); return indx.isValid(); } QVariant DocumentModel::data(const QModelIndex &idx, int role) const { if( !idx.isValid() ) return QVariant(); int row = idx.row(); if( row < 0 || row >= _digests.count() ) { return QVariant(); } const DocDigest digest = _digests.at(row); if(role == Qt::DisplayRole) { return columnValueFromDigest( digest, idx.column() ); } else if( role == Qt::SizeHintRole ) { QFont f = data(idx, Qt::FontRole).value(); QFontMetrics fm(f); int h = fm.height(); return QSize( 0, h + 4 ); } return QVariant(); } DocDigest DocumentModel::digest( const QModelIndex& index ) const { int row = index.row(); DocDigest digest; if( row > -1 && row < _digests.count() ) { digest = _digests.at(index.row()); } return digest; } kraft-0.97/src/models/documentmodel.h000066400000000000000000000040621410616450300176440ustar00rootroot00000000000000/*************************************************************************** documentmodel - the database model for documents ------------------- begin : 2010-01-11 copyright : Copyright 2010 by Thomas Richard, 2011 by Klaas Freitag email : thomas.richard@proan.be ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef DOCUMENTMODEL_H #define DOCUMENTMODEL_H #include "docbasemodel.h" #include class DocDigest; class AddressProvider; class DocumentModel : public DocBaseModel { Q_OBJECT public: DocumentModel(QObject *parent = 0); ~DocumentModel(); QVariant data(const QModelIndex &idx, int role) const; // QVariant headerData( int, Qt::Orientation, int role = Qt::DisplayRole ) const; QModelIndex index(int row, int column, const QModelIndex &parent) const; QModelIndex parent(const QModelIndex &index) const; int rowCount(const QModelIndex &parent) const; // int columnCount(const QModelIndex &parent = QModelIndex()) const; DocDigest digest( const QModelIndex& ) const; void setQueryAgain(); void removeAllData(); void addData( const DocDigest& ); bool isDocument(const QModelIndex& indx) const; // protected slots: // void slotAddresseeFound( const QString&, const KContacts::Addressee& ); protected: private: DocDigestList _digests; }; #endif kraft-0.97/src/models/documentproxymodels.cpp000066400000000000000000000124301410616450300214620ustar00rootroot00000000000000/*************************************************************************** latestdocmodel - the latest documents model ------------------- begin : 2010-01-11 copyright : (C) 2010 by Thomas Richard email : thomas.richard@proan.be ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ //QT includes #include #include #include #include #include #include #include #include #include #include //Kraft includes #include "datemodel.h" #include "documentmodel.h" #include "defaultprovider.h" #include "docdigest.h" #include "documentproxymodels.h" DocumentFilterModel::DocumentFilterModel(int maxRows, QObject *parent) : QSortFilterProxyModel(parent), _enableTreeView(false), _treeModel(0), _tableModel(0) { m_MaxRows = maxRows; this->setFilterCaseSensitivity(Qt::CaseInsensitive); } void DocumentFilterModel::setMaxRows( int max ) { m_MaxRows = max; invalidateFilter(); // refreshes the model } void DocumentFilterModel::setEnableTreeview( bool treeview ) { _enableTreeView = treeview; DocBaseModel *model; if(_enableTreeView) { if( _treeModel.isNull() ) { _treeModel.reset(new DateModel); _treeModel->loadFromTable(); } model = _treeModel.data(); } else { if( _tableModel.isNull()) { _tableModel.reset(new DocumentModel); _tableModel->loadFromTable(); } model = _tableModel.data(); } setSourceModel(model); } bool DocumentFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const { QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); if( !index.isValid()) { return false; } bool accepted = false; // filter works on the document ID, the client name and the document type. const QRegExp filter = filterRegExp(); if( filter.pattern().isEmpty() ) { accepted = true; } else { const QModelIndex index0 = sourceModel()->index(sourceRow, DocumentModel::Document_Ident, sourceParent); const QString idStr = sourceModel()->data(index0).toString(); const QModelIndex index1 = sourceModel()->index(sourceRow, DocumentModel::Document_Type, sourceParent); const QString typeStr = sourceModel()->data(index1).toString(); const QModelIndex index2 = sourceModel()->index(sourceRow, DocumentModel::Document_ClientName, sourceParent); const QString clientNameStr = sourceModel()->data(index2).toString(); const QModelIndex index3 = sourceModel()->index(sourceRow, DocumentModel::Document_Whiteboard, sourceParent); const QString whiteboardStr = sourceModel()->data(index3).toString(); const QModelIndex index4 = sourceModel()->index(sourceRow, DocumentModel::Document_ProjectLabel, sourceParent); const QString projectStr = sourceModel()->data(index4).toString(); if( idStr.contains(filter) || typeStr.contains(filter) || clientNameStr.contains(filter) || whiteboardStr.contains(filter) || projectStr.contains(filter)) { accepted = true; } } // for the treeview, check all the children if( _enableTreeView ) { int rows = sourceModel()->rowCount(index); for (int row = 0; row < rows; row++) { if (filterAcceptsRow(row, index)) { accepted = true; } } } // if the entry is accepted so far, check if it is within the time limit if( accepted && m_MaxRows > -1 ) { const QModelIndex index = sourceModel()->index(sourceRow, DocumentModel::Document_CreationDateRaw, sourceParent); const QDate docDate = sourceModel()->data(index).toDate(); int dateDiff = docDate.daysTo(QDate::currentDate()); if( dateDiff > m_MaxRows ) { accepted = false; } } return accepted; } bool DocumentFilterModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const { QVariant leftData = sourceModel()->data(source_left); QVariant rightData = sourceModel()->data(source_right); if (leftData.type() == QVariant::DateTime) { return leftData.toDateTime() < rightData.toDateTime(); } if(leftData.type() == QVariant::Date ) { return leftData.toDate() < rightData.toDate(); } else { const QString leftString = leftData.toString(); const QString rightString = rightData.toString(); return QString::localeAwareCompare(leftString, rightString) < 0; } } kraft-0.97/src/models/documentproxymodels.h000066400000000000000000000036341410616450300211350ustar00rootroot00000000000000/*************************************************************************** documentproxymodels - contains proxymodels to show the documentmodel in different views ------------------- begin : 2010-01-11 copyright : Copyright 2010 by Thomas Richard email : thomas.richard@proan.be ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef DOCUMENTPROXYMODELS_H #define DOCUMENTPROXYMODELS_H #include #include class QModelIndex; class QVariant; class QObject; class DateModel; class DocumentModel; //Filters out the last 10 items of the DocumentModel class DocumentFilterModel : public QSortFilterProxyModel { public: DocumentFilterModel(int maxRows = -1, QObject *parent = 0); void setMaxRows( int ); void setEnableTreeview( bool treeview ); protected: bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const; bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const; private: int m_MaxRows; bool _enableTreeView; QScopedPointer _treeModel; QScopedPointer _tableModel; }; #endif kraft-0.97/src/models/globalcontactmodel.h000066400000000000000000000033041410616450300206400ustar00rootroot00000000000000/* This file is part of KAddressBook. Copyright (c) 2009 Tobias Koenig 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef GLOBALCONTACTMODEL_H #define GLOBALCONTACTMODEL_H namespace Akonadi { class ChangeRecorder; class ContactsTreeModel; class Monitor; class Session; } /** * @short Provides the global model for all contacts * * This model provides the EntityTreeModel for all contacts. * The model is accessable via the static instance() method. */ class GlobalContactModel { public: /** * Destroys the global contact model. */ ~GlobalContactModel(); /** * Returns the global contact model instance. */ static GlobalContactModel* instance(); /** * Returns the item model of the global instance. */ Akonadi::ContactsTreeModel* model() const; private: GlobalContactModel(); static GlobalContactModel *mInstance; Akonadi::Session *mSession; Akonadi::ChangeRecorder *mMonitor; Akonadi::ContactsTreeModel *mModel; }; #endif kraft-0.97/src/mysqldetails.ui000066400000000000000000000045701410616450300164270ustar00rootroot00000000000000 mySqlDetailsForm 0 0 401 213 Please enter the MySQL Database server settings. For detailed setup instructions for the MySQL to use with Kraft please check the Kraft website. true Database Host: Database Name: Database User: Password: Qt::Vertical 20 12 kraft-0.97/src/newdocassistant.cpp000066400000000000000000000202211410616450300172610ustar00rootroot00000000000000/*************************************************************************** newdocassistant - widget to select header data for the doc ------------------- begin : 2008-02-12 copyright : (C) 2008 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "newdocassistant.h" #include "defaultprovider.h" #include "filterheader.h" #include "doctype.h" #include "kraftsettings.h" #include "addressselectorwidget.h" #include "documentman.h" CustomerSelectPage::CustomerSelectPage( QWidget *parent ) :QWizardPage ( parent ) { QVBoxLayout *vbox = new QVBoxLayout; setLayout( vbox ); setTitle(i18n( "New Document Settings" )); QLabel *help = new QLabel; help->setText( i18n( "Please select a customer as addressee for the document. " "If there is no entry for the customer in the addressbook yet, it can be opened " "by clicking on the button below." ) ); // help->setTextFormat( Qt::RichText ); help->setWordWrap( true ); help->setSizePolicy( QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed )); vbox->addWidget( help ); mAddresses = new AddressSelectorWidget(this); connect( mAddresses, SIGNAL( addressSelected( const KContacts::Addressee& ) ), SIGNAL( addresseeSelected( const KContacts::Addressee& ) ) ); vbox->addWidget( mAddresses ); } void CustomerSelectPage::saveState() { mAddresses->saveState(); } void CustomerSelectPage::setupAddresses() { } CustomerSelectPage:: ~CustomerSelectPage() { } // ########################################################################### DocDetailsPage::DocDetailsPage( QWidget *parent ) : QWizardPage(parent), _haveAddressSelect(true), mCustomerLabel( nullptr ) { QVBoxLayout *vbox = new QVBoxLayout; setLayout( vbox ); setTitle(i18n( "New Document Settings" )); QLabel *help = new QLabel; help->setTextFormat( Qt::RichText ); help->setText( i18n( "Select a document type and a date. A comment on the whiteboard " "helps to classify the document." ) ); vbox->addWidget( help ); mCustomerLabel = new QLabel; mCustomerLabel->setFrameStyle( QFrame::Box + QFrame::Sunken ); mCustomerLabel->setTextFormat( Qt::RichText ); mCustomerLabel->setText( i18n( "Customer: Not yet selected!" ) ); vbox->addWidget( mCustomerLabel ); QFormLayout *grid = new QFormLayout; vbox->addLayout( grid ); mTypeCombo = new QComboBox; mTypeCombo->insertItems( 0, DocType::allLocalised() ); mTypeCombo->setCurrentIndex( mTypeCombo->findText( DefaultProvider::self()->docType() )); grid->addRow( i18n("Document &Type:"), mTypeCombo ); mDateEdit = new QDateEdit; mDateEdit->setDate( QDate::currentDate() ); grid->addRow( i18n( "Document Date: " ), mDateEdit ); mWhiteboardEdit = new QTextEdit; grid->addRow( i18n( "Whiteboard Content:" ), mWhiteboardEdit ); QHBoxLayout *hbox = new QHBoxLayout; vbox->addLayout(hbox); mKeepItemsCB = new QCheckBox( i18n("Copy document items from predecessor document")); hbox->addWidget( mKeepItemsCB ); mSourceDocIdentsCombo = new QComboBox; hbox->addWidget(mSourceDocIdentsCombo); mSourceDocIdentsCombo->setVisible(false); mKeepItemsCB->setChecked(true); mKeepItemsCB->setVisible(false); vbox->addStretch( 1 ); } DocDetailsPage::~DocDetailsPage() { } void DocDetailsPage::setNoAddresses() { _haveAddressSelect = false; } // ########################################################################### KraftWizard::KraftWizard(QWidget *parent, const char* name, bool modal ) :QWizard( parent ), mCustomerPage( nullptr ), mCustomerBox( nullptr ), mParent( parent ) { setObjectName( name ); setModal( modal ); const QByteArray geo = QByteArray::fromBase64( KraftSettings::self()->newDocWizardGeometry().toAscii() ); restoreGeometry(geo); } KraftWizard::~KraftWizard() { } void KraftWizard::init( bool haveAddressSelect, const QString& followUpDoc ) { QScopedPointer addressProvider; addressProvider.reset(new AddressProvider); if( followUpDoc.isEmpty() ) { setWindowTitle( i18n( "Create a new Kraft Document" ) ); } else { setWindowTitle(followUpDoc); } mDetailsPage = new DocDetailsPage(); addPage(mDetailsPage); // w1, QLatin1Literal("

            ") + + QLatin1Literal("

            ") ); // only pick an addressee if the document is really new if( addressProvider->backendUp() && haveAddressSelect ) { mCustomerPage = new CustomerSelectPage( ); addPage( mCustomerPage); // w, QLatin1Literal("

            ") + i18n( "Select an Addressee" ) + QLatin1Literal("

            ") ); mCustomerPage->setupAddresses(); connect( mCustomerPage, SIGNAL( addresseeSelected(KContacts::Addressee)), this, SLOT( slotAddressee(KContacts::Addressee))); } } void KraftWizard::done( int r ) { if( mCustomerPage ) { mCustomerPage->saveState(); } const QByteArray geo = saveGeometry().toBase64(); KraftSettings::self()->setNewDocWizardGeometry(geo); QWizard::done(r); } void KraftWizard::slotAddressee(const KContacts::Addressee& addressee) { // qDebug () << "Addressee Changed!"; mAddressee = addressee; } QDate KraftWizard::date() const { return mDetailsPage->mDateEdit->date(); } QString KraftWizard::addressUid() const { return mAddressee.uid(); } QString KraftWizard::docType() const { return mDetailsPage->mTypeCombo->currentText(); } QString KraftWizard::whiteboard() const { return mDetailsPage->mWhiteboardEdit->toPlainText(); } void KraftWizard::setDocToFollow( DocGuardedPtr sourceDoc) { if( !sourceDoc ) { return; } DocGuardedPtr dPtr = sourceDoc; QString id = sourceDoc->docID().toString(); while( ! id.isEmpty() ) { // store the id of the follower and clear id const QString idT = dPtr->docIdentifier(); mDetailsPage->mSourceDocIdentsCombo->addItem(idT, id); id = QString(); // remember the current dptr to be able to delete it soon DocGuardedPtr oldDptr = dPtr; dPtr = DocumentMan::self()->openDocumentbyIdent( dPtr->predecessor() ); if( dPtr ) { id = dPtr->docID().toString(); } if( oldDptr != sourceDoc ) { delete oldDptr; } } if( mDetailsPage->mSourceDocIdentsCombo->count() > 0 ) { mDetailsPage->mKeepItemsCB->setVisible(true); mDetailsPage->mSourceDocIdentsCombo->setVisible(true); } // we already know the customer, disable the customer select page. mDetailsPage->setNoAddresses(); if ( mDetailsPage->mCustomerLabel ) { const QString followText = i18n("Followup Document for %1", sourceDoc->docIdentifier() ); mDetailsPage->mCustomerLabel->setText( followText ); } } QString KraftWizard::copyItemsFromPredecessor() { QString re; if( mDetailsPage->mKeepItemsCB->checkState() == Qt::Checked ) { re = mDetailsPage->mSourceDocIdentsCombo->currentData().toString(); } return re; } void KraftWizard::setAvailDocTypes( const QStringList& list ) { mDetailsPage->mTypeCombo->clear(); mDetailsPage->mTypeCombo->insertItems( -1, list ); } kraft-0.97/src/newdocassistant.h000066400000000000000000000061511410616450300167340ustar00rootroot00000000000000/*************************************************************************** new doc assistant - widget to select Addresses ------------------- begin : 2008-02-12 copyright : (C) 2008 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef NEWDOCASSISTANT_H #define NEWDOCASSISTANT_H #include #include #include #include #include "kraftdoc.h" #include "docguardedptr.h" class DocText; class TextSelection; class KraftWizard; class AddressSelectorWidget; class QDateEdit; class QComboBox; class QHBox; class QTextEdit; class QCheckBox; using namespace KContacts; // --------------------------------------------------------------------------- class CustomerSelectPage: public QWizardPage { Q_OBJECT friend class KraftWizard; public: CustomerSelectPage( QWidget *parent = 0 ); ~CustomerSelectPage(); void setupAddresses(); public slots: void saveState(); signals: void addresseeSelected( const KContacts::Addressee& ); private: AddressSelectorWidget *mAddresses; }; // --------------------------------------------------------------------------- class DocDetailsPage : public QWizardPage { Q_OBJECT friend class KraftWizard; public: DocDetailsPage( QWidget *parent = 0 ); ~DocDetailsPage(); void setNoAddresses(); private: bool _haveAddressSelect; QLabel *mCustomerLabel; QDateEdit *mDateEdit; QComboBox *mTypeCombo; QTextEdit *mWhiteboardEdit; QCheckBox *mKeepItemsCB; QComboBox *mSourceDocIdentsCombo; }; // --------------------------------------------------------------------------- class KraftWizard: public QWizard { Q_OBJECT public: KraftWizard(QWidget *parent = 0, const char* name = 0, bool modal = false ); void init(bool haveAddressSelect, const QString& followUpDoc = QString()); ~KraftWizard(); QDate date() const ; QString addressUid() const; QString docType() const; QString whiteboard() const; void setCustomer( const QString& ); void setDocToFollow( DocGuardedPtr sourceDoc); void setAvailDocTypes( const QStringList& ); void done(int r); QString copyItemsFromPredecessor(); protected slots: void slotAddressee( const KContacts::Addressee& ); private: CustomerSelectPage *mCustomerPage; DocDetailsPage *mDetailsPage; QHBox *mCustomerBox; QWidget *mParent; KContacts::Addressee mAddressee; }; #endif kraft-0.97/src/numbercycle.cpp000066400000000000000000000030251410616450300163630ustar00rootroot00000000000000/*************************************************************************** numbercycle.h - document number cycles ------------------- begin : Jan 15 2009 copyright : (C) 2009 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include "numbercycle.h" NumberCycle::NumberCycle() { } NumberCycle::NumberCycle( dbID _id ) :id( _id ) { } void NumberCycle::setName( const QString& n ) { mName = n; } QString NumberCycle::name() { return mName; } void NumberCycle::setTemplate( const QString& t ) { mTemplate = t; } QString NumberCycle::getTemplate() { return mTemplate; } void NumberCycle::setCounter( int c ) { mCounter = c; } int NumberCycle::counter() { return mCounter; } QString NumberCycle::defaultName() { return QString( "default" ); } kraft-0.97/src/numbercycle.h000066400000000000000000000027361410616450300160400ustar00rootroot00000000000000/*************************************************************************** numbercycle.h - document number cycles ------------------- begin : Jan 15 2009 copyright : (C) 2009 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef NUMBERCYCLE_H #define NUMBERCYCLE_H #include #include "dbids.h" #include "kraftcat_export.h" class KRAFTCAT_EXPORT NumberCycle { public: NumberCycle(); NumberCycle( dbID ); void setName( const QString& ); QString name(); void setTemplate( const QString& ); QString getTemplate(); void setCounter( int ); int counter(); static QString defaultName(); private: dbID id; QString mName; QString mTemplate; int mCounter; }; #endif kraft-0.97/src/numbercycledialog.cpp000066400000000000000000000322731410616450300175520ustar00rootroot00000000000000/*************************************************************************** doctypeedit.h - the document type editor ------------------- begin : Fri Jan 2 2009 copyright : (C) 2009 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "prefsdialog.h" #include "kraftsettings.h" #include "kraftdb.h" #include "kraftdoc.h" #include "defaultprovider.h" #include "doctype.h" #include "doctypeedit.h" #include "numbercycledialog.h" NumberCycleDialog::NumberCycleDialog( QWidget *parent, const QString& initType ) :QDialog( parent ) // "NUMBER_CYCLES_EDIT", true, i18n( "Edit Number Cycles" ), Ok|Cancel ) { setObjectName( "NUMBER_CYCLES_EDIT" ); setModal( true ); setWindowTitle( i18n( "Edit Number Cycles" ) ); QVBoxLayout *layout = new QVBoxLayout; setLayout(layout); QWidget *w = new QWidget; layout->addWidget(w); mBaseWidget = new Ui::NumberCycleEditBase( ); mBaseWidget->setupUi( w ); mBaseWidget->mPbAdd->setIcon( QIcon::fromTheme( "list-add" ) ); mBaseWidget->mPbRemove->setIcon( QIcon::fromTheme( "list-remove" ) ); mBaseWidget->mCounterEdit->setMaximum( 1000000 ); mBaseWidget->mCounterEdit->setSingleStep( 1 ); const QString tip = i18n( "The template may contain the following tags:" "
            • %y or %yyyy - the year of the documents date.
            • " "
            • %yy - the year of the document (two digits).
            • " "
            • %w - the week number of the documents date.
            • " "
            • %ww - the week number of the documents date with leading zero.
            • " "
            • %d - the day number of the documents date.
            • " "
            • %dd - the day number of the documents date with leading zero.
            • " "
            • %m or %M - the month number of the documents date.
            • " "
            • %MM - the month number with leading zero.
            • " "
            • %c - the customer id from kaddressbook
            • " "
            • %i - the unique counter
            • " "
            • %type - the localised doc type (offer, invoice etc.)
            • " "
            • %uid - the contact id of the client.
            • " "
            %i needs to be part of the template." ); mBaseWidget->mIdTemplEdit->setToolTip( tip ); connect( mBaseWidget->mPbAdd, SIGNAL( clicked() ), SLOT( slotAddCycle() ) ); connect( mBaseWidget->mPbRemove, SIGNAL( clicked() ), SLOT( slotRemoveCycle() ) ); loadCycles(); connect( mBaseWidget->mCycleListBox, SIGNAL( currentRowChanged( int ) ), SLOT( slotNumberCycleSelected( int ) ) ); QListWidgetItem *initItem = mBaseWidget->mCycleListBox->findItems( initType, Qt::MatchExactly ).first(); if ( initItem ) { mBaseWidget->mCycleListBox->setCurrentItem( initItem, QItemSelectionModel::Select ); } QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); _okButton = buttonBox->button(QDialogButtonBox::Ok); _okButton->setDefault(true); _okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); layout->addWidget(buttonBox); slotUpdateExample(); connect( mBaseWidget->mIdTemplEdit, SIGNAL( textChanged( const QString& ) ), SLOT( slotTemplTextChanged( const QString& ) ) ); connect( mBaseWidget->mCounterEdit, SIGNAL( valueChanged( int ) ), SLOT( slotUpdateExample() ) ); } void NumberCycleDialog::loadCycles() { QSqlQuery q( "SELECT id, name, lastIdentNumber, identTemplate FROM numberCycles ORDER BY name" ); mBaseWidget->mCycleListBox->clear(); while ( q.next() ) { dbID id( q.value( 0 ).toInt() ); NumberCycle nc( id ); nc.setName( q.value( 1 ).toString() ); nc.setCounter( q.value( 2 ).toInt() ); nc.setTemplate( q.value( 3 ).toString() ); mNumberCycles[nc.name()] = nc; mBaseWidget->mCycleListBox->addItem( nc.name() ); } } void NumberCycleDialog::slotUpdateExample() { DocType dt; dt.setName( i18n( "Doc-Type" ) ); int id = mBaseWidget->mCounterEdit->value(); dt.setIdentTemplate( mBaseWidget->mIdTemplEdit->text() ); QString idText = dt.generateDocumentIdent( QDate::currentDate(), QLatin1String(""), id ); mBaseWidget->mExampleId->setText( idText ); } void NumberCycleDialog::slotTemplTextChanged( const QString& str ) { bool state = false; if ( !str.isEmpty() && str.contains( "%i" ) ) { state = true; } if( _okButton ) { _okButton->setEnabled( state ); } slotUpdateExample(); } void NumberCycleDialog::updateCycleDataFromGUI() { // Store the updated values if ( !mSelectedCycle.isEmpty() ) { // qDebug () << "Updating the cycle: " << mSelectedCycle; if ( mNumberCycles.contains( mSelectedCycle ) ) { QString h = mBaseWidget->mIdTemplEdit->text(); mNumberCycles[mSelectedCycle].setTemplate( h ); // qDebug () << "Number Cycle Template: " << h; int num = mBaseWidget->mCounterEdit->value(); // qDebug () << "Number Edit: " << num; mNumberCycles[mSelectedCycle].setCounter( num ); } else { // qDebug () << "WRN: NumberCycle " << mSelectedCycle << " is not known"; } } else { // qDebug () << "The selected cycle name is Empty!"; } } void NumberCycleDialog::slotNumberCycleSelected( int num ) { updateCycleDataFromGUI(); // set the new data of the selected cycle QString name = mBaseWidget->mCycleListBox->item( num )->text(); if ( ! mNumberCycles.contains( name ) ) { // qDebug () << "No numbercycle found at pos " << num; } NumberCycle nc = mNumberCycles[name]; // qDebug () << "Selected number cycle number " << num; mBaseWidget->mIdTemplEdit->setText( nc.getTemplate() ); mBaseWidget->mCounterEdit->setMinimum( 0 ); // nc.counter() ); mBaseWidget->mCounterEdit->setValue( nc.counter() ); mBaseWidget->mNameEdit->setText( nc.name() ); mBaseWidget->mNameEdit->setReadOnly( true ); // remember the cycle name mSelectedCycle = name; bool state = true; if ( name == NumberCycle::defaultName() ) { state = false; } mBaseWidget->mPbRemove->setEnabled( state ); } void NumberCycleDialog::slotAddCycle() { QString newName = QInputDialog::getText( this, i18n( "Add Number Cycle" ), i18n( "Enter the name of a new number cycle." ) ); if ( newName.isEmpty() ) return; bool uniq = true; if ( mNumberCycles.contains( newName ) ) { uniq = false; } if ( uniq ) { NumberCycle numCycle; numCycle.setName( newName ); numCycle.setTemplate( QString::fromLatin1( "%y%w-%i" ) ); QSqlQuery q( "SELECT 1+MAX(lastIdentNumber) FROM numberCycles" ); if ( q.next() ) { numCycle.setCounter( q.value( 0 ).toInt() ); } mNumberCycles[newName] = numCycle; mBaseWidget->mCycleListBox->addItem( numCycle.name() ); } else { // qDebug () << "The name is not unique!"; } QListWidgetItem *item = mBaseWidget->mCycleListBox->findItems( newName, Qt::MatchExactly ).first(); if ( item ) { mBaseWidget->mCycleListBox->setCurrentItem( item ); } } void NumberCycleDialog::slotRemoveCycle() { QString entry = mBaseWidget->mCycleListBox->currentItem()->text(); QListWidgetItem *item = mBaseWidget->mCycleListBox->currentItem(); if ( entry.isEmpty() || !item ) return; mRemovedCycles << entry; if ( item ) { mNumberCycles.remove( entry ); delete item; } } bool NumberCycleDialog::dropOfNumberCycleOk( const QString& name ) { QSqlQuery q; q.prepare( "SELECT count(att.id) FROM attributes att, attributeValues attVal WHERE att.id=attVal.attributeId AND att.hostObject=:dtype AND att.name=:attName AND attVal.value=:val" ); q.bindValue( ":dtype", "DocType" ); q.bindValue( ":attName", "identNumberCycle" ); q.bindValue( ":val", name ); q.exec(); if ( q.next() ) { int cnt = q.value( 0 ).toInt(); if ( cnt > 0 ) { QMessageBox msgBox; msgBox.setText(i18n( "The numbercycle %1 is still assigned to a document type.")); msgBox.setInformativeText(i18n("The number cycle can not be deleted as long as it " "is assigned to a document type." ).arg( name )); msgBox.setStandardButtons(QMessageBox::Ok); } return cnt == 0; } return true; } void NumberCycleDialog::accept() { // qDebug () << "Slot Ok hit"; // get the changed stuff from the gui elements updateCycleDataFromGUI(); // First remove the dropped cycles if ( mRemovedCycles.count() > 0 ) { QSqlQuery qDel; qDel.prepare( "DELETE FROM numberCycles WHERE name=:name" ); for ( QStringList::Iterator it = mRemovedCycles.begin(); it != mRemovedCycles.end(); ++it ) { // qDebug () << "about to drop the number cycle " << *it; if ( dropOfNumberCycleOk( *it ) ) { qDel.bindValue( ":name", *it ); qDel.exec(); } } } // update existing entries and insert new ones // CREATE TABLE numberCycles ( // id INTEGER PRIMARY KEY ASC autoincrement, // name VARCHAR(64) NOT NULL, // lastIdentNumber INT NOT NULL, // identTemplate VARCHAR(64) NOT NULL // ); QSqlQuery q; q.prepare( "SELECT id, name, lastIdentNumber, identTemplate FROM numberCycles WHERE name=:name" ); QMap::Iterator it; for ( it = mNumberCycles.begin(); it != mNumberCycles.end(); ++it ) { QString cycleName = it.key(); NumberCycle cycle = it.value(); q.bindValue( ":name", cycleName ); // name changes can not happen by design q.exec(); if ( q.next() ) { // qDebug () << "Checking existing number cycle " << cycleName << " for update"; // there is an entry if ( q.value( 2 ).toInt() != cycle.counter() ) { bool doUpdate = true; if ( q.value( 2 ).toInt() > cycle.counter() ) { if ( q.value( 3 ).toString() == cycle.getTemplate() ) { // The number has become smaller but the template remains the same. // That has high potential to end up with duplicate doc numbers. QMessageBox msgBox; msgBox.setWindowTitle(i18n("Dangerous Counter Change")); msgBox.setText(i18n("The new counter is lower than the old one. " )); msgBox.setInformativeText(i18n("That has potential to create duplicate document numbers. Do you really want to decrease it?" )); msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); msgBox.setDefaultButton( QMessageBox::Yes ); int re = msgBox.exec(); if( re != QMessageBox::Yes ) { doUpdate = false; } } } if ( doUpdate ) { updateField( q.value( 0 ).toInt(), "lastIdentNumber", QString::number( cycle.counter() ) ); } } if ( q.value( 3 ).toString() != cycle.getTemplate() ) { updateField( q.value( 0 ).toInt(), "identTemplate", cycle.getTemplate() ); } } else { // qDebug () << "This number cycle is new: " << cycleName; QSqlQuery qIns; qIns.prepare( "INSERT INTO numberCycles (name, lastIdentNumber, identTemplate) " "VALUES (:name, :number, :templ)" ); qIns.bindValue( ":name", cycleName ); qIns.bindValue( ":number", cycle.counter() ); qIns.bindValue( ":templ", cycle.getTemplate() ); qIns.exec(); } } QDialog::accept(); } void NumberCycleDialog::updateField( int id, const QString& field, const QString& value ) { QSqlQuery qUpdate; QString sql = "UPDATE numberCycles SET " + field + "=:value WHERE id=:id"; qUpdate.prepare( sql ); // qUpdate.bindValue( ":field", field ); qUpdate.bindValue( ":value", value ); qUpdate.bindValue( ":id", id ); qUpdate.exec(); } kraft-0.97/src/numbercycledialog.h000066400000000000000000000041141410616450300172100ustar00rootroot00000000000000/*************************************************************************** numbercycledialog.h - edit number cycles ------------------- begin : Jan 15 2009 copyright : (C) 2009 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef NUMBERCYCLEDIALOG_H #define NUMBERCYCLEDIALOG_H #include #include #include #include "dbids.h" #include "numbercycle.h" #include "ui_numbercycleseditbase.h" class QLineEdit; class QLabel; class QPushButton; class QComboBox; class QCheckBox; /** * @author Klaas Freitag */ // ################################################################################ class NumberCycleDialog: public QDialog { Q_OBJECT public: NumberCycleDialog( QWidget *parent, const QString& initType = QString() ); public slots: protected slots: void slotAddCycle(); void slotRemoveCycle(); void slotNumberCycleSelected( int ); void slotTemplTextChanged( const QString& ); void accept(); void slotUpdateExample(); private: void updateField( int, const QString&, const QString& ); void loadCycles(); void updateCycleDataFromGUI(); bool dropOfNumberCycleOk( const QString& ); Ui::NumberCycleEditBase *mBaseWidget; QStringList mRemovedCycles; QMap mNumberCycles; QString mSelectedCycle; QPushButton *_okButton; }; #endif kraft-0.97/src/numbercycleseditbase.ui000066400000000000000000000126351410616450300201110ustar00rootroot00000000000000 NumberCycleEditBase 0 0 503 279 <h3>Edit Number Cycles</h3> false Number Cycle Details &Number Cycle: false mNameEdit &Counter: false mCounterEdit false Example Id: false ident &Template: false mIdTemplEdit false false example false &Select a number cycle and edit the details on the right: Qt::AlignVCenter true mCycleListBox New Item Qt::Horizontal QSizePolicy::Expanding 37 20 Click to add a new document type to the list. add click to remove the current document type remove kraft-0.97/src/pdfconverter.cpp000066400000000000000000000174471410616450300165710ustar00rootroot00000000000000/*************************************************************************** pdfconverter.cpp - convert documents to pdf ------------------- begin : March 2020 copyright : (C) 2020 by Klaas Freitag email : kraft@freisturz.de ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include "pdfconverter.h" #include "defaultprovider.h" #include "archiveman.h" #include #include #include #include #include #include #include PDFConverter::PDFConverter() : QObject() { } // ==================================================================== ReportLabPDFConverter::ReportLabPDFConverter() :PDFConverter() { } void ReportLabPDFConverter::convert(const QString& sourceFile, const QString &outputPath) { // qDebug() << "Report BASE:\n" << templ; if ( sourceFile.isEmpty() ) { return; } // findTrml2Pdf returns a list of command line parts for the converter, such as // /usr/bin/pyhton3 /usr/local/share/erml2pdf.py QStringList rmlbin = DefaultProvider::self()->findTrml2Pdf(); if ( ! rmlbin.size() ) { emit converterError(ConvError::TrmlToolFail); } QApplication::setOverrideCursor( QCursor( Qt::BusyCursor ) ); // qDebug () << "Writing output to " << mFile.fileName(); // check if we have etrml2pdf bool haveErml = false; QStringList args; if( rmlbin.size() > 1 ) { QString ermlbin = rmlbin[1]; if( ermlbin.endsWith( "erml2pdf.py") ) { haveErml = true; } args.append(rmlbin.at(1)); } const QString prg = rmlbin.at(0); if( haveErml ) { args << sourceFile; mFile.setFileName(outputPath); mOutputSize = 0; if ( mFile.open( QIODevice::WriteOnly ) ) { qDebug() << "Converting " << mFile.fileName() << "using" << prg << args.join(QChar(' ')); mProcess = new QProcess(); connect(mProcess, &QProcess::readyReadStandardOutput, this, &ReportLabPDFConverter::slotReceivedStdout); connect(mProcess, &QProcess::readyReadStandardError, this, &ReportLabPDFConverter::slotReceivedStderr); connect(mProcess, QOverload::of(&QProcess::finished), this, &ReportLabPDFConverter::trml2pdfFinished); mProcess->setProgram( prg ); mProcess->setArguments(args); mTargetStream.setDevice( &mFile ); mProcess->start( ); } else { emit converterError(ConvError::TargetFileError); } } } void ReportLabPDFConverter::slotReceivedStdout( ) { QByteArray arr = mProcess->readAllStandardOutput(); mOutputSize += arr.size(); mTargetStream.writeRawData( arr.data(), arr.size()); } void ReportLabPDFConverter::slotReceivedStderr( ) { QByteArray arr = mProcess->readAllStandardError(); mErrors.append( arr ); } void ReportLabPDFConverter::trml2pdfFinished( int exitCode, QProcess::ExitStatus stat) { if( mFile.isOpen() ) { mFile.close(); } Q_UNUSED(stat) QApplication::restoreOverrideCursor(); // qDebug () << "PDF Creation Process finished with status " << exitStatus; // qDebug () << "Wrote bytes to the output file: " << mOutputSize; if ( exitCode == 0 ) { QFileInfo fi(mFile.fileName()); if( fi.exists() ) { emit docAvailable( mFile.fileName() ); if( mProcess) { const QString rmlFile = mProcess->arguments().last(); // the file name of the temp rmlfile QFile::remove(rmlFile); // remove the rmlFile } } else { emit converterError(ConvError::TargetFileMissing); } } else { if( mErrors.contains(QLatin1String("No module named Reportlab"))) { emit converterError(ConvError::NoReportLabMod); } else { qDebug() << "Trml2Pdf Error:" << mErrors; emit converterError(ConvError::UnknownError); } } mProcess->deleteLater(); mProcess = nullptr; mFile.setFileName( QString() ); } // ==================================================================== WeasyPrintPDFConverter::WeasyPrintPDFConverter() :PDFConverter() { } void WeasyPrintPDFConverter::convert(const QString& sourceFile, const QString& outputPath) { mErrors.clear(); const QString prg = DefaultProvider::self()->locateBinary("weasyprint"); const QString styleSheet = DefaultProvider::self()->locateFile("reports/kraft.css"); QFileInfo prgInfo(prg); if ( ! prgInfo.exists() || ! prgInfo.isExecutable() ) { emit converterError(ConvError::WeasyPrintNotFound); } mFile.setFileName(outputPath); QApplication::setOverrideCursor( QCursor( Qt::BusyCursor ) ); mProcess = new QProcess; connect(mProcess, &QProcess::readyReadStandardOutput, this, &WeasyPrintPDFConverter::slotReceivedStdout); connect(mProcess, &QProcess::readyReadStandardError, this, &WeasyPrintPDFConverter::slotReceivedStderr); connect(mProcess, QOverload::of(&QProcess::finished), this, &WeasyPrintPDFConverter::weasyPrintFinished); QStringList args; QFileInfo styleFI(styleSheet); const QString styleSheetDir = styleFI.canonicalPath(); args << sourceFile; args << mFile.fileName(); args << "-u"; args << styleSheetDir; if (!_templatePath.isEmpty() && _templatePath != styleSheetDir) { args << "-u"; args << _templatePath; } qDebug() << "Arguments for weasyprint:" << args; mProcess->setProgram( prg ); mProcess->setArguments(args); mOutput.clear(); mProcess->start( ); } void WeasyPrintPDFConverter::slotReceivedStdout( ) { QByteArray arr = mProcess->readAllStandardOutput(); mOutput.append(arr); } void WeasyPrintPDFConverter::slotReceivedStderr( ) { QByteArray arr = mProcess->readAllStandardError(); mErrors.append( arr ); } void WeasyPrintPDFConverter::weasyPrintFinished( int exitCode, QProcess::ExitStatus stat) { if( mFile.isOpen() ) { mFile.close(); } Q_UNUSED(stat) QApplication::restoreOverrideCursor(); // qDebug () << "PDF Creation Process finished with status " << exitStatus; // qDebug () << "Wrote bytes to the output file: " << mOutputSize; if ( exitCode == 0 ) { QFileInfo fi(mFile.fileName()); if( fi.exists() ) { emit docAvailable( mFile.fileName() ); if( mProcess) { const QString htmlFile = mProcess->arguments().first(); // the file name of the temp rmlfile QFile::remove(htmlFile); // remove the rmlFile } } else { emit converterError(ConvError::TargetFileMissing); } } else { if( mErrors.contains(QLatin1String("No module named Reportlab"))) { emit converterError( ConvError::NoReportLabMod); } else { qDebug() << "Failed: " << mProcess->arguments(); emit converterError( ConvError::UnknownError); } } mProcess->deleteLater(); mProcess = nullptr; mFile.setFileName( QString() ); } kraft-0.97/src/pdfconverter.h000066400000000000000000000061361410616450300162270ustar00rootroot00000000000000/*************************************************************************** pdfconverter.cpp - convert documents to pdf ------------------- begin : March 2020 copyright : (C) 2020 by Klaas Freitag email : kraft@freisturz.de ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef PDFCONVERTER_H #define PDFCONVERTER_H #include #include #include #include #include #include "dbids.h" class PDFConverter : public QObject { Q_OBJECT public: PDFConverter(); enum class ConvError { NoError, SourceFileFail, TrmlToolFail, TargetFileError, NoReportLabMod, TargetFileMissing, UnknownError, WeasyPrintNotFound, PDFMergerError }; virtual void convert(const QString& sourceFile, const QString& outputPath) = 0; QString getErrors() { return mErrors; } /* * Sets the path of the template which can be used as base path to find * stylesheets and images and such. */ void setTemplatePath(const QString& path) { _templatePath = path; } signals: void docAvailable(const QString& fileName); void converterError( ConvError ); protected: QString mErrors; QProcess *mProcess; QFile mFile; QString _templatePath; }; // ==================================================================== class ReportLabPDFConverter: public PDFConverter { Q_OBJECT public: ReportLabPDFConverter(); void convert(const QString& sourceFile, const QString& outputPath) override; private slots: void trml2pdfFinished( int exitCode, QProcess::ExitStatus stat); void slotReceivedStdout(); void slotReceivedStderr(); private: QFile mFile; QDataStream mTargetStream; int mOutputSize; }; // ==================================================================== class WeasyPrintPDFConverter : public PDFConverter { Q_OBJECT public: WeasyPrintPDFConverter(); void convert(const QString& sourceFile, const QString& outputPath) override; private slots: void slotReceivedStdout(); void slotReceivedStderr(); void weasyPrintFinished(int exitCode, QProcess::ExitStatus stat); private: QByteArray mOutput; }; #endif // PDFCONVERTER_H kraft-0.97/src/pics/000077500000000000000000000000001410616450300143055ustar00rootroot00000000000000kraft-0.97/src/pics/16-actions-kraft_alternative.png000066400000000000000000000055441410616450300224120ustar00rootroot00000000000000PNG  IHDRasRGB7MS uiCCPiccxWPOwO C!A$$A F&dqVI .AVQŀ((`@wE@YW;胷޺O֝é_}U}OUDNR*l@/b  eb@ T'5 %Fc$'xnT*GiB&qW+,Br G~;9 )+pWZaN,? .aڷ_#r8^ZYb+<2=.1M7?:g|7V_Ĩ^,|W %kj{-8%H?3P+ Ё P@#`,-pp@,H| r. A 8@-hM< [.L`Ka!2Dd %Hҁ 6d 9AC1JrP!T UAuP t ݀4 }aXևٰ8΂}p\;-x/ D(#q@P$#ۑGZn9 (]%RPQE*ITu5G}Ah AgFt;*z=~` c1EØ6%fbe:X+76VbOa/bS8"N gsƅx\\9׃Mxuo;)A"X ] B+*aH$͉8Nb4:qD%iHat> %C2A%M+b41=171jaW6P`,!@j] tB7;hj,'9#q%Hj@<'ˠV._F@kPcA9T9a?q([[0jM:-d/N:mNu5V`K  񂛆{̳j'NldZ]IfCH+|t,!"m8#i}6zoIENDB`kraft-0.97/src/pics/16-actions-kraft_demand.png000066400000000000000000000055361410616450300213250ustar00rootroot00000000000000PNG  IHDRasRGB7MS uiCCPiccxWPOwO C!A$$A F&dqVI .AVQŀ((`@wE@YW;胷޺O֝é_}U}OUDNR*l@/b  eb@ T'5 %Fc$'xnT*GiB&qW+,Br G~;9 )+pWZaN,? .aڷ_#r8^ZYb+<2=.1M7?:g|7V_Ĩ^,|W %kj{-8%H?3P+ Ё P@#`,-pp@,H| r. A 8@-hM< [.L`Ka!2Dd %Hҁ 6d 9AC1JrP!T UAuP t ݀4 }aXևٰ8΂}p\;-x/ D(#q@P$#ۑGZn9 (]%RPQE*ITu5G}Ah AgFt;*z=~` c1EØ6%fbe:X+76VbOa/bS8"N gsƅx\\9׃Mxuo;)A"X ] B+*aH$͉8Nb4:qD%iHat> %C2A%M+b41=171jaWŇy&1]4`fV1M0 A34PuNlф[A)?gf}&[ $8«3C|'esͽud}w0j4bEjg9Ӹ.pTQ8bȄme KuUYփ3ṧ$1]>mG`I$ pͣ[IENDB`kraft-0.97/src/pics/16-actions-kraft_fulltax.png000066400000000000000000000015131410616450300215430ustar00rootroot00000000000000PNG  IHDR1_sRGBbKGD pHYs  tIME +s IDAT8uMh\eޛ{g&4hmD0V? iE"*6)h\ZT .., ڤ6m&ӌs'H'=}$23\=A (/V!&f$jB2fJa)c;^,6)ouhth:L /}ξԀ 'm`ϭr*jR=Z|Ce*\?xTs3ffY"`!vRx=0Zυqqa$$rwOEHދ˱.p]"{R2*Mn`' $jCS.m TAȚW.kpMn~Ml-][y#?-sK%`a.zodv۝,}iI@v<<dDGQɇl6cJ>o47Ycߦ2VjTZi\>اp;"##`^MF%* Y.c a"z.gV)teS14ShCA@:dp[bA̫Q;O.z(LtXro]wIENDB`kraft-0.97/src/pics/16-actions-kraft_redtax.png000066400000000000000000000015241410616450300213550ustar00rootroot00000000000000PNG  IHDR1_sRGBbKGD pHYs  tIME ,IDAT8mMhTgsd21ŌĎMBG`qaR"h)WڂfaSnZ•RQDKj`E$TJ Yԍ VFI-d Nƙ{]4N~pw*Z+k6kfyK4P(暊ErNߏ:bن-.  V(|׷~Pb'?>tYv!"Jऄw@׆{ҝO[`eRW#៽Sږx>󭢙]QkkT(tDZÈًd ,t,w)%g[(` !,OyټA 2mnjPu܇SG{:-nSf8͆51'#f.' 3<ೠ QUK04vh̍ X)h׿oL,HLDtV3~0!AH("BqZ$Y'/1=wUbzFw7-QY)@G4l{Lrnu'W]۶t,ߵ)8'7 _³WꕉCtIS9lϹbcFVA<H.Ct@. O˫]-!ʤJ0^`mA;nTUęz/cJX2Q{8V"%9:B*:5[jIENDB`kraft-0.97/src/pics/22-actions-kraftdice.png000066400000000000000000000030771410616450300206350ustar00rootroot00000000000000PNG  IHDRĴl;gAMA7tEXtSoftwareAdobe ImageReadyqe<IDATxb?xyy1| g yGܸqΝ;3^ԃ_0 ,hXׯ_,,,***AvvvV"222 .]2:СCI@= @1###WJ _aشiC``ý{߿?(.  QUTTt`ڵ=zEWQokʕ~ , D60xhRY9t_QU8 UȍDx03|}\__ ^ _%K,/,,k?y0PEPp x |U^a p Aىa p! _~cghmm*m8B?  ^kdccc8p CSS=а ^ ||T"###3bAJb@闇@Ï]h;;;'73`df+/>} Ky\L`0U0())1f2D-P&t70L_ $" ? P@Y3DEA÷oXX<@?Af?A}-Css;0޾} 0emcؼy?0H8LoaHLf ?A d@.`{>8̰q1`bx=o. &. _?~3  %Y>II1`!_  103?vȱ /_Fw~AP s1@] Y; JJr` @dPCtT00'5˖_ ) `,nŋ{g̘lXd?1\z gN_Z䀿0?8vX=rVAS7=S/{wKu33ݵ[F}@(1lْǷ̄hon>fa&;6FV7^08~϶={۷o?@(etǺ%%%>|ˇ?|;?};mX?##㓚1zV+ @Ja a0Laaaaw>/?qb7WB ʣX98  x @( #ǎ;/ߺus70@e%  aD qEݟ?%(*}IENDB`kraft-0.97/src/pics/CMakeLists.txt000066400000000000000000000005661410616450300170540ustar00rootroot00000000000000add_subdirectory(global) ########### install files ############### install(FILES kraftapp_logo.png DESTINATION ${DATA_INSTALL_DIR}/kraft/pics) ecm_install_icons( ICONS 16-actions-kraft_alternative.png 16-actions-kraft_demand.png 16-actions-kraft_fulltax.png 16-actions-kraft_notax.png 16-actions-kraft_redtax.png 22-actions-kraftdice.png DESTINATION share/icons) kraft-0.97/src/pics/Calendar_page.png000066400000000000000000000051161410616450300175230ustar00rootroot00000000000000PNG  IHDR|/YbKGD pHYs B(xtIME &8J5 IDATxil\;2 ;P4vҬ%$JReQ"Q[Z?B !PC)*QQR(MR'Gib; Όcgc$KHw|sB!B!B!"-0ee:\Y_}GKz{ {+ɓ' Z88YNek|{a=c:6nLvuuUo/?ld>^42ۮau`]3{]2$մ6'W$;4=Jh@L[k!61,XSn]V"9:TGi!n7zNjŲ Ԏ ̬p6#Ѳ0BiRgXf;V(!͙eN AQ &tuڊ._)N-Ә,6kJwŻha+I5 eN7gkIKp!B… .$\Hp1nS0`j<l\oFɒo]JJ GȈ>_qґ6)v9x }3J3r#_\ _2԰im4 0CCc*!+˺ քaM9sQwM&/$ k\aJHsvv{Np\9篜!' _n[O~]S-<;71:,rۇcK\+N3NBX '`\27WFi$rJwbۇ&,0μ‡W g@c/<OX)Vu,xoyc-&5hm3(K`\e# ]+%w@AI}~STuu,&DL c,e>Nr__:MH\v gpF̝KQ&x[Ȝ@7@A+ _❏TVP9%'Z!v3ps(DS ӧIVv6PJd^^:Orr9IF,FM_I:3!7wo>u| e$Y\CME_vXy颭\d7ɉ3 >F~5t RPx:u o)K3`syPT^R;2aMH]!o/lpíUR6w$)N­zБ."-pC.=v6DT)x a4pUxpUi$ܦ O) עMh&RlJWkJ-Ӕ^mѦm" ׶Lrv>P6Aot܋ .$\Hp!B… .$\Hp .$\Hp!B… .$\Hp W $\Hp!B… .$\Hp!B%\Hp!B… .$\Hp!B…Kp!B… .$\Hp!si* eYmhhhPH-G,R6ҀH$ kooW6Ҁ`0c bJGjF[gg?y3JIjkmm}:fΝH$ܬ(Pf j|o޼3&5yz[ZZ^'m'ݭX[] Z[n̞=#G,AfDʕ+}V aÆnK=ZWW}e˖>7.mp8|2MsҥKi7=zџ}ٳmmm3N͋-P ՉZ]]ݻ>x/=z^:0 e3yW6mz=wU~5TTTܽv㝝Z%)pzO͚5gS5x*?|饗zLT۷,Xp0??vʧ-^m۶i8LӴvYjՑ'ǻy0=V[[{݊+YYZ_K(04662IQXZZ.[_TTz2 CoNp8 Ѿ`WWWkǩSNODlAIENDB`kraft-0.97/src/pics/README.icons000066400000000000000000000001211410616450300162710ustar00rootroot00000000000000Tabler icons MIT licensed icons from https://iconify.design/icon-sets/tabler/ kraft-0.97/src/pics/book-open.svg000066400000000000000000000011011410616450300167100ustar00rootroot00000000000000 kraft-0.97/src/pics/cloud-up.svg000066400000000000000000000010451410616450300165560ustar00rootroot00000000000000 kraft-0.97/src/pics/cloud.svg000066400000000000000000000007341410616450300161400ustar00rootroot00000000000000 kraft-0.97/src/pics/document-clock.svg000066400000000000000000000011071410616450300177340ustar00rootroot00000000000000 kraft-0.97/src/pics/document-search.svg000066400000000000000000000011151410616450300201050ustar00rootroot00000000000000 kraft-0.97/src/pics/document.svg000066400000000000000000000011231410616450300166410ustar00rootroot00000000000000 kraft-0.97/src/pics/folders.svg000066400000000000000000000011101410616450300164550ustar00rootroot00000000000000 kraft-0.97/src/pics/global/000077500000000000000000000000001410616450300155455ustar00rootroot00000000000000kraft-0.97/src/pics/global/16-apps-kraft.png000066400000000000000000000005131410616450300205460ustar00rootroot00000000000000PNG  IHDRasRGBbKGD pHYs  tIME 8-IDAT8˥=@?PЈS g#c $4ظ:b%y;? ydb0;_S$|4k`4ҌJ=g9`,k:Y}.k:`|( 3)IhpV8dlm5iS'1 }'E I K9I[J{DҖ ;IENDB`kraft-0.97/src/pics/global/32-apps-kraft.png000066400000000000000000000021431410616450300205450ustar00rootroot00000000000000PNG  IHDR szzsRGBbKGD pHYs  tIME BaIDATXŗMh\U3yg&iƚ$f,;m A QE1j]bHE[$ d#!B! N~jJ7yH$̅˅={s(JtB(}7.3Y=ΌӢJ n- "EirZ8_v@0?sw J,_bɧ'R`9u9HtRokr cqB98xuDU> Gv13A[/y,9א`6`O "ڈXhqWgm W݈MN,3.sn S5@BAѳ^Wf֖N cKԌ);G4$එ>rd ̞<3Iq!hXR6z/Ii౟RΓ3k.2I>PߩELizfnc#d6RJ}5'D$]Pɇم^ n|!{BqǢef J3]V-b>.RU)"$ yyѹO^~<D^(@pXDb7b-_<&Đ"6 ;X5עVU}]jDDlg/rjIݼh|VEֶ^7Fz+t { :nbt=G -smhܑ3 lgl.^UV)%Z^(m35|P}A7}|Pʚ@TLoF /ޡ̱/xѢTD:3yy+^ )Ƣ*PPf/!lbњi0^c_C8+7s:=}HIFwy) ē{ݔRpU?#7 z&okadHcIENDB`kraft-0.97/src/pics/global/48-apps-kraft.png000066400000000000000000000036461410616450300205650ustar00rootroot00000000000000PNG  IHDR00` gAMAOX2tEXtSoftwareAdobe ImageReadyqe<PLTE !!,,-)(+('*(&)   M669..0<''( ^__$$$/00 ̣򸹹TTUc{HCBEGGHώggi.-/_aa123JSC//2##$"% klnNOOBCCmmotuuc89;--0fff|Lʮy{{{{}z|{ ;<>ۙ FGG//0蟠-,/IJJ-./;;>=P%vwx679UWWcdd###557556242_uG())kkk89888:**,GHJHHHGHHddf=>@eefjkkghhghiabcacc^^_224335TUU$%&:: ~BzBB<޿^^^%xmr34:Rц@U>)K C@5P nBs_zyI}4M\0hQ4гrsjw3羾0>Y 6h 75ɛ:`l'ᡤdn~ӇA0T@ (`Wn;X9BCD, >j6q ]+& eڎ_$dt(AKiX SĦ@a-LapA*Pȟo XH H#P4` /@,NPl#iД^z+@M _dgA@8ks ɅDi+_$?5Ǯ3k@Uf pxZ|jJ`XSP&;%e|| i*Ń:[jF:GE`65;vBf$M2c5IX Rw`MbԺЕ+F*Z\ ٤! , .QPN\dn3MpwCR+Dē%^F$3]b:~5Y=4sJ-,fWJ!"zFX! $EKu J7,~"Rj5{P[|r*(ʷϵ߇RgX]aum$1ր\>J4Ξ<-$|ַ̊yX(2FJkHu0c*@Nv17\,@HU5bZ6,? >rov2Fh@cX*n <ȫz1מ!On6VM Ԍ=;O(%_?Rw2FVo 1ND z  ƺhl@)5'm}%x'x @DO_۷*TLmNwG4~RL_)RDaerBuK۽< #el%3Ba îMgËbg$He"宛r߳bpʫ FZ]Ga9"RaVݒ8񤈼+ir5T|lx53u%Aq', ޾?'9qsG)Z6erFly&@r u(lUg~*x<% image/svg+xml kraft-0.97/src/pics/kraft.qrc000066400000000000000000000007711410616450300161300ustar00rootroot00000000000000 Calendar_page.png postit.png kraft_customer.png book-open.svg cloud.svg cloud-up.svg document-clock.svg document-search.svg document.svg folders.svg puzzlepart.svg global/32-apps-kraft.png global/kraft_small_arm.png kraft-0.97/src/pics/kraft_customer.png000066400000000000000000000114401410616450300200430ustar00rootroot00000000000000PNG  IHDR00WtEXtSoftwareAdobe ImageReadyqe<IDATxZi]Y~wx;;N8iRph46 TЖPAS "AT@-E~T]( ޔiK8x6sG 1f sMpw<ව ꋏc~]` ю8QSuyh=R[=3`;6LY< W3 }<<_4d, ~?e^!]hi;2Fɑ4Lv729/֔,{a<'{;l+dz}:sdǂ0XVV+ô_mdO XӴˤ]-)3U`Q?+eOM^4%@7ip9<ݹGN{_ ĽKjٖ-"K;ô]:!ycFXyGEᑇnŁr o=VTݿ^z= ɛb$ftc?:d7l?sMC?KLDy׆\HBE(v:]ty;?{|6[q YSs]سK.Y• j6i92ꦇ=b lu+*dLF-TDwA:9TVѢ#ׁ{M bdD8_ysWuA\^lm9T6T6^=҉/:4OV{Ư i Aԏ&2@u XFͶD9 sþad6q~zuJIE# dFرs|R Kg:_AU& l07%Kz}|\]vhP0 oFʏ"td_;KK l7>V*-,5 窛ݡ8Jwt ˎ򐉁9Cm:)^zfBρ/kvM`6,ǁC5<ze(+ aY$2ܨ t]3:EL2K}epw+z>1NAo9t V]?ҷ/4;v abba26p@ NEq8y-7XD1Z (3AR ڍv>|^$F$6L]3{ʥ܁ geF th"y!f*X}w '=YXvqۑ]8zx'cn`!ɖn (-~$>U7K*hWMJmt5 e rb)9X 'jBR*#v,^qxiEJؐfK8VEx,J l.r Ik$mIB֙UmCϹkdߊ$8 y4  :B ZBu ( NT7XW]U 0`|8b0 U3f4 J[v|C{K)0(ӑ׽M1q1o,/1384sIKx)Y&>9<p| ftזt |Hō(1.,n/ [~tjT^^ax8԰uOWuF'عmxj~gq2;i:mvhA&>c<с\.3e0dK5 WdND,k}F?YF[N%CFet#)5|?jy5U 3K8 4:8h1CN_Bg=U<9d{x<5F,t{L6sbe"u`3ņsnFWc"vSp9#) n–ϠRbtg|Wkx1ߦ,a{"e%ք\L=V*f9Q9hӡ@#e gbqE|6-ߏ*f)|I+'!f$Rwގѹ#_{fm]3BkVkr54dRgFulx!x<[^_|_Bg #Mv-d9 RpJ4 =q\=C]{|-1G cR0Xkx2W~X Y\xMЉ<(a,[}oib03zTC”c2"wrɏ@E+D(g#2?s;8kP퀑v8#C>!Cٗ x A~/$9fh}YKae*2c *i! ~< \3~K}yTIĖ7?73ͫ/)l qLOa3į+QŽq%(Ρ,Xܨh$Xo'oY:y`M7+w,Nݍ0nB7Iﻟ`xI5'$^{9ŻIB/TqH&MsIL|s0 34ʵDqWQaYCXrz_1>gkfQL$0_G'7XFpMXWq.t}{OlYC1ìW;ǵlN89JaR* &GʸkrÅTrTƔSrHt,,.+Qcj$*a/=S.h=p>8)n,CF ohቸĕ_۞$ ö09>ZRer$\2+MsjVP6Ռ"QGQB |ȽX;x/^\_?}_C @/lp\/:fH9Rh4=>Dof⻭\QJ>5YYp]qF1iI Ѧ9 LyeҒ I$~Ze<<6!$NHH7T$F"L% <`&'$S7r-5ic;K"QP+xK[mjI$,xRݓj2x D}A&b]b qh<hG A3DyME?z],bHL !wv$-(Lx=Q-aj֦:{Gл6>RCD9q3fRž4yD Wg{&۝q$5~7WE^V7HG_Qճ-beOo,t,JZKk-it=xtSuA4g c {O_Z+FD;ݒ+YMo]][mf;.SE|@E[2HhEH"wVd$ GIK1իu]{ĺT&x#Qa-U/peYyB^ȴ;pzSW>0D`;@0cYdSR#&ힰMJ->SoE| 0ۉ%mϏ~/G|2S7L-+EO32ENm2$-HcV%V4A8J]Cz?s?|pZ3:arxI/$p#K P·3 F8`&-!eDUE%tk><}/3o` V7nIENDB`kraft-0.97/src/pics/kraftapp_logo.png000066400000000000000000000062041410616450300176450ustar00rootroot00000000000000PNG  IHDR@@iqsRGBbKGD pHYs  tIME^H?tEXtCommentCreated with GIMPW IDATxkpuww |ShZX(Hd\MH6VWL>H'mY~LM'i&Nfq?$r+GIc%N슖&)E@I{o>HHb]Clr`yPqOJ9|M5Kh[;ZVr=P[W77R&&xz˗bסEiDb<l@<g@ >W(\J$C5lyr+^16=ѦS4>R}Cog^dڵ77 e&V5t|oU`ddN s^?:a}'GċPr9}H)o>xNNb&uTL;s؝_H*d&p@:n &I~ =84c Mry&3'lwWU(< Hǁ2+@~7dFl}76oE }6O4pEo!xEui-۶{}!c9ܳr R(nS{o%\G/( lJ}} q߶󶓡c%hqiB'Xĺu|rNP^^N(9x9۲ _NJN~r?ՑBm-[f鲥({~IS+¬ctttqv8~SW%oҵ4fymΝ=GyE_qֶSYYoAeDЃ򋟒,r%E_W4~?b}!;h Ho>;!>vno9S_rPB(Er|mo*(Z!/\=d D+[Zu#_~s^hR +c01m `vJ?mp ]{_!ɿ{,]zmo^&Q(4]DZmc9bT4|*- 5՗Lww7={8\䁝s^楃.HGbgljLM!XI\tcG5qe5uFk[[n~kw羚)[4 ߏmYdLgܕҹȂ奎M/J2XS8 pر<,>z8Jxk#lfӖb1zg0:?ৱ7o!HP@DzBJLMʲ.F}Сe˖ " jjjĽ?=X aMsKneuR*$40<$JMrYJ'@@ȥl݃W_0. e)A:B*oV^@扡0ժkaq>+C8qD:uV:mD%Ɉn@*ہ*ځҒR7 g `0Hee(D:ҽLxvM)1SO aRTn\^QN"=t]婒 xɒw'#lF}?s.&^)GV$rr9_So/N^Tmo@09C+p0@'%~q+oO,[`yPB|6SǞxu5WJRU9zJjcZB8[)++ˍ=pL&lG;;m@X: χǀ?0TG"˲XR (˲{׆%qF.^$JJ'p*!%H۶IML*XabXNk޾d"LMM ,]lln"$9ũHH? TUWں:oں:}N06y`y>G˔IENDB`kraft-0.97/src/pics/postit.png000066400000000000000000001746521410616450300163540ustar00rootroot00000000000000PNG  IHDRwzTXtRaw profile type exifxڥk$7r:O3@Y8,$p?}_=\^s7| χ"o%V|/wؾ7F)|nw(޹f\sZ~~d,5GoV;?賱o[MF~~.)kLYF wb o#dLwoKn7?, a _ϿbY?h~[t{w|V7re%p c?t4e헟YHoa b9h|qW׈E+)YFwj~Ļ^Kx~En`߹WKB>=PˈHM-oo`!,o ~~%[:q]'ǂ5PޅɄD| h![L9N"JIƜR%6$v|»6y$%U)5 VXn`hTr)+2jّaZKX5fFK-jZoǞ`IzcpȣwfiYf6s,*.[m5viǻnm=N8@SܩN; n[v?Q/R~Ew> (fD,@MQ1-9HV$bR>!~jq7rNF͝EԶzd'ߟ6Ιwƺ]<~4L?nlOW dD;9sm[(+po W\✽^k_ i'4kjU9r+)1vet[ŵ*߉Jl᱀>uI6W v5ѹC0ۿ@kr$")Ov,FZH˚=rĻ@.rjִ xH^.S 4]|om۝=ȪcX㦱Н_A[ֽ(m_g2qޜU*Iqn n/βv ] Rmtݛ?a1v5S㚿0k5J|,Y iG'Ci@ |-  iV'XjsL$h6k,b$|kc&i6zT$Mnzs"4!zvy<(@4أ2 ;V]Y0Xly)Xx3KA>P&ea!3/ lf{-A>;S ^MlT7֟Ǔ&P$ku|Xz*-[pւ8VG63TW̨e;W{`Wwu@=یLzuU22g`cT 4С T׋r MԈvW:AX5y9Jm}2#ѱEy@F+ Vר-%e˩iHQȠy甎ʽRި2%)sڏ#76GQ-?QXZcF& [G9h5  ua)A7:oy8v2mD>v ʟb v qha&J*a` ۃaVe dZۜ?edaƒJbSw# 5N&BVlٛ?6]*Y+mHbIj-Po ۡ)%Ejl6-BsȌvXҦ\9)+RH6Q^H9TBZ[ JP0vAkb<3) / EұsU GTy"*T6?2]#vd@%r ᅶZ5^ą (6JG ʼ=A_^%vjN juL@a!#y=ɴZKL%RaX#qxH2%]d}E {o(ý*k?5coɞTE> ؙAGi(.T"tN_LD6.'xYp2؄kb'Mìƻ ^Ml.5}gK-Ӻ8  R+B/Qcd~%rE`weEvGX옋z%%:d I 㛑⺈ %x_Bx4sRTvP#)AEzQ@H d8ZSU)S+j6)Q̅;A}¡R3L`=b맬w}Hz"4P1ddjMƅJ[EzDg C9"0p+gq2xxcj na!(#ےJvn;V ?ڔHl T* _""gXԡ'B%8xP(ViH+ad0kPaz`X'Al9P Q"͍3 $P" 8ROwdQ@VȎԇp$5.BI4>@2"}RBmc̽QF>rfrBhf *{PГj&RHadO%~ M$ 858^S)UI yT/G3mL87V׌M, <)J0aH+x}i} lp )tGVPN-˛Uj`qS2TŶ,J`)f|xzFWt)Yi#PU=ETk[$ #aZ':/R6,\ant$EY?8= l2&jp{ n햺9צ.Ύ*cA2#oE!V*~(K-2~CEׁ*Ձ#仡:jtиPLU%ߺvj.M h(5nEC1cLu[y6 ,~SGF FdM4`Fʛ$ ˬQ,p.pa&;]`P{jBZpm)CYE(1ɹ'<0꧲͊:1Zd5*Bd L#N9 &\EDƄif|4jaVaح-mqU ‡ww:;/7Pũ;D)܃xua(NZ 9+i2`&!PPW @&Fы.Ң$"p,듗ڛԳj~#:6$7a隝Eo,?jH@A=(a똅O8lfA\қd7Cc^sRWW>{0$cSAYY?gLNǪRѧ棗|`aк+|YAB.-h1i\tv&:mG<"wEߒwz@Bi po23݊yFwKa E|p̝R=p;E37(eؓ𴴅 dK :( wȀo@.!sw pLoұOc8>%{tG1"PpKk7 VF- cuȣ<jPdO"$Mm2&!RZlTQdWt ^W)IB>U *<@}0hsª Vَ\ʲtN@٭;qPLPdyED(i"8*sوuAV=KU9ԥtQjvz%'PeH:עAlw :ȑԬXa 8Sׄ‚E~w;lܖ,l8 `<')׮D5G2"Hyc7 j!DRI#:}_t[{j<VDL=:Gpnz"JXǹPºfp}pC9~^Uq? %KMcxK3*zʸt{n(dR U^qR>^Ǟr7EߢD`n Ǫ݉mtsuٮ8HYA8rI9mSpԀm PDOb,đCuqt^JO,cK!t$FUp<.*M+c2O?wHz"R,l6AV,u)t)q \Йzv' ]H#8ʥb ׊w,?MXJ \A^I&ZJ4{8._;|6HsQ=)RMz>}[K_* aAOǂH)?BKXo0%VT[0\6l[Mu0P;Z,c0_~ZBg8aA^\m Mpu czԤx$ң088܅([7bl/Nd! PƔE :2}7:n #%u<:nz]ב L+ BQr66w/=6"6Ÿ$5O5)𾂚:" @oQbId+КK'Oc6tACԙBrWL:15v|YbĨf Q3Hyw +OCՊB_DJ밖0j8HlL8<_}^ͦmwݫ^Eg%FMUVcĎcX0XC=KFǘM@1Z1KPS@d{,nL[ЖAz>`@-悉fܞ!ϕ&tPl7@҅ ;F>CwoVK@R Xs~Be7H^ujrzy>:{*Zuu{xȱ-g"{B$)&)jufSHIO:UNY Q'cݺmVԝD\:-BW츍 *1`iPy6$ ﭸT$nTd ĤHM]tyP`(JdI%Eg]#l1EL@n2 ߯4JQHG׹+ѰluW"JG iEVR!mԋ#GCXk#9׉TvF,G.^.z0ϳHr%zf([i Xi@=xʛbYO|N߳@r̦g(H?x;r}}N'9M+i+@/5o3Ȍ43nzStp]3 Fx$v NQ|PvL}[k?17 .y>z"ğ'B NtNԑ=co'Iɀ"U{Uz4Xx[%%8٦fFTNaL٫Cŵ_] 68sӂNӳ\w/^L!!$;:dc$P \> jj,٨tt_捍Ev=Qӈo=^2鑥к\V#ANPS`xĀřq _ ĕw*DXuDPJV*ǜFð؛[zj Xu%W86Qx* w66|YMAJ{StO~8!/Ek%1EI(-x+W=osԎC@1]=J`ꑲWqHa|9g5BVP.Z/*BQfJgiCCPICC profilex}=H@_S* ␡:YMP Vh/hҐ8 ?.κ: "ƃ~{2Ӭ1@m3c,cN_.Ƴ9ԜŀH< & ڴ VUsQ.Hu71өyXhcYԈ'SXY+WYuCH`K BA%a#FN}_"B9Pnĸ/1 t|;N>Wz_ӟZZo-M.w'C6eW y) k^o}>i*y#^yww{oiHr_sbKGD pHYs  tIME ;+ tEXtCommentCreated with GIMPW IDATx]%Wv[kWѷoM45hFZDz1a@@V;8yJd#J?x`'?DrxF 5 )rfvqN+{]lΌ"sԩSZl{=۞m϶g۳l{=۞m϶g۳l{=۞m϶g۳l{=۞m϶g۳l{=۞m϶g۳l7cwQ/k_W^E^x^OWyuI_?-wxw 㾿H_^^}^Ç^/w. /LO_|i}yon͍.Ƚ_n|y`۾ 6(pmpm0^ o߆v{r7/aGG9"px Vpq=w:_b;ȷ'o?p?"2;ֻlWw<>k58>xq͗+ ^{{ا_ ݹܻGux}~"&{~g1Ly Wi^8>F~=k/Ge_ϣ+j!}Rcw}' מo䧁7\Bo>~sK糐mv#;}!vx=L{y .b9pvɊK{7/#p,_ǍQ;)R,^&+.hؕ=d;F9=ؿԒV~嘏+ ia22?VN?mkpxE>n"'.Kqmv>uxw [iۏ{]G؍F̎Az6[)ބ2ͽ/x^}5r.k_Ww13^qO4կb7ncd˖7dX$k_xBlZ7O f~8:7>X~'|]p"12g| gf(I+x+̐<<7G}x|m~zr{ ·ߛ/ޱ[=ӹDp݃}dO<^üW.s-OG_ǮAre9\SO\.rt4 N0@帇G٥޴>Vs>%=^vwyyߏܨء_"@!rr {G;•|s͑ iz3CZMdyDC8ҽ ж.p6 *2WDSWd&6yх^ * B;#HBB#hRT#FH6B@ѐ4A$' !A ̾&&?Y{o{Klo;<_'2Qy N ] =^.T@Ӈ0'kA7!-;ʖ gcyy+rEvΑهq=0 {d(,wk/ .WOgA={w ]{[#]{?spi;"]`ݩٲL~O`}ζf!b'7a9Cmrg+xzz9C?+ B\}ЦGn@XZ aih\m& -BD\U XbI(Y=O(44 !!.VB Y~%#Z5?u}ئD5UH xف>r%_!۶o~nvOOclwv/z#ǕU({$[i9|-z_z+[A+niv춴ݙ fF[ҮlYwfA%X F@"j |&HL; gFЄE HJhHpJ $!BZeK-kTG[+T1!tkJKL12'F?ANjv6d^z݂W%KcSa7hZ}Rk-}L8n"Ӎ?)|!;d'+н]і/ݒX"g]-НeEP\!=r1qAkUo<ky}!Ƶkn۝ʭ[hzPCΐkW}@9CCU9AcUU""mඃ~ܾGAZEdc(Hb- =$$ &ɟ F א? ۰}O"s(W1g̉"kbY^-$E-+q~Q\IĕD,[yLѹdb?+6=Jъu`U`(_[qͺciE)c6$YBQaiDs5#}'7oYYm`рs6zǎ'pm>sgaڟ38;> &cZ ǀ,d܊}! V;_cE/=4ѹ9\6 C5M|("!ҴYD](ٝLh -[Jrܬ1$%̄ !qxz k\ 㡋Uڭ!A1 <7N)y`؟kK?sAz)#w;Yb%HK4AHA$!?%40KVaI=\,P)G +VkbmL@rbQI* EYj8n*$qQ>y% ,y3G}]"=;+|4|׮KDvN jiWY\.6=7n̮oh+2 ZF#2AH(]FS: J-M!d!j ӆ f*"HRU4B(h#H %4A/XU, 0R0 )V#Ը E^酪RW:h}!HTg9k[?9`yx;:&.'o+g(|6tfQĕqQ[P'v^mq'.|OES){'b% c* FE7Cc6R HX.wr~\=w/c7b2`]iJ =\g;!?\G_D>'/('${ aU8 >H |;/^ҋT)hi*;fjHE~]`*N`T@ɉkFc8BCyHfXF3/P |ېZs/2Rm1RlpF7}I!YTrXBJ@yÚIcpOMC?_F Ld1\|w_\޽{p1 oE, #t]jn&x RP^ X4&iD]$=fٺ&$h4@JeFj\I业-d'GnC$D_Q3QGmH]  b!G@_?C>vߡ=[s9>`S`$GT+n՟y8ԐC^E+>Y. D|dhk&01L*bNiB=񧲮bf[$xk:gxJ8f2ω &==`]S.y,c,~kir^r.ȓp !.>DV+t w~! >Qu$ih ěrJ^?V*UX\5B瞆 d/ylo<|JiTsPt!b!fPӌ Ϩ#yi:'Sp. 7?fG,4WmAyT>P{+8`5W5E+=#eP,q;.E3W߶#*H+$+S <9TJRe av&|_ue=O5 ~xqQ #i}#FDAzQÔGiMErI[@jh\0ј~~=ʻӰt1,yEih ԩS-Z~k1@6[cmrZW"FҘO _#H̠Ydysk`oC!BȨ4?sK gk" -/!I3$bӞ43=[?;(6VrE$;^i4,8ԥ65XhwdIeX5}[.l^@eT0lϿk`";ַwsmםݻ+._p*MOMxs,ȕ@P+BmR#TY-\3e&+SRtҘ-T]خ,6`[RTKYaLy3!HXc]p@#J6y$)DA[%x(Kf[GkD~O Ox~8/0~OϊҮ8sgܘ}כD"jiJTFe `q45nx%ÍMJʖ!FɎ h֛k3Ye(X*ףl9e|G‡?RB$:5$]Hi͏]Oѐ4jv'{ /@?yw` .&z%x-N旾~jUT\!`oxh2~=HJ9~>1DkjZAuQULs+{dSaCW$3\ '6aJUKQe KG>gfmܷN>\$%˃p=E'Gz6HEܹf9ҐhWipHkXzLUh^`BW~ɕA,^2 (MqKŸ kA gȒ[u BB'^)r1X.R<2ԝPe$2[\0*g+B3GΫ4@3t'㷡;z{̮say= ~Ra~_oCT!|g9#gDJ`ŀgSs I BYCvBWGpЈY:z-c@y*+ErF*3!Y!f&'D??S/ g-"ߏt{~Md{e8N"J h6ĉF6T(LT;N+UڷS'.LjEvX)~MdJ14+ +4pZZuwB Z Xj:pE3X֦ B* }l 7)+fm8pAw2395c'4p~G>ƎyO \'d]Ufl5G+_f_6 IDATWwxf٦V_4xL) aHIkJ J˶70].>-E*g/dcjţ-,~ iٷ^]slE׹; '34~-ȩ@)PI`Rf\kw N0Ju;`ע .Ua9%$\UrU9dXh"Ҍ!)9*#:~%(Ʉ T bu Vkf ZN~}?b ,B?lށ>=N %!%X"M3Ǿ?3t!Ƶ(k(U;x9бg(*в\܃+>m2edhu32dK=)5HS/ũ),`ֵR41ſ9'O]߄4~nQLe“Һ qfcX 2_Фi&TBK1YMDJ#UgIfdpQoFŕR&yR%*)hUZ?aдj@fu I")Bj31Mpֲ(H3Ag2U% zGwEy1x~Ye'~Gen|cXrq WYaĕ M1rIkVm;ރ8ƙʣz(4'BaDs+\3XEbHh3k3KNlX{Eu^hn $&5X+C$ #"_e%in8#Eԡre\aI,6rަ壏th[2%Ɋ3l[ת]2#%&ٚzKOK'F#WJO@urӬN+8Rv6c!z<^"uW/ƢN?YP^?fy;?zkw~iY>GpNcA @FRϭO󣭽{{߻~u=9'<^NG8Һ 0qL^aXo-@)GKM>3ZmSHP5W?g^"DŽ8TQ}c))R4{궪Ҫu7{1 =3ˈk ǯF$R;ݼ +^œ5Idr)$F,MG:y%5Z=긒9p^MM1w.1-> G<]2gQZhtZƁ)^i< E. Yi_9@yDT zm *8Gɮ4#b7H4F(*amצ\*u%~1&1]҅k?nhr6ÅdSm68IbĤtn~!SDa!ce4^%x0UEoC:9qoS4Ю#WhKڛ~W89e 4](@|>_%Ip)t]٭[4慰%(c'1kV͡ȳ0C˅d=a%roy|@a7iig!%9R1CQx3|a < VgJC+ 1aeb 0]E=7O >Q)(tȬjYoH RDZr3R%Ĺ$ҞM_En-SRRM̍tFRNj:I<ax̜WgqNxgGZkVI`-@r˫9'cMĊ Ib'Q;_ww'0nʒ4١Sh{t={D{!9m,LMc bN_ҔZ*' l&5=UTrŬ2N`Ć693cR?(д8cO_W9V0^WUsȎJwn}%i{7"/_E%]#Q{P rC\]NLږ"W)rU\R$dM u(vb;ٖ^-"d`Ӧ*VҐ4Ś\6rms3.&NR#4'LBin[bVa7q8H͜8MCJ54yZӇ[r9 iMnXI2bvwn_{~luNX5Mѭ=.%^CkYzI Z7-rƑPY-:+;H, %W66BnU]Bd{(I(O[)s :6WR*p#cɄ?sus#X^Cөf!2,kd9r|ucRgfڈ^:W ب-8BvShPG5ƊVU-[:[r!o^F%aÈܠTtԢdLju+efy`QWqos2{nIۂ jПv;9ϳs>sx>hB5a{KH`!m;XX\1T`Zaj^# U#%8:p7JvKb_tD#,cF!Qz] KO0z,@s6)C,]3|$I9GȬcSEB17g'%Za$A!7`0nqepw^H^|LF*fM}8jː'`SؚqN5{E2-t&DR vJ-re0(/b@ OP٦M2Ѕk`_ݧ?z}kf]OX).~2ߥJأm[J7$mF)dL| j)}Z:]*屹&Q22'n:,VewLOx{@qz7UrU VBU?CH>P cYN'=;c'wN+r["){U'h[BIjD*dǸR:YF YRR]F'+ u_Ћ QB:tb)3霏eo+2 sR=025$d=)c*<5Sّ!֧qz1kP9GV+,b O'ӨC @7mzBC_HsuL4y+%В < VBt Zɾm̼G$fYBÀ]gziٳk_%xE;ڑ:t˿(qJ7i@9 } d)lU^ 9Śo'rl&9H+'u@ϒ,.YprwXl06nZBW, Ah˲kX-g%222<,+.tHE8ZђtcV0a8FuB.rᔔR/K"rVk+caB\ic`U.$B BVռe5kY-|N auPP]l+22MHRW&lPl>qsb^B=|B'/I0F T&͞zaX*u4YmI ¯*`BhX,N{}sk[T1:&,^)qZJQdݨ%] %Jk1rB wRndW6 x>6ӐKo>+ 8F"tǨ*hԊx^;\øw82!͙AlE,5з-e6%c ԏMC z>c|gj>cX9mB\R%mׇU+B%౯U@;k[-CEwi@Eʡ҄4hwFrJ12G1u5g 0)ڔYn/pTl54${6g!w,Iy,N:o2U"Ѻ`=QK[keܠ\VP<G!JJUaTc6 W ѻu/hي DdViF`[= Esy$ZX0T0I qAܥpZo[cV3'ksf>+J!dBV;s9.9gق4˝٬mr9 I dc /d{18 (GlyJ٨̅KÇl7إcfxLc S|):Z#  c2%y$VV`q} M`Xq=庨@<|G kԑz{pk .G{||gw41qx#UvA;[Kֻ;Β|˜ !nD>zzZS )#$%0VeޞRJ(Ira.)NN&!$.buasÛ -TΆbiь=%&a p3:fK4H8C|x'y{qԨG|SxBHr1 k zRF'uK $<"ыi\*wU+nyuNsZnۑȊH쒷6lfX|&wOpvG{]u4JX2T 8k9rzd}enoz~w$.Ħ#l w͖hZ;g(\rٚ+ީL|TM1B('kٶL\B-VHY$z->Flt_{Skϣ޹MΪI./#3+ >3Bu3TOnI&\L;Q咂4#I7hD4HkQLJΰ:4`4Tvb !2Z7'*tbܤ[2y֮uZJơ(PW LK's%um۱wrv$hIq[МhV+5!h4TRŜ4_/; eKGܥח3|Fl[bNZ \N=Sӵuo %{kfគCBSSP;'cg-d~lmr~!2aZrF̤%ӒnIbXM Gy \s 9&dy9y߸|#1r͌sVFҭ sR_(]miجoxFemњ}92봔shZ5=֡pmB[qXlc^8\*[וBY)x]b;9AFYDZX'<@NL[9#f9i1?]syGքGq6Cf\a,|͡I*n &SdեY_sqר\q,*iBϘ!jhƾSzi3;kJ%u ;'X3UnC0$Hf 2ZHvwX` ހ/@]*v{r ,44Ly=!l?FpL)ZeOEޙekNC.BUB /ZbmF.k^8PoP},*3EC-u3 [4y! PCJa푚^;͍m3)̿´q,Wiqo^t g][n,~0&DZfE߆\6kHZ ]-ʽQa4y(otwV#'%xȖ9&3]"~UuGlz-UmGvEJBzhڱZBi@/ݐ;<ʵ4]󙏅s\GV~^)id}ڒ=aOŋyotR_g2j)ֹ eTo[[WQGNO)Qׇ6j,fTC9lΰqCqb71 )f6])dz+Xs!)^qI|<$lvS\ϭkr ֏MxF04^r/cٲT*7=>ϙTt)y-lMj:RI+̄)Yu|6r]̤&3͸%ZKe @1F> Uo&xُ.s+6^&&>m}3QJEh͑M|]CŔ̩buIF*G6E ldbY'GCEې2`>Oƙ6cKWsKc S$6њ5 _A#;y sjqj9tc[BE4Si^Ixw/d Bⱆϱ5GOw sw JfH^sc뵇%<v?(O 7nJ4#Vcq7ʐ9RpCz4L ѻFp„{VNRVKP=ymqh,c$o@[\$d&NRXSSKEQC0 X] $\e1tx:|*K&[ҜX`=Lf2Ve(9IXC!@e# bx"d {1S$+}6#~'B yߐ; xo?ܻrls0!}iǪpi!٘Bݤ8#4NC{h91V,topƧd >!U A CJX jJE>̖UGQ IS+vAX5;4z kWy|[%nF8yvYoEy4H7mbEF3"`uc=jcv IDAT_΄񢧘3Zt6ħܑr {E6mn$DH{r9z ϭ2{}^ 24U\=9 #6J̧%)FɌd3JJr е A:kx)=BK%1’o0 b 4PԂ+ 3z$mRWWS6 b!Sxc)=E?Cڜe UI0xXNwyaoOfV0_S;fWx>}s+eD\ԀXc 0NCib4gb_kZ#Fj{{ۓtz 5b6g(YefأG9$>X##^;i Uu3xETz?f&* j*.L)+ x6:7P#Sn|UX*]j!py6h5ePɭKq"6">#CD4Lzc7moX(툘B< S!iՂeH qئ8xc?Jb Sij+iVjHQ fm7r Fea X0TNp m4-q}iq^~cwƶȀ #^Un?)1nTF&c{er].& *͜R Eᇱ".a64}KOs }x~E&c&H eGږz p-VѸǻl͆%+33J&_}Bf#AaK]i^84ڊongU%lۯTu=./ O)+=N..fmd=&!AQO\6rqQ vyb` zY'OX#vVSvTӑFt2綗q7)bD+e/b9*i}b&n;͋F`xf G.`BJicaqB~ */d=$*W,PP< ;zF"fb Z^FO@7KFVVy%4@v1ǒX)%B+*rw҃UtN$̀ưx;McuxxB,}Dfd9ʯ,ZyvBXbnChC)^ aWn =}>"TYL;;؇]$c'%;#\+„Vԁ46)%QȆlݡea2M/Edelj5ze*Mlu+dh Ei:uϳ.PvAZ{;]^ ӔmBB mP*Ѡљ'{ CL"`i qM"CQpupqVOyM8>_MإkUf!CaL rznXN fn4L8,unq|o4qixU2:ܿOO՝A4,x &! ^f y=p keih^Ͻ,xMQT cjC4W Q=ґ<2 7-1m\hn˼ ODc 8hIÐvf$=,Ef#NW%-ac}M,UcJJ𞋬pJ`=\- %AUɮʏk^7<>8>& 뛘a~Ryxs`wcoDCǔ6ˎIo`Fpv6D mf"{ÌAu-q/w9 r<Ź2j{),dFJ1<ϴ]Naݺt;sy `4xOe߮z@T^(?T T,E'Q_*\va)30ߐS!K m(КQ\ti6<3 Y5 {n}joM߰u8Lf\{MSG3/%cI`L!!2 B[AQӍ7Uر' Ɠ BbIY9\'$X}9A,~R%,clBY@1v 4X"(y8Gi*g&)s=A/ E~w{f5)@Ng`}ޞ0IĻc2_D+cUnۙg|HrHϢ[k'.A9Os˵.ZD[f%TJ5Ќ=&= N/lqjΤ}v ԩ`:H1Ř]*WJ2ٲSr32{q21ajnM8ѣ^$ OMd(*es ܳwRP4qj4fP005='OqoܭkӟOqR)+V s􎖡R& 0:*֬Н)R8,#tZ|ήv#.уZ?M0ELvf5-1 뚨qtt;SmBQe&9+KSa)9) pu{_ | Q НxrLbp'J}e?p1yx茆e^IX>$Bؕؒr-C>; (ee:;v`(]5`q oȔAprϾ_ܨDWrnƵB-z`PX%ܶ ަ`iDq p^F:9bV5 p!N ߰4vO1Glyi7,HȩA-u9T~VYajՕ~5xߩ3^*MG|%E@qZn4b`Y-}2 alHJHC8Q0@ &\C=fN~ ߓot:BWd.@zBNK6Ej3g3E4 UPUI!:\e̽8ƽH0i#,)`ary(Zg漮nV6j(#e썰ĕC F+UɶDS19(,:q /М, $P5[Ur]SȘ @B =+ީQ=j@q>MgY1@,`)AH&*)s?M8+ ށ @o _A_*#N! ]1wG݃jqZf9pqgQݺ{8@.Ln:*՝}A|2\ YU͂wid8mXR~`4IMzg δXd^ޕbVS.c[ttt%o_G /t4Ij@q p>Stx\X:t6Kq@P݆`|8(a[fD  pkmT X!o'uu7DMz6_p7asUH\w]DC NބptƢX-Ƨ;L,"zK9fӰƚUV%,Ǚ8"@&gF 35t9Ļ|,~J0'go>9 Wo/^@5諯>~"z :ܷr\ w'䅡2G+{@3jp!/ ;d.7xc2eWH>"#CS^ (Sopƒ`=tY/{+lD(:M#Ύ7h12{K(Hy,H&SMzb.M<`Tٍq.3+ds,e>,E0(e ]ddo.>o|^.B-p0Av"\QͣQQNl, *3D Y{cqsl.Hv5Ȃg Zj]ha$M/"}ѴOgP.=\N)$}ޭ[V荑*N-QK,>:tJuO@.4'1C;H0z䷪M]$z.{N9ZM}0s֖ -؝P3OlYkr5lu>h_7\EKV9([#Ytu&4є{ ǤuG}ɹ:@Xo09 nӔ@344ՑvJD9;eT Li ՀϥC>Hb`񛪴4>˽X(omRW;@ {i #K^J@732$PؙN%VKPf\2(K&Ƕ6_ْɤY .n5Y-SMkiMЉz;7 x_V\\icTjLDq+jBj|]xGܷNF,e LIǨ {%Dn<|MZM90)tfDL/So: E~~ًb|!/\w[E# ? ,-9@]$-,(gxP"3ųOvT}Hwᅡzg߇UZtPuZJLe *i0\U%`9{1k,LY:~I>/j@-%z!p*{ I,=_ST>" E#ZV-FƔE97 P*ڮQ 6Z)NIиCVo]P4NCJqFj!Ujg#ɠF6YNN ; Ь Vlq &Ti0+]ExׯxF:]G߽%% v- tMz_R֓iK*$Y)X\jWZ@S|TꚚõ\5`}EU^E p,I”㺛0a -=X6ΖycE# j 4!jgR/=uii%jlj=jt3NQQoؒC %M\z!B ΂3)L:%tځ1#:x!ɨUM&?Ky]>S2{Sa x>ܺK.v tskx#]η;v_.+-,\cҜ:cy< /zmNv}מ5ŁCKAMsaOUa6xsvxsVɶ.ŚPB4t8ZO]g.ob#|- zC7n?=FzSw #uylAD ?{\#׿zUU~YogdE 8zW|41r<ʍi>A+܀Κ>"mocl#*p`5Hj^(:jX]pxu/`ocM RC&vrɲ5Pk'Q'*3tcۨ) B5(Za 'H6x2u(s]ŌՓV<WI9:f,N-˥ӅXv=: O0*m-R)1g`1i͋ȄTAԥO!wZ|Z™+h/LtOv;yTvGлW g_) 5ɬ(tiMsMjLs.O'vn]PN{W,NNf`)HC70*>J5m DVUö y Jjv5J }VJԥdʨJCCVL6@HPswn\d+KH(4=tR~8|t-OV>_'e񡀗9C:YFzqu(آ\;%]"-|A>]Ue)8\ն7N׮g_ Nf#s!nEMt&:5P!:1N$wA sFݷi7Y IDATwJ +Yf V K9;iDRuW+#9#!L~L%Q(jz ?B$7j>cf;])7kYI@Q I8x;Xbn:kL|E؂͔2r㒋U^8p)b yzB[JkE#њ%i&EQ,&zCgPKO 9RSG|nF9.}E/ಕl쀉tF+]SX:'E^Vp(쓠b%ɖa\aO,5u׃: 5g~:pe&;/맍kj́U+VX]N";DVqc@ xڃ*pl\WRvVGGXƓm{%ATpś~I˚{h|\,?[E2~-#["6m g89`.XA殏<8o{ښ=]"gg8G yahLL<5FO8؄N@i**?y;f#t+|3WVwwЇۂ/3Ep <=7GKz.)AqoYNgN`-qUc ^VmD@0B#ҏt)P ̓oZt)3`F"ƔX!"V/T0Xc3a| /@Or<қ=љ#t.Q{*b~Zμz9ܣ&;".qzxQ+)Ujkrp)s_Pr )cR|f~V#P;Eł`śs#[:pw?iG-O \ez|^BF,.XVnb7t5Ց;442hQ#,J#G"e{ F"0WZAK+~Vsool)s (8 ?[\iV9y\s\) < K+_}}ϵl^UXp}w=JBd.ʲPCj]pM4i73%JC?5Ui~Lh?%ƔT'l:fHbΕ,7֤gP7Hqv\n.l[؜Uɗxhǂ~"tyT;ɿ.KƂ<>7K׽HL?^In{6SR3b %KyJaLo!U7o^ʛ|%E7ӱs]tEGJƨWC }%=8 dXl4F"ϫٻ48p PoZuDwl-e# EcO- mmeE.fc|w`Gn%nD)Y>*I(TMEgX Jl]!p+!b{j.9^I'S7H|-Qt*]Ѡ&оWa`ż(}7|3\/ =BqUtTt i*n"a]*\bPo\;9Vsq/ELsr )?l&+"Zx2& keIA| F:vkEd ч~SKHkuma* y&KU0“};s;FA`n*%lWǒn+IܶWL<x׳KqN`g@@W %2n97%~0+ Zl"FbA":wPa? DCb^FӇ׸{n8A< dNj'1 K7Ʈ:P#ٓ TV#9]P|ی?/Պt iisDA 1?]fog,QW+avukIdCpڗftmn[߫>D6"C ȍpOeU>jDBjNÊ?eBu=6Fm1@9GQc^-MFحT&k jQu'5N4J) j\|2O90Kczҩ=w[AlIo.0!U3Pqd|{h'@w{\eoX+k\-"`}`dz`У‹m.!v!Ξrn@aofcKVjG-hG*2P"v߅_?Y ~T4!xmQHIdxëqIE7 g;Xْ\ 1s(Dk "4Cgj]FGրי8C~pߣ~vS]y Y,ܞGÒtK{}PUx+JZ3MhaNfBdPi^2U6eR20F+}1WL9\/iB:avşyC NFS9\hbҲyWchHHֶbq+gO؈/ z=La΂iϘ1M  &ULg9O)6&i1!U=L TSxr55פ5*;uKXl3pɱj0hnɏАcߢjY7V< CyXr8'ҩȟ _*3'bi5jac-cJG&mYl WPX 6I Fh)>j14O% v09Q%`%ɮdSxڟKcNHR|J (-!Ri :v-@9{[?_A+K_F/ ZRLZ6r]NҲ좒[ٟ>AwإXyp#j0mkA3g)AOE/gwt,(Yf\|ۀԑ$0%p2-.їbLRF I\s_},) %LMk":Sji1tf[sG/wjlAsOIWb~%t(e˴y}ņPnԢ%8V mҖ̰$Ediɂ5,2k=1xvgHNWȫh=e=d7AdҞk9zw. $zo~Ou,/U xֳLS>!ct}Py#OxW9?-V2E"Z|l~9.Z+"?Mc#9G*b}dƺ [Ȏp, 薹$-M(rrrcQG Jz.Lzlȇ6XB XC#tBOD:5$A&gV}1"$l2V ̸Td1-+¯I)D$ Cu+O1()K1*k2v{9$EL'$K{F4 /dj Vn#+VJAxNbω  K9 ID\x"Zr~QJa4D,Ao\q$[WeqqZlӵR"R˪%cO: HUgUƒ7}U>t -F'Hΐtem6S!(MWǵ[f6~Rڅ0Ԭ(ܤuIUZ< -y!U|2H;=)p*>v)'I#Xcp4NTgNgZTc@ uG#7H]h8FLut~iL ͨs-R@6 3cE(NZ`DUxqi{PeKEMHt]tU#igg_AP6e= n`dzgV8uV[rZThWw|_xRȲadN ,'zI41u5*1} ]t }pOus"UP@f81vA?=C C&ILv1kZ4M\_1%}[iR5=CLBM;n۫uG9<]H7`L|i{%ɮ%NsR:a/nܴ**բ>h5!YH͓q]qݜXZ23L]#j? )̍'6"׸#?'/o/|yyWD`iYBno)&Cԥ ύp_~$%ĊPKZƧv2ԍ6H%٠*SRgX}%X4@=CzXK2tW#ЦfY@;h w7V™Wukn7-QxRx}!,ْPvtT,Ţ  kn e$[k@8Esn|̈MK5;GCWKb[Ur^9ab&6=,=o 43Y'Y@\vVWoq_7m 5t.PP?G: oWdJ(!z)wțh~H LIcia̴{g/ K<Tk˞.e@%WK>n4pG_Tsz5c C\bW.FViN!(ukhࠀhN<&BY.CH"A8FWtRY)R-^1fn;|TP0p1Ю4!=4+Իs.@1@ r Ul ʩV3(z;Ay ޝhfܽcGwq`9+_E!x~W,|ͳ2Sxb9`YM6앸F?ЧN.`C gNLՌ#z]ҫ^4rۙm>x3Pm,B7awlͳ%^x9t.YBxE=WU1<bMd> ǺA<>*w `|0f<=\\0oom(5S(<[֬mUv&= 7UKn^TixB!ycHdK0v[EnjCbFl=A@Auex U};$gU1Zrxf{eMҪ4w%.܊Ϛ\U g9hE (:2h.]\nlfWox1j"};㒺^bd խ yuEe8 MډLKSMK+ n~D^=kuyׯ_v;ng }& ۇMp[9&t2+e<uM}΍Xz}dП3mGJdY#_#=Y)6 Kj#6]:r1h(b9 3 4A&)a-EUlG2vArCNvOPqz7T/bOyaDV',9# xYZ!w]+A)a} ?_Z30$0%4`KrXt mL⻡&%#fմ pZdz҉1x26fG C{FK.El4IRa@UXMY[$*Ì?FzA/+榼֗_XאY1rK2"nEҰ'U1G!Q܊N]%j"ZYKl>S6qĬVѫ8,\vc7Fa,bq&zUi%'%̳Klc6v$<%42'헐ӫp+\G-b*H^י_ t<8\md`U`9~(t|^}M,#%9?ݒK<XN y$x;H\N<3e\MjZxv {ms2ޣ l2]l] gu-V,h,Ә27"Z [w,o4+ywa %ֆvo:Ϊ`HU\NGSԙ8c [;x9B!lho9(kU>(S.E΀H=$ 8W8SDnλ>(5x7T%_LLEhYÌYx>#jCI۩RDlGc)1Rsn5LbY7ǨԂߐbW:j7SS}zwe.T`n9V]KN穃>@٨,ֽyW9  tdZ_4 lQyC%@|8 1ϴ) t`dLN85ؙ$~4͕: '8r,V5~/ _ tl+gSNJZ [ȳu6$ؔs2HLt 0-Lyw#: !$7d:LUY2e$RT2*<ͨs3p$m*AMݠ}HGb\u7H.zت RvԢA[Ǻbanxrl] p}L 4@/J$g]698zs (:8 [JWHeCE#UF4邎6fuzw\a)q1`%λ" .e+t|yW'0p~#P G1"0i kvC_d#$LvMTʺ402)ok|EƔl.Q LI4Fc$k:O/toJnVא#pxsVr*w%`^ sBesKi \A٩$kw54;q۫.MS<.r۾ڭaԣ/wBMZmfTd+ܝ&#ȋSKXB.UQG "0NS3s}Xl " +9, ll.\ 77s>Կz*Y&IBW:NoOZmM*@08Pu][?5QszA T<`HhdN3ϰ za[Kw卭0EͫbR͈ L1JF䇼归l&Jg&wBP4ؓ>IosBqJl&ܰ8AC9ӳnk©Lo}Mmu`s\d" - nob~/nn~-ps tBqURŴ*KYPLubε ?db&XfٗR'Aqݥ& V˾Ҋl'&5_>1cZn^v5ܻe  vhw -9;pw]F]ITg%Bв s'qKFb.WTu++[!WX2/*qKx߳$@V׆ xx..Žx&`̀)?j{,@T_߫Ujؒ}T+3JA!?iXFZukQzVWssgOp x݃ LA*׿ ߃eP6b).%~^!=uLXY|1)$`pR RHUB08h/Å#R䱏2TïngZI$T1bp`E27(grAZ4U ?Xnb5v2VT.HmN .T#vo:?{ρ'so1c諗@v\o_c& ^a$ɝul.敜 @tkdRBHsrx#Rأl"߿ K.'7KXy6Y8ss(PoDI?|tZLx wIbFHA8V/gE.Xw35b#( 4[$D.m(UjGR 3 91gyK(tP$Rj7fveiI\OWB&N"S'LnL {癹Mn^L_(+jk{MrGurL=Dh t-q,Q=+TQjWPEarZ؏?H 6ChHv`Y;˗|<>y⋷G_, Q,]ײ "O)W#[VϊU#dPMNԨQ +ќۺ"%{U-΁+%+.Y#oǬAAG9e)\gX̅+ǀ#ZlSQhp^RHrwI5ns2aĤ h.j*}Ъ%>q^*PJ""ǮF{󽒦* DHuC^OsON0hb(-v1z]q[ _CZ3ow|"KB R,O$WE)LՓܷp8|\o:G FB.`[bOhF>1(O&׾(Ώ| /ucK'X)3Hؤ|?z쳸or|_ϒ8p BEQU?wn&`l$:N2٩$@Sr .uO5l)2ytqn&' oOm|Ѭ`Y+#2!=[Rp ϿhOºSԍ@/O}_ z\ aƤZ,D Id w0D`Rq phP3^0[L^dV~6cוx25/hMƈ 07 JZ:Ywe X6/o 92bHs}RĠey)g,S,#P-#z C&e.~N.ٞԒ HfCsh|_igL UE KtW3`&dtK-QmErsyЬ쎶]2Tv,!5T4JQ\ZigBMUSXkahƠک2$ZBoNo@w\l]Og@܈c."vۦ{d OӐbm\eجvn30C=raREipk< =i~xzuapoKLr~mU3 LD}?>/glgB4 ZNl^މ˔eTPvJh R6sӑ41(MjW(:1˾6>a`_1'z495.O,BŻ#n~n$7?,4ˁL-;XBPfsQĮ$KGQl$95:%Ԩ"R Ԯvi{i= rGu~Qg8ڭvR|b``rEM$T=xQ z8O~c~`k7Coo_)9oo͖ iU߹S-IڑǞ y؛gh]FK.vbq"gK$d݇2VK{s>*ԽX{ie0~U *Y& yIk ?)aA+h}A񖽿"=䙹6'*!ά/{!HiT} wxtW5;ZI&YKP*Ql mG.'D\U}Ķ\U}%CLg^%/0ڋG\W7l5ҵGUeD;)X@g&x &樔7)w,E, V\Xqj]^Ϯ Y"9$NȶT켚tdmZ@ @g•辰q=s?s97/w%urnCvZ,R_8q mE?Q)?1Qۑ/܂}dIlkR{ .\܎USl"і)r8u}5WDz Ë^!&G#dtKh~}.slf%6g6+yvs1UlhW }{K[yNmEs0FQ]{AٱuxPSh:5G  6 qoB7')׸4~3 A(Wl/n?Dzв;/^K.f` eOoxg]{ 7r3b>#ܕd%1rp+5ppAL}G1*գL=L*=Oni[&s ˕6O׭{`}ޕ2mDiceUmYP+^!cTTئn,^9 Fu:ilOpȒvkLh0-"s<_58qhF=<_?Ϊ-F jIWPjHnwwC(qEqs5.بzwv`h[7f[@&Pڕ'v ;:"m` ],SB!̹kB́MnAr@LKe@^t!Ӡ"ʲE&xS|4Rmf;&CYo>֐#.hqeyOyv`4DME\Iҋ8$2"ETDbF~wX  Ph` Ei+D/Rcv@Zjvp5_4y6wz@3<_}vdA_w焫=*TV|iX Djmxч{kzyw" hJP kkϖ5Nkks֯,XKEV3\qQFVV56EdH? @:.7ߙ#a.>]|?GFڒ$5:ӛطgJbN9 GXA0{7O<)H @/i[…uu<|< i7m̱}%Vya@?WI-xAupGؔG襀bEm>~\iTwNkW9k?xKL `b\OJq.oU3o߿L0R. L\UI0f' z1&wԪv8kDֆc{$ZwguU,ɋ4 1z;{*!׭XA1Q]-OJkC"3gqN(!aK.fS:xVI<8jaҤK7K$)ª&k'jQ<1H+-"VMu np{!''^qx 01$ZڊMW'3F$6hpf8Mx6? {T엥D&\* >Nh2! ھK[ .g1HN?rcD<9QU V IDATħ{) 1/)n^`d{AB}slhFj5q[Vb4%؄P dhKXl'{ʡ #%׉Vf>6zp)jA Π;Ґ#u%>܌A~.PHE6q+'U7e?BT ^5 {蕧bZc?GULO,AP273(@}oϣ OL PaRly 0/Q(9 E.q6-#xr ŰJϑ.*LJ$.e4 -Vw ThF %G#.DIE 8[Pȝc_()@{Ǡa0Ge@r⊚G:HĻ`h) woJ0,jW@liЀj@uѮVpu ?PVpڜ- !SPnn^@2{z}3y9pr\Mum\_k{8E'gme7p3?ӰiļP4bؒ(3#NΨ:{_dq\w^Q#)J`#$xaZhv\ba֡Z0M!1tw"3C^e==++Fy־Ȯ^vz'I)-bQ Uj(}h>aAaZ1 #^ܽTc k@6􂮄6:F[x.VZŔS8JOEIojr"VD'݇մYQ[$jD6p6{(VU,dǃȠ]k1>A2%%@yP/c1]I wgl><6n)uy&޾1'l`jBIe+ylbX-BLH %s+\SNZ917v-7D+.ic1 W o]W+5bg jFfRٖ<2:!J73(@SlĎ$y`M,VC;`bF*`v\6{?Ttp1ɜ񥻂{8H`iCT>abW<+(8yST.9vM\ooﺯP.́Kp|?r؝kIqĄΦc朗7Dz 7k5%t7" ^L{*TCھ; IdX.0*Xǿga$<|q_ǻ(?BAboS>3D="A"6U12Ax9?ĝ>^]ܦ]OR-H2[4>@3Zkgy"Š rh4aw,_h[> Z$x@`vdk\Nn&s"3-kcC vzԝ@sO"v Vݫ*EޖW❂}ɲJ NF}R=޳Ϭ1 +iPysjx VzD_K # G pA&IJc*; AŦkpY@mU >cnN_ 0d\?Y_DWo`OBtB!U!DN 볯><7g /} ekSEcY:!sDjM'@NGO]xcps^P W # ^bb*M '%aM!BбJUoKiRK_+j=FsǏIdG͎w~U[;ZSHBPktq ttX<6_R"Hi+1pj%7&‚n4MJgJ-VӔ mȺQYФvA Gp( tyh|Aqtr +4\؅>(-Vҝ*X]<왼".bx/aTG7R}<t%Nϴa Q+ӯF4.m\L"Rq[2u뤄Ώ$>My:4%:69eWdS5IaQx6MXARw\x..7Ao@b},nͤ;)$ 5›G&Ǥ[{QDMMan]^}<>\&<Ak{D8 x4op|HC#+m䄚hUW^L@rd{SkfJ20/(UZ J,qVl;q?F[ҝ|fdm1ɻVV+IXv}i=w/$ZM+"^FU+U.Gvp危l4ޫTQ1mK)#"P(6Y3RhԎiM;p, rFR7%s5LJ48;5ԋC3.>遆.mۦZܢ%έ J=RFG;f6#?ޠp|PyXe!X_cGI*$3wUΛ"6f`AlJed輞8ܽ:(EH\b v'j%iL>0f 7 遃؞:Z2HkֆK빗ÓOꁕ^G0RQbO'%[WTZ:R7 鑭LociP#uZnxIvg O'4yJy18umQI~q [P֒ zn;V0*V=Ċ\Vڏn+ӫ=-N7$^k0zz$yBZ;yaBw.cMJ1?4ZWBW8ײ-Lxo:^l#6=YVy1ry*LPxj$zK1wo:c,脼A \.k1n@ջJ8ECo,҄*ܡ*$]_!.S^X8 ._~=/;ՕNͭ1[ES;i] yNKZӘx!/+NƊk#COE Fէ r1jKũ/TN)$-IKmh%!CC"DF΂1+9*CF{=wJh<ڂJ7-r{u7@B|Guj l0!a&?'FJYu-±fT%XDBzf5%*֖J,8EQ-sy{g5"-#U Q Mu-,H/?8u`z `> ,>)@.)_}*,s YVPennp GFQA8Z]qnc-Ӷhm%ȃ*aC>{r},DI۞_c91MW?8h-mkHE!\F3E.>g8^/d.\QWeX9)-P[wg3L99nR雮$a#Yۀ o"ħeiE{^߳#_PxIaO.SXpje:J1b5"Ut:'U:rE#֯11mctR>b?+O ꠰lJ7Cr4FFMv.=c>} }btx( xx[ĖW}17PYUP.aGHԥZrv|.컆q&&I'DIAS$csP5-Ohڗfk{VNEwif%h ~AM\ՔqElJ6BSU3zUyc1h>_@OndS$8%Q720> WÑ2NBGozWYnV,|ߕXzlldc4Ny^'F: )MgFtug{ZP$xQ)h/OR*7?9Yol VZ{ T١/˙[/)%;"^^ScDa- }>@k_a> fRH!ks:Zt/?ǎع&yx 5եљk.F$zH 1ĭ(ukPU}.݌ze֟]Og(hImg׆FP][a}_JrH59pU'qdYq@#!9(%= .&/f}[AwEq5Vzה bwDDYMYq֧ؾc ulMB Ji+mR`}1U\ѪJ?C;b+ n1u?֌<m+L@@NyuW@rzCGG00=#a*TuX.82>{8[1:7lwRy 9p& dePk+;AVu6;Ϲ9`Xlu4;p9OlM 鮵XG?ڤDyOp׋Jo' >5h.te$-vh)zpJ/1$;ZiDL߅g}᭦&w|6Bn1x OfZ^#ɺ!5ՑlK%a#QōJ/laiƑi kF3P쯥*&HͫPC`<ғ{rTtAGl!{@>]Gq I7!e#vÓwd0Kn[!k%$CnX$"#oYrۻKXuk<7MG9`?9dZC809AkSɔd"Q$xZ$]M-aqcSS4hpbD>D1N(V5jŢ:n.tL#!kZ{y5`hF5ԁTq3/Lo(J^gC?эBp, Yn$$\byF5ôD2iä$IY"?E&nu%9vmdEh5M[5gG ~zpj7G%hUk.+w=H4gOy(s5^&%5ў\.hC=09NG7q<,b t0Jŷ(LHĐa3))A;jr`RQЧ**+ǭ60-Sv^EP:SI\2c:9Q&}`K؍4M^5`o=cT0 S2nB-寰<lĠҬ@Lݚ]V'q V|iXRxBH$u$ c} rJQNKJOה؂zunrJqme=@..Gkx[Ȥ6>rJ^''ޒLF`@l]hƫJҦwثϵudy04jXlBR'd۩ϿhwcVNJT)QW?k\ 1 Sr;r@jNʦ3CQ`ԉ "?ciV#HIna}Mc-?{\˄9+e_GKWot%ro.ep)BtwHa!wPF$ ;L鏾NC155Iѳkݏf1JASմXggp|WܫBHn_m]ʚO?)Z@ 4ޱ{^.Sx\S_& |[\Hk9+gvuG mHeSn`)ũL@8g0uL@KXbjҹ%2 ٸ.7^HE 3:?O",.P y V ֟Φ +v?j4}V5)?T 7,%y#YqME8 I}Ma.Lb9R?t{aPhre}SOp S6O;m,e6ܽaERQ̭r>89aara ɾu) J $Ad`T߾AVB0GSbƦD֧ܘ7ě5aޭL 4<AZ3MoBב&t媏6{|%q)tɠG\eiihh(-|bDMX\ĹuWRWe}7?h:YW`Fβ-a3BfJb6"WeX׺Juo%3sm$ц`$D2T{@nO3J>@rvhuŨj1`s>K@I5>yʣaQ9).\Y^ 4 *9xg!ITq4;1dhm G]_Cɘ&,B(vH[|י%rXS ,`6,a%TS1ujy  9ZHN}W0wGkR ȓ ^`^."IuVKd\!4mYl6^yH%ߙ.q|V]jbWzi 5mB]#l`AJ[OޫUfl-ع@[Ҁњ̈R7DRsr0s.Uw:_"L#fRYP9X+\LHyl8g-"'Ӻ[c6SڣNj~uaFZM$ 㖺8rhhcfctWM&]D~L\2p(q!Te$\BP3{h#&E=~wX%ţEc(ֺnm珱 UY¤ԈHQuT&(s"m`p6Bպ bZںY 涪m:U&Z]ܪP !ga(0c10Al⭯ǏxiZpwJ:!_ 3Y$whE'\ORi &ۇ{KX^;gTwӒXACLX{gduiv}]]9=v6nIDAT1U 0Y6 /m[&9*{H9iC2L$HJSMos w5ME"u9pВ_}uS`^OVn_ E JJw@vPֲ]bڸjjU- ШY D#$.(HN7R%-W_Ao~'п3Jf!&(JV$%ϿS=kDuk7]gaf7~FcCUwrcd4*#􂢡uCOԎ'uǥ+FS r`Jj{8UVI, e*WؼFP0R͈6)<*a 64[xU0jYݵkc/ECi2[4AUVn-T0q?!"G#TPh3 xGP8ko>V 6pTÍ\͸FV}"/u~=1 |1-a5mqR0iĴmF;$0ٔսr1D@%IsbHD-8RQ@hQ`@ CW,YlQn =.i8B'I76 c6Քxw-[DxGwj&}/I ,%BhRT!X &5Xf ?() #a)S1G,0b9P>'g~} ,?xs&L0dL y%hܿLu %?(^m,-%2v;,@hf\t(G]ѳ '#V{?&íBxd41d',3>BOźIf<?+.pYl<)a i9t+G0M(mLCҺցZm'T;Qh#m3S '5`=s9ި ^f$',s'L,$`Q@X,_Ϙ+LS?C;#c#yfI&)׆F|S/D% G 1šVP2;?sZe..bot/XIjA-2[hqEmVzżH] $&Zԧlz"T|I5 *=UkF&X됑jz*`ٰnn:eوVĪV2=Od=cR1A! lG!EPJ¢&RH`ļ'QPŒ<%3_OAM;ݓ'P |xˉ0pŀQ@ uR4*;=i' $f2RneKe畜fڇq/5>FmٰFRT2ZpFʡXTWn*jUgC+cq^[k:_c!E ,&oVEnV] @g{JHb[LW?Ww{M@x?'Xqr&\'QZkg6V8"C).LݺE3f}s2*;nc!Pb ӳ 0ȣjUctƉ jOͩ%)(LPPfFQ} QD0PeH( r R A~cOj7o}}[X */ժ7/ v>ȋFPegU>ᮠ\k>j;ec\Aiw G> oy^m hfv2i[ׯ(>Nk< ;nݽguK2b(9-d .lc P zZ lDŽtدaw+[^wP"wr픨i[h= d@6З C0m 5Q$ cņ,@|O9[46wx}=gwͿ; M,wg/3d̐Ai"'\A Rf  = -sGA?z  78R x5&qfd0NQ}}ߍ>NM@,Gcݎ) TUg_goX RyU዆+;Kۙ{>CgߥZ{ kj@Nj'`X(df[E3 ם.V vzn9P'V8,Nbj, uQw6i̹\}Q >`ݻu^ZCDSiqsz-d݁Mow75Y8AvTUݙ,~}AJ 3dLt .h31Kdfhyr 2Л?>#pt~ Џ?~cB/Dg@oq\9;p2͊z99<*' /`C_8!H9`w~oA: OP"l@_1񡁀 Hm F1ҾڱrD;$(OhӃ߆u* 4sL<|hx GdZR9za di;sE޳}bCf7Gis|`$L)lI&䓂~/_X2Cx٘x5^ ;Nq`}/egEy}YQA#r`p'-G-+AX˶S!{ >]0v>%ξU=K[@ʎ1AI}ȵm ;&\3o!dez`8X {BdɎDJnoO6CbJp8_4Cюza^HvVP,p {@N)@VG`b-d7@wA{|rMO#.AO] E8(}d7ɞx0^@ #gA{|:T]J6X\\@u$XQsU/oaXKqj茄iߋB<ޮb 3k osp6foiWveJ"oՊ j'Lt;cuvCrl&JMBy73t܁x$β8+{w{0tۿF:LR@CɊ++﻾|s2\Al!#'$, e&]-;:e;G#\.};%F5g~1 TAcXh^UQc@ jԇ ۺ%]z7iGR W,g_̛dJ/ :'˨fd3d d;N'C[ prcBkGM7\_'oxH7v|o P|-̇ ts _?<nFhz.FO&_<<7eې?SgϠгgkv3+W ~|v>˟B_g>_?8ߵPIѸ불3xSC?>qŰ@*| AWWVT+iPjQ:<)h5op$o{fDbsЯXX-ưn*م-6m޾Z۫,s,u=0 %C0φ hHa8@ϐ}/'9?|!}}o?{* l_ۢ?|%|q~Z?bQ':z*; $-{tw ϠO8R2r FkۇG;f@ӌs/< Ubkst2d[[F"VZ|w4K'ꓟ })F yjN<! TYbа@/rU,BMZ}pz}2ɀC̐]a+;o\ի~o_%/k wSyUVLV_5>9pŅEǠs㩭קo S}Q>YO/x+eiwIENDB`kraft-0.97/src/pics/puzzlepart.svg000066400000000000000000000012461410616450300172510ustar00rootroot00000000000000 kraft-0.97/src/portal.cpp000066400000000000000000001156161410616450300153660ustar00rootroot00000000000000/*************************************************************************** portal.cpp - The Kraft portal page ------------------- begin : Mar 2006 copyright : (C) 2006 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ // include files for QT #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // include files for KDE #include #include #include // application specific includes #include "kraftview.h" #include "portal.h" #include "portalview.h" #include "kraftdb.h" #include "katalog.h" #include "katalogman.h" #include "kraftdoc.h" #include "floskeltemplate.h" #include "templkatalogview.h" #include "materialkatalogview.h" #include "prefsdialog.h" #include "documentman.h" #include "reportgenerator.h" #include "kraftsettings.h" #include "defaultprovider.h" #include "archdoc.h" #include "newdocassistant.h" #include "doctype.h" #include "tagtemplatesdialog.h" #include "kraftview_ro.h" #include "databasesettings.h" #include "setupassistant.h" #include "addressprovider.h" #include "alldocsview.h" Portal::Portal(QWidget *parent, QCommandLineParser *commandLineParser, const char* name) : QMainWindow( parent ), mCmdLineArgs( commandLineParser ), _readOnlyMode {false} { setObjectName( name ); _readOnlyMode = mCmdLineArgs->isSet("r"); /////////////////////////////////////////////////////////////////// // call inits to invoke all other construction parts initActions(); initView(); setAttribute( Qt::WA_QuitOnClose ); /////////////////////////////////////////////////////////////////// mAddressProvider = new AddressProvider( this ); const QByteArray state = QByteArray::fromBase64( KraftSettings::self()->portalState().toAscii() ); restoreState(state); const QByteArray geo = QByteArray::fromBase64( KraftSettings::self()->portalGeometry().toAscii() ); restoreGeometry(geo); // setAutoSaveSettings(); QTimer::singleShot( 0, this, SLOT( slotStartupChecks() ) ); } void Portal::initActions() { QIcon newIcon; newIcon = QIcon::fromTheme( "application-exit"); _actFileQuit = new QAction(newIcon, i18n("&Quit"), this); _actFileQuit->setShortcuts(QKeySequence::Quit); connect(_actFileQuit, &QAction::triggered, this, &QWidget::close); newIcon = QIcon::fromTheme( "edit-cut"); _actEditCut = new QAction(newIcon, i18n("&Cut"), this); _actEditCut->setShortcuts(QKeySequence::Cut); connect(_actEditCut, &QAction::triggered, this, &Portal::slotEditCut); newIcon = QIcon::fromTheme( "edit-copy"); _actEditCopy = new QAction(newIcon, i18n("C&opy"), this); _actEditCopy->setShortcuts(QKeySequence::Copy); connect(_actFileQuit, &QAction::triggered, this, &Portal::slotEditCopy); newIcon = QIcon::fromTheme( "edit-paste"); _actEditPaste = new QAction(newIcon, i18n("&Paste"), this); _actEditPaste->setShortcuts(QKeySequence::Paste); connect(_actEditPaste, &QAction::triggered, this, &Portal::slotEditPaste); newIcon = QIcon::fromTheme( "settings-configure"); _actPreferences = new QAction(newIcon, i18n("&Settings"), this); _actPreferences->setShortcuts(QKeySequence::Preferences); connect(_actPreferences, &QAction::triggered, this, &Portal::preferences); newIcon = QIcon::fromTheme( "document-new"); _actNewDocument = new QAction(newIcon, i18n("&Create Document"), this); _actNewDocument->setShortcuts(QKeySequence::New); connect(_actNewDocument, &QAction::triggered, this, &Portal::slotNewDocument); _actCopyDocument = new QAction(newIcon, i18n("&Copy Document"), this); // _actCopyDocument->setShortcuts(); connect(_actCopyDocument, &QAction::triggered, this, &Portal::slotCopyCurrentDocument); _actFollowDocument = new QAction(newIcon, i18n("Create &Followup Document"), this); _actFollowDocument->setShortcut( QKeySequence( Qt::CTRL + Qt::Key_F )); connect(_actFollowDocument, &QAction::triggered, this, &Portal::slotFollowUpDocument); newIcon = QIcon::fromTheme( "document-print"); _actPrintDocument = new QAction(newIcon, i18n("Print Document"), this); _actPrintDocument->setShortcut( QKeySequence::Print); connect(_actPrintDocument, &QAction::triggered, this, &Portal::slotPrintCurrentDocument); newIcon = QIcon::fromTheme( "document-view"); _actViewDocument = new QAction(newIcon, i18n("Show Document"), this); _actViewDocument->setShortcut( QKeySequence( Qt::CTRL + Qt::Key_R )); connect(_actViewDocument, &QAction::triggered, this, &Portal::slotViewCurrentDocument); newIcon = QIcon::fromTheme( "document-edit"); _actOpenDocument = new QAction(newIcon, i18n("Edit Document"), this); _actOpenDocument->setShortcut( QKeySequence::Open ); connect(_actOpenDocument, &QAction::triggered, this, &Portal::slotOpenCurrentDocument); newIcon = QIcon::fromTheme( "document-edit"); _actOpenArchivedDocument = new QAction(newIcon, i18n("Open Archived Document"), this); _actOpenArchivedDocument->setShortcut( QKeySequence( Qt::CTRL + Qt::Key_A )); connect(_actOpenArchivedDocument, &QAction::triggered, this, &Portal::slotArchivedDocExecuted); newIcon = QIcon::fromTheme( "mail-forward"); _actMailDocument = new QAction(newIcon, i18n("Mail Document"), this); _actMailDocument->setShortcut( QKeySequence( Qt::CTRL + Qt::Key_M )); connect(_actMailDocument, &QAction::triggered, this, &Portal::slotMailDocument); newIcon = QIcon::fromTheme( "settings-configure"); _actEditTemplates= new QAction(newIcon, i18n("Edit Tag Templates"), this); _actEditTemplates->setShortcut( QKeySequence( Qt::CTRL + Qt::Key_E )); connect(_actEditTemplates, &QAction::triggered, this, &Portal::slotEditTagTemplates); newIcon = QIcon::fromTheme( "settings-configure"); _actReconfDb = new QAction(newIcon, i18n("Redo Initial Setup..."), this); _actReconfDb->setShortcut( QKeySequence( Qt::CTRL + Qt::Key_R )); connect(_actReconfDb, &QAction::triggered, this, &Portal::slotReconfigureDatabase); newIcon = QIcon::fromTheme( "help-about"); _actHandbook = new QAction(newIcon, i18n("Kraft Handbook..."), this); _actHandbook->setShortcut( QKeySequence( Qt::CTRL + Qt::Key_H )); connect(_actHandbook, &QAction::triggered, this, &Portal::slotHandbook); _actAboutQt = new QAction(newIcon, i18n("About Qt..."), this); _actAboutQt->setShortcut( QKeySequence( Qt::CTRL + Qt::Key_Q )); connect(_actAboutQt, &QAction::triggered, this, &Portal::slotAboutQt); _actAboutKraft = new QAction(newIcon, i18n("About Kraft..."), this); _actAboutKraft->setShortcut( QKeySequence( Qt::CTRL + Qt::Key_K )); connect(_actAboutKraft, &QAction::triggered, this, &Portal::slotAboutKraft); _actFileQuit->setStatusTip(i18n("Quits the application")); _actEditCut->setStatusTip(i18n("Cuts the selected section and puts it to the clipboard")); _actEditCopy->setStatusTip(i18n("Copies the selected section to the clipboard")); _actEditPaste->setStatusTip(i18n("Pastes the clipboard contents to current position")); _actNewDocument->setStatusTip( i18n( "Creates a new Document" ) ); _actPrintDocument->setStatusTip( i18n( "Print and archive this Document" ) ); _actCopyDocument->setStatusTip( i18n( "Creates a new document which is a copy of the selected document" ) ); _actFollowDocument->setStatusTip( i18n( "Create a followup document for the current document" ) ); _actOpenDocument->setStatusTip( i18n( "Opens the document for editing" ) ); _actViewDocument->setStatusTip( i18n( "Opens a read only view on the document." ) ); _actMailDocument->setStatusTip( i18n( "Send document per mail" ) ); _actEditTemplates->setStatusTip( i18n("Edit the available tag templates which can be assigned to document items.") ); _actReconfDb->setStatusTip( i18n( "Configure the Database Kraft is working on." ) ); _actOpenArchivedDocument->setStatusTip( i18n( "Open a viewer on an archived document" ) ); _actOpenDocument->setEnabled( false ); _actViewDocument->setEnabled( false ); _actPrintDocument->setEnabled( false ); _actCopyDocument->setEnabled( false ); _actFollowDocument->setEnabled( false ); _actMailDocument->setEnabled( false ); _actOpenArchivedDocument->setEnabled( false ); // use the absolute path to your kraftui.rc file for testing purpose in createGUI(); QString prjPath = QString::fromUtf8(qgetenv("KRAFT_HOME")); QMenu *fileMenu = menuBar()->addMenu(i18n("&File")); fileMenu->addAction(_actFileQuit); #if 0 QMenu *editMenu = menuBar()->addMenu(i18n("&Edit")); editMenu->addAction(_actEditCopy); editMenu->addAction(_actEditCut); editMenu->addAction(_actEditPaste); #endif QMenu *docMenu = menuBar()->addMenu(i18n("&Document")); docMenu->addAction(_actViewDocument); if (!_readOnlyMode) docMenu->addAction(_actOpenDocument); docMenu->addAction(_actOpenArchivedDocument); if (!_readOnlyMode) { docMenu->addSeparator(); docMenu->addAction(_actNewDocument); docMenu->addAction(_actCopyDocument); docMenu->addAction(_actFollowDocument); docMenu->addSeparator(); docMenu->addAction(_actPrintDocument); docMenu->addAction(_actMailDocument); } QToolBar *toolBar = addToolBar(i18n("Kraft")); if (!_readOnlyMode) { QMenu *prefsMenu = menuBar()->addMenu(i18n("&Preferences")); prefsMenu->addAction(_actEditTemplates); prefsMenu->addAction(_actReconfDb); prefsMenu->addSeparator(); QMenu *submen = prefsMenu->addMenu(i18n("Toolbars")); submen->addAction(toolBar->toggleViewAction()); prefsMenu->addSeparator(); prefsMenu->addAction(_actPreferences); } QMenu *helpMenu = menuBar()->addMenu(i18n("&Help")); helpMenu->addAction(_actHandbook); helpMenu->addSeparator(); helpMenu->addAction(_actAboutKraft); helpMenu->addAction(_actAboutQt); // Toolbar toolBar->setObjectName("PortalToolbar"); if (!_readOnlyMode) { toolBar->addAction(_actNewDocument); toolBar->addAction(_actCopyDocument); toolBar->addAction(_actFollowDocument); toolBar->addAction(_actPrintDocument); toolBar->addAction(_actMailDocument); } else { toolBar->addAction(_actOpenArchivedDocument); toolBar->addAction(_actViewDocument); } // initial enablements _actEditCut->setEnabled(false); _actEditCopy->setEnabled(false); _actEditPaste->setEnabled(false); } void Portal::initView() { /* Since we do the database version check in the slotStartupChecks, we can not do database interaction here in initView. */ //////////////////////////////////////////////////////////////////// // create the main widget here that is managed by KTMainWindow's view-region and // connect the widget to your document to display document contents. m_portalView.reset(new PortalView( this, "PortalMainView" )); QVector menus = m_portalView->docDigestView()->contextMenus(); foreach( QMenu *menu, menus ) { menu->setTitle( i18n("Document Actions")); menu->addSection(i18n("Document Actions")); menu->addAction( _actViewDocument ); if (!_readOnlyMode) menu->addAction( _actOpenDocument ); menu->addAction( _actOpenArchivedDocument ); if (!_readOnlyMode) { menu->addSeparator(); menu->addAction( _actNewDocument ); menu->addAction( _actCopyDocument ); menu->addAction( _actFollowDocument ); menu->addSeparator(); menu->addAction( _actPrintDocument ); menu->addAction( _actMailDocument ); } } connect( m_portalView.data(), SIGNAL(openKatalog( const QString&)), this, SLOT(slotOpenKatalog(const QString&))); connect( m_portalView.data(), SIGNAL(katalogToXML(const QString& )), this, SLOT(slotKatalogToXML(const QString&))); // document related connections connect( m_portalView.data(), SIGNAL( createDocument() ), this, SLOT( slotNewDocument() ) ); connect( m_portalView.data(), SIGNAL( copyDocument( const QString& ) ), this, SLOT( slotCopyDocument( const QString& ) ) ); connect( m_portalView.data(), SIGNAL( openDocument( const QString& ) ), this, SLOT( slotOpenDocument( const QString& ) ) ); connect( m_portalView.data(), SIGNAL( viewDocument( const QString& ) ), this, SLOT( slotViewDocument( const QString& ) ) ); connect( m_portalView.data(), SIGNAL( openArchivedDocument( const ArchDocDigest& ) ), this, SLOT( slotOpenArchivedDoc( const ArchDocDigest& ) ) ); connect( m_portalView.data(), SIGNAL( documentSelected( const QString& ) ), this, SLOT( slotDocumentSelected( const QString& ) ) ); connect( m_portalView.data(), SIGNAL( archivedDocSelected( const ArchDocDigest& ) ), this, SLOT( slotArchivedDocSelected( const ArchDocDigest& ) ) ); setCentralWidget(m_portalView.data()); } void Portal::slotStartupChecks() { const QString dbName = DatabaseSettings::self()->dbDatabaseName(); SetupAssistant assi(this); if( assi.init( SetupAssistant::Update) ) { if (_readOnlyMode) { // Update not under our control here. QMessageBox::warning(this, i18n("Database not running"), i18n("Kraft was started in readonly mode, but the configured " "database can not be connected.\n\nKraft will abort.")); QTimer::singleShot(500, this, [this] { close(); }); return; } else { assi.exec(); } } if( ! KraftDB::self()->isOk() ) { QSqlError err = KraftDB::self()->lastError(); // qDebug () << "The last sql error id: " << err.type() << endl; QString text; if ( err.text().contains( "Can't connect to local MySQL server through socket" ) ) { text = i18n( "Kraft can not connect to the specified MySQL server. " "Please check the Kraft database settings, check if the server is " "running and verify if a database with the name %1 exits!" , dbName ); } else if ( err.text().contains( "Unknown database '" + dbName + "' QMYSQL3: Unable to connect" ) ) { text = i18n( "The database with the name %1 does not exist on the database server. " "Please make sure the database exists and is accessible by the user " "running Kraft.", dbName ); } else if ( err.text().contains( "Driver not loaded" ) ) { text = i18n( "The Qt database driver could not be loaded. That probably means, that " "they are not installed. Please make sure the Qt database packages are " "installed and try again." ); } else { text = i18n( "There is a database problem: %1", err.text() ); } m_portalView->systemInitError( m_portalView->ptag( text, "problem" ) ); // disable harmfull actions if( !_readOnlyMode) { _actNewDocument->setEnabled( false ); _actCopyDocument->setEnabled( false ); _actFollowDocument->setEnabled(false); _actOpenDocument->setEnabled( false ); } _actViewDocument->setEnabled( false ); _actPrintDocument->setEnabled( false ); _actOpenArchivedDocument->setEnabled( false ); _actMailDocument->setEnabled( false ); slotStatusMsg( i18n( "Database Problem." ) ); } else { // Database interaction is ok after this point. m_portalView->slotBuildView(); m_portalView->fillCatalogDetails(); m_portalView->fillSystemDetails(); slotStatusMsg( i18n( "Check commandline actions" ) ); if ( mCmdLineArgs ) { const QString docId = mCmdLineArgs->value("d"); if ( ! docId.isEmpty() ) { // qDebug () << "open a archived document: " << docId << endl; slotPrintDocument( QString(), dbID( docId.toInt() ) ); } } // Fetch my address const QString myUid = KraftSettings::self()->userUid(); bool useManual = false; if( ! myUid.isEmpty() ) { KContacts::Addressee contact; // qDebug () << "Got My UID: " << myUid; connect( mAddressProvider, SIGNAL( lookupResult(QString,KContacts::Addressee)), this, SLOT( slotReceivedMyAddress(QString, KContacts::Addressee)) ); AddressProvider::LookupState state = mAddressProvider->lookupAddressee( myUid ); switch( state ) { case AddressProvider::LookupFromCache: contact = mAddressProvider->getAddresseeFromCache(myUid); slotReceivedMyAddress(myUid, contact); break; case AddressProvider::LookupNotFound: case AddressProvider::ItemError: case AddressProvider::BackendError: // Try to read from stored vcard. useManual = true; break; case AddressProvider::LookupOngoing: case AddressProvider::LookupStarted: // Not much to do, just wait break; } } else { // in case there is no uid in the settings file, try to use the manual address. useManual = true; } if( useManual ) { // check if the vcard can be read QString file = QStandardPaths::writableLocation( QStandardPaths::AppDataLocation ); file += "/myidentity.vcd"; QFile f(file); if( f.exists() ) { if( f.open( QIODevice::ReadOnly )) { const QByteArray data = f.readAll(); VCardConverter converter; Addressee::List list = converter.parseVCards( data ); if( list.count() > 0 ) { KContacts::Addressee c = list.at(0); c.insertCustom(CUSTOM_ADDRESS_MARKER, "manual"); slotReceivedMyAddress(QString(), c); } } } } connect( &_reportGenerator, &ReportGenerator::docAvailable, this, &Portal::slotDocConverted); connect( &_reportGenerator, &ReportGenerator::failure, this, &Portal::slotDocConvertionFail); slotStatusMsg(); } } void Portal::slotReceivedMyAddress( const QString& uid, const KContacts::Addressee& contact ) { disconnect( mAddressProvider, SIGNAL(lookupResult(QString,KContacts::Addressee)), this, SLOT(slotReceivedMyAddress(QString, KContacts::Addressee))); if( contact.isEmpty() ) { if( !uid.isEmpty() ) { // FIXME: Read the stored Address and compare the uid const QString err = mAddressProvider->errorMsg(uid); qDebug () << "My-Contact could not be found:" << err; } return; } myContact = contact; // qDebug () << "Received my address: " << contact.realName() << "(" << uid << ")"; _reportGenerator.setMyContact( myContact ); QString name = myContact.formattedName(); if( !name.isEmpty() ) { name = i18n("Welcome to Kraft, %1", name); statusBar()->showMessage(name, 30*1000); } } bool Portal::queryClose() { return true; } bool Portal::queryExit() { return true; } ///////////////////////////////////////////////////////////////////// // SLOT IMPLEMENTATION ///////////////////////////////////////////////////////////////////// void Portal::busyCursor( bool on ) { if ( on ) { QApplication::setOverrideCursor( QCursor( Qt::BusyCursor ) ); } else { QApplication::restoreOverrideCursor(); } } void Portal::slotNewDocument() { slotStatusMsg(i18n("Creating new document...")); KraftWizard wiz; wiz.init(true); if ( wiz.exec() ) { DocumentMan *docman = DocumentMan::self(); DocGuardedPtr doc = docman->createDocument( wiz.docType() ); doc->setDate( wiz.date() ); doc->setAddressUid( wiz.addressUid() ); doc->setWhiteboard( wiz.whiteboard() ); createView( doc ); } slotStatusMsg(); } void Portal::slotFollowUpDocument() { const QString locId = m_portalView->docDigestView()->currentDocumentId(); DocGuardedPtr sourceDoc = DocumentMan::self()->openDocument( locId ); DocType dt( sourceDoc->docType() ); KraftWizard wiz; wiz.init( false, i18nc("Dialog title of the followup doc dialog, followed by the id of the source doc", "Create follow up document for %1", sourceDoc->ident())); QStringList followers = dt.follower(); if ( followers.count() > 0 ) { // only if there are currently followers defined, if not the default wiht // all doc types works. wiz.setAvailDocTypes( dt.follower() ); } // qDebug () << "doc identifier: "<< doc->docIdentifier() << endl; wiz.setDocToFollow( sourceDoc ); DocPositionList posToCopy; delete sourceDoc; if ( wiz.exec() ) { QString selectedId = wiz.copyItemsFromPredecessor(); if(!selectedId.isEmpty()) { DocGuardedPtr copyDoc = DocumentMan::self()->openDocument( selectedId ); posToCopy = copyDoc->positions(); delete copyDoc; } DocGuardedPtr doc = DocumentMan::self()->createDocument(wiz.docType(), locId, posToCopy); doc->setDate( wiz.date() ); doc->setWhiteboard( wiz.whiteboard() ); createView( doc ); } } void Portal::slotCopyCurrentDocument() { const QString locId = m_portalView->docDigestView()->currentDocumentId(); slotCopyDocument( locId ); } void Portal::slotCopyDocument( const QString& id ) { if ( id.isEmpty() ) { return; } QString oldDocIdent; DocGuardedPtr oldDoc = DocumentMan::self()->openDocument( id ); if(oldDoc) { const DocType dt = oldDoc->docType(); oldDocIdent = i18nc("Title of the new doc dialog, %1 is the source doc id", "Create new Document as Copy of %1", oldDoc->ident()); delete oldDoc; } KraftWizard wiz; wiz.init(true, oldDocIdent); if ( wiz.exec() ) { DocGuardedPtr doc = DocumentMan::self()->copyDocument(id); doc->setDate( wiz.date() ); doc->setDocType( wiz.docType() ); doc->setWhiteboard( wiz.whiteboard() ); if(doc->addressUid() != wiz.addressUid() ) { doc->setAddress(QString()); } doc->setAddressUid( wiz.addressUid() ); doc->saveDocument(); m_portalView->docDigestView()->slotUpdateView(); // qDebug () << "Document created from id " << id << ", saved with id " << doc->docID().toString() << endl; } } void Portal::slotOpenCurrentDocument() { QString locId = m_portalView->docDigestView()->currentDocumentId(); slotOpenDocument( locId ); } void Portal::slotViewCurrentDocument() { QString locId = m_portalView->docDigestView()->currentDocumentId(); slotViewDocument( locId ); } void Portal::slotViewDocument( const QString& id ) { slotStatusMsg(i18n("Opening document to view...")); if( !id.isEmpty() ) { DocumentMan *docman = DocumentMan::self(); DocGuardedPtr doc = docman->openDocument( id ); createROView( doc ); } slotStatusMsg(); } void Portal::slotOpenArchivedDoc( const ArchDocDigest& d ) { busyCursor( true ); ArchDocDigest digest( d ); const QString file = d.pdfArchiveFileName(); // qDebug () << "archived doc selected: " << file << endl; slotOpenPdf( file ); busyCursor( false ); } void Portal::slotPrintCurrentDocument() { QString locId = m_portalView->docDigestView()->currentDocumentId(); // qDebug () << "printing document " << locId << endl; busyCursor( true ); slotStatusMsg( i18n( "Generating PDF..." ) ); DocumentMan *docman = DocumentMan::self(); _currentDoc = docman->openDocument( locId ); QString ident; if ( _currentDoc ) { ident = _currentDoc->ident(); dbID archID = KraftDB::self()->archiveDocument(_currentDoc); slotPrintDocument( ident, archID ); // m_portalView->docDigestView()->addArchivedItem(docPtr->docID(), archID); } busyCursor( false ); slotStatusMsg( i18n( "Ready." ) ); } void Portal::slotMailDocument() { const QString locId = m_portalView->docDigestView()->currentDocumentId(); // qDebug () << "Mailing document " << locId << endl; slotStatusMsg( i18n( "Generating PDF for EMail" ) ); DocumentMan *docman = DocumentMan::self(); DocGuardedPtr docPtr = docman->openDocument( locId ); QString ident; if ( docPtr ) { ident = docPtr->ident(); dbID archID = KraftDB::self()->archiveDocument( docPtr ); _clientId = docPtr->addressUid(); busyCursor( true ); _reportGenerator.createDocument(ReportFormat::PDFMail, ident, archID ); busyCursor( false ); } slotStatusMsg( i18n( "Ready." ) ); } void Portal::slotDocConvertionFail(const QString& failString) { QMessageBox::warning(this, i18n("Doc Generation Error"),failString); } void Portal::slotDocConverted(ReportFormat format, const QString& file, const KContacts::Addressee& customerContact) { if (format == ReportFormat::PDF) { slotOpenPdf(file); } else if (format == ReportFormat::PDFMail) { openInMailer(file, customerContact); } } void Portal::openInMailer(const QString& fileName, const KContacts::Addressee& contact) { QString mailReceiver; if( !contact.isEmpty() ) { mailReceiver = contact.fullEmail(); // the prefered email } QStringList args; QString prog; if( KraftSettings::self()->mailUA().startsWith("xdg") ) { args.append( "--utf8"); args.append( "--attach"); args.append(_pdfFileName ); if( !mailReceiver.isEmpty() ) { args.append( mailReceiver); } prog = QLatin1String("/usr/bin/xdg-email"); } else { // Fallback to thunderbird prog = QLatin1String("/usr/bin/thunderbird"); args.append("-compose"); QString tmp; if( !mailReceiver.isEmpty() ) { tmp = QString("to=%1,").arg(mailReceiver); } tmp += QString("attachment='file://%1'").arg(fileName); args.append(tmp); } qDebug () << "Starting mailer: " << prog << args; if (!QProcess::startDetached(prog, args)) { qDebug () << "Failed to start thunderbird composer!"; } _pdfFileName.clear(); } /* * id : document ID * archID: database ID of archived document */ void Portal::slotPrintDocument( const QString& id, const dbID& archID ) { if ( archID.isOk() ) { slotStatusMsg(i18n("Printing archived document...") ); _reportGenerator.createDocument(ReportFormat::PDF, id, archID ); // work on document identifier. } } void Portal::slotOpenPdf( const QString& fileName ) { QUrl url( fileName ); QDesktopServices::openUrl(url); } /* * A special document tree is built up here. */ void Portal::savePdfInCustomerStructure(const QString& fileName) { // save pdf into a / structure if( _currentDoc ) { QString uid = _currentDoc->addressUid(); QString docType = _currentDoc->docType(); if( !uid.isEmpty() ) { QString outputDir = KraftSettings::self()->pdfOutputDir(); if ( outputDir.isEmpty() ) { outputDir = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation); } if ( ! outputDir.endsWith( "/" ) ) outputDir += QLatin1String("/"); QDir customerDir(outputDir + QString("%1/%2").arg(uid).arg(docType)); if( !customerDir.exists() ) { customerDir.mkpath( customerDir.absolutePath()); } if( customerDir.exists() ) { QFileInfo fi(fileName); QString target = customerDir.canonicalPath() + QLatin1Char('/') + fi.fileName(); QFileInfo tfi(target); if( tfi.exists() ) { QFile::remove(target); } QFile::copy( fileName, target ); } } } } void Portal::slotOpenDocument( const QString& id ) { if (_readOnlyMode) { slotViewDocument(id); return; } slotStatusMsg( i18n("Opening document %1", id ) ); // qDebug () << "Opening document " << id; if( !id.isEmpty() ) { DocumentMan *docman = DocumentMan::self(); DocGuardedPtr doc = docman->openDocument( id ); createView( doc ); } slotStatusMsg(); } void Portal::slotDocumentSelected( const QString& doc ) { // qDebug() << "a doc was selected: " << doc << endl; bool enable = !doc.isEmpty(); _actViewDocument->setEnabled( enable ); _actOpenDocument->setEnabled( (!_readOnlyMode) && enable ); _actPrintDocument->setEnabled( (!_readOnlyMode) && enable ); _actCopyDocument->setEnabled( (!_readOnlyMode) && enable ); _actMailDocument->setEnabled( (!_readOnlyMode) && enable ); _actFollowDocument->setEnabled( (!_readOnlyMode) && enable ); _actOpenArchivedDocument->setEnabled( enable ); } void Portal::slotArchivedDocExecuted() { ArchDocDigest dig = m_portalView->docDigestView()->currentLatestArchivedDoc(); slotOpenArchivedDoc( dig ); } void Portal::slotArchivedDocSelected( const ArchDocDigest& ) { // slotDocumentSelected( QString() ); _actOpenArchivedDocument->setEnabled( true ); _actViewDocument->setEnabled( false ); _actOpenDocument->setEnabled( false ); _actPrintDocument->setEnabled( false ); _actMailDocument->setEnabled( false ); } void Portal::slotEditTagTemplates() { TagTemplatesDialog dia( this ); if ( dia.exec() ) { // qDebug () << "Editing of tag templates succeeded!" << endl; } } void Portal::slotReconfigureDatabase() { // qDebug () << "Reconfiguring the Database"; SetupAssistant assi(this); if( assi.init( SetupAssistant::Reinit ) ) { assi.exec(); } } void Portal::createView( DocGuardedPtr doc ) { // FIXME: We allow only one view for the first time. // Later allow one write view and other read onlys. if ( !doc ) return; // if there is a read only view already, close it and open a // editor if( mViewMap.contains(doc)) { if( (mViewMap[doc])->type() == KraftViewBase::ReadOnly ) { KraftViewBase *view = mViewMap[doc]; mViewMap.remove(doc); delete view; } } if( !mViewMap.contains(doc) ){ KraftView *view = new KraftView( this ); const QByteArray geo = QByteArray::fromBase64( KraftSettings::self()->docEditGeometry().toAscii() ); view->restoreGeometry(geo); view->setup( doc ); view->redrawDocument(); view->slotSwitchToPage( KraftDoc::Positions ); view->show(); connect( view, SIGNAL( viewClosed( bool, DocGuardedPtr ) ), this, SLOT( slotViewClosed( bool, DocGuardedPtr ) ) ); connect( view, &KraftViewBase::openROView, this, &Portal::slotViewDocument ); mViewMap[doc] = view; } else { mViewMap[doc]->raise(); // pop first view to front // qDebug () << "There is already a view for this doc!" << endl; } } void Portal::createROView( DocGuardedPtr doc ) { if ( !doc ) return; if( !mViewMap.contains(doc)) { KraftViewRO *view = new KraftViewRO( this ); view->setup( doc ); // view->redrawDocument(); const QByteArray geo = QByteArray::fromBase64( KraftSettings::self()->docViewROGeometry().toAscii() ); view->restoreGeometry(geo); view->show(); mViewMap[doc] = view; connect( view, SIGNAL( viewClosed( bool, DocGuardedPtr ) ), this, SLOT( slotViewClosed( bool, DocGuardedPtr ) ) ); } else { mViewMap[doc]->raise(); } } void Portal::slotViewClosed( bool success, DocGuardedPtr doc ) { // doc is only valid on success! if ( doc ) { KraftViewBase *view = mViewMap[doc]; const QByteArray geo = view->saveGeometry().toBase64(); if( success ) { if( view->type() == KraftViewBase::ReadWrite ) { AllDocsView *dv = m_portalView->docDigestView(); dv->slotUpdateView(); KraftSettings::self()->setDocEditGeometry(geo); } else { KraftSettings::self()->setDocViewROGeometry(geo); } } if( mViewMap.contains(doc)) { mViewMap.remove(doc); view->deleteLater(); } // qDebug () << "A view was closed saving and doc is new: " << doc->isNew() << endl; delete doc; } else { // qDebug () << "A view was closed canceled" << endl; } } void Portal::closeEvent( QCloseEvent *event ) { slotStatusMsg(i18n("Exiting...")); // close the first window, the list makes the next one the first again. // This ensures that queryClose() is called on each window to ask for closing //We have to delete katalogviews ourself otherwise the application keeps running in the background QMap::iterator i; for (i = mKatalogViews.begin(); i != mKatalogViews.end(); ++i) { // KatalogView *view = i.value(); // qDebug () << "Windowstate" << view->windowState(); i.value()->deleteLater(); } // FIXME: Close the document windows. const QByteArray state = saveState().toBase64(); KraftSettings::self()->setPortalState(state); const QByteArray geo = saveGeometry().toBase64(); KraftSettings::self()->setPortalGeometry(geo); KraftSettings::self()->save(); if( event ) event->accept(); } void Portal::slotEditCut() { slotStatusMsg(i18n("Cutting selection...")); slotStatusMsg(); } void Portal::slotEditCopy() { slotStatusMsg(i18n("Copying selection to clipboard...")); slotStatusMsg(); } void Portal::slotEditPaste() { slotStatusMsg(i18n("Inserting clipboard contents...")); slotStatusMsg(); } void Portal::slotStatusMsg(const QString &text) { /////////////////////////////////////////////////////////////////// // change status message permanently statusBar()->clearMessage(); if (text.isEmpty()) { if (_readOnlyMode) statusBar()->showMessage(i18n("Ready. Kraft is running in read only mode. Document editing is prohibited.")); else statusBar()->showMessage(i18n("Ready.")); } else { statusBar()->showMessage(text); } } /** Show the window with floskeltemplates */ void Portal::slotShowTemplates(){ } void Portal::slotOpenKatalog(const QString& kat) { // qDebug () << "opening Katalog " << kat << endl; if ( mKatalogViews.contains( kat ) ) { // bring up the katalog view window. // qDebug () << "Katalog " << kat << " already open in a view" << endl; mKatalogViews.value(kat)->show(); mKatalogViews.value(kat)->raise(); } else { QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); KatalogView *katView = nullptr; if( kat == MaterialKatalogView::MaterialCatalogName ) { /* Materialkatalog */ katView = new MaterialKatalogView(); } else { /* normaler Vorlagenkatalog */ katView = new TemplKatalogView(); } if ( katView ) { // qDebug () << katView; katView->init(kat); katView->show(); mKatalogViews.insert(kat, katView); KatalogMan::self()->registerKatalogListView( kat, katView->getListView() ); } QApplication::restoreOverrideCursor(); } } void Portal::slotOpenKatalog() { // qDebug () << "opening katalog!" << endl; KatalogView *katView = new TemplKatalogView(); //this); katView->show(); } void Portal::slotKatalogToXML(const QString& katName) { // qDebug () << "Generating XML for catalog " << katName << endl; Katalog *kat = KatalogMan::self()->getKatalog(katName); if(kat) { kat->writeXMLFile(); } } QString Portal::textWrap( const QString& t, int width, int maxLines ) { QString re; int lines = 0; if( t.length() <= width ) { re = t; } else { int start = 0; int pos = width; while( pos < t.length() && (lines < maxLines || maxLines < 0) ) { pos = t.indexOf( QLatin1Char('\n'), start ); if( pos > -1 && (pos-start) < width ) { re += t.mid(start, pos-start)+QLatin1Char('\n'); start = pos+1; } else { pos = t.indexOf( QLatin1Char(' '), start+width ); if( pos > -1 ) { re += t.mid( start, pos-start)+QLatin1Char('\n'); start = pos+1; } else { re += t.mid( start ); pos = t.length(); } } lines++; } if( lines == maxLines && pos != t.length() ) { re += QStringLiteral("..."); } } return re; } void Portal::preferences() { _prefsDialog = new PrefsDialog(this); connect( _prefsDialog, SIGNAL(finished(int)), SLOT(slotPrefsDialogFinished(int)) ); connect( _prefsDialog, SIGNAL(newOwnIdentity(const QString&, KContacts::Addressee)), SLOT(slotReceivedMyAddress(QString,KContacts::Addressee))); _prefsDialog->setMyIdentity( myContact, mAddressProvider->backendUp() ); _prefsDialog->open(); } void Portal::slotPrefsDialogFinished( int result ) { if( result == QDialog::Accepted) { } _prefsDialog->deleteLater(); } void Portal::slotHandbook() { QUrl url; QLocale *loc = DefaultProvider::self()->locale(); QString hbLocale; if (loc) { hbLocale = loc->bcp47Name(); } // find the localized version QString hbFile = DefaultProvider::self()->locateFile(QString("manual/kraft-%1.html").arg(hbLocale)); // if not found, fall back to the english manual QFileInfo fi(hbFile); if (hbFile.isEmpty() || !fi.exists()) { hbFile = DefaultProvider::self()->locateFile(QStringLiteral("manual/kraft-en.html")); } if( !hbFile.isEmpty() ) { url = QUrl::fromLocalFile(hbFile); qDebug() << "opening manual url" << url.toString(); } if (!url.isEmpty()) { QDesktopServices::openUrl(url); } } void Portal::slotAboutQt() { QApplication::aboutQt(); } void Portal::slotAboutKraft() { m_portalView->displaySystemsTab(); } QWidget* Portal::mainWidget() { return m_portalView.data(); } kraft-0.97/src/portal.h000066400000000000000000000140251410616450300150230ustar00rootroot00000000000000/*************************************************************************** portal.h - Portal view for Kraft ------------------- begin : Mar 2006 copyright : (C) 2006 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef PORTAL_H #define PORTAL_H // include files for Qt #include #include #include #include #include #include "docguardedptr.h" #include "katalogview.h" #include "dbids.h" #include "portalview.h" #include "reportgenerator.h" class KraftViewBase; class ArchDocDigest; class AddressProvider; class PrefsDialog; /** */ class Portal : public QMainWindow { Q_OBJECT friend class KraftView; public: /** construtor of Portal, calls all init functions to create the application. */ Portal( QWidget* parent = 0, QCommandLineParser *commandLineParser = 0, const char* name = 0); static QString textWrap(const QString& t, int width=40, int maxLines = -1 ); QWidget* mainWidget(); protected: /** initializes the QActions of the application */ void initActions(); /** creates the centerwidget of the KTMainWindow instance and sets it as the view */ void initView(); /** queryClose is called by KTMainWindow on each closeEvent of a window. Against the * default implementation (only returns true), this calles saveModified() on the document object to ask if the document shall * be saved if Modified; on cancel the closeEvent is rejected. * @see KTMainWindow#queryClose * @see KTMainWindow#closeEvent */ virtual bool queryClose(); /** queryExit is called by KTMainWindow when the last window of the application is going to be closed during the closeEvent(). * Against the default implementation that just returns true, this calls saveOptions() to save the settings of the last window's * properties. * @see KTMainWindow#queryExit * @see KTMainWindow#closeEvent */ virtual bool queryExit(); protected slots: void slotStartupChecks(); void slotOpenArchivedDoc( const ArchDocDigest& ); void slotPrefsDialogFinished( int ); void slotDocConverted(ReportFormat format, const QString& file, const KContacts::Addressee& customerContact); void slotDocConvertionFail(const QString& failString); void openInMailer(const QString& fileName, const KContacts::Addressee& contact); public slots: /** closes all open windows, then quits the application. */ void closeEvent( QCloseEvent * event ); /** put the marked text/object into the clipboard and remove * it from the document */ void slotEditCut(); /** put the marked text/object into the clipboard */ void slotEditCopy(); /** paste the clipboard into the document */ void slotEditPaste(); /** changes the statusbar contents for the standard label permanently, used to indicate current actions. * @param text the text that is displayed in the statusbar */ void slotStatusMsg(const QString &text = QString()); /** Show the window with floskeltemplates */ void slotShowTemplates(); void slotOpenKatalog(const QString& ); void slotOpenKatalog(); void slotKatalogToXML(const QString&); void preferences(); void slotNewDocument(); void slotCopyCurrentDocument(); void slotCopyDocument( const QString& ); void slotOpenDocument( const QString& ); void slotOpenCurrentDocument(); void slotViewCurrentDocument(); void slotViewDocument( const QString& ); void slotFollowUpDocument(); void slotDocumentSelected( const QString& ); void slotArchivedDocExecuted(); void slotArchivedDocSelected( const ArchDocDigest& ); void slotPrintCurrentDocument(); void slotPrintDocument( const QString&, const dbID& ); void slotViewClosed( bool, DocGuardedPtr ); void slotEditTagTemplates(); void slotReconfigureDatabase(); void slotAboutQt(); void slotAboutKraft(); void slotHandbook(); void busyCursor( bool ); void slotMailDocument(); void slotOpenPdf( const QString& ); void slotReceivedMyAddress( const QString&, const KContacts::Addressee& ); private: void createView( DocGuardedPtr ); void createROView( DocGuardedPtr ); void savePdfInCustomerStructure(const QString& fileName); QScopedPointer m_portalView; // QAction pointers to enable/disable actions QAction* _actFileQuit; QAction* _actEditCut; QAction* _actEditCopy; QAction* _actEditPaste; QAction* _actAboutQt; QAction* _actAboutKraft; QAction* _actHandbook; QAction* _actPreferences; QAction* _actReconfDb; QAction* _actNewDocument; QAction* _actCopyDocument; QAction* _actOpenDocument; QAction* _actViewDocument; QAction* _actFollowDocument; QAction* _actPrintDocument; QAction* _actMailDocument; QAction* _actEditTemplates; QAction* _actOpenArchivedDocument; QAction* _actViewFlosTemplates; QCommandLineParser *mCmdLineArgs; QMap mKatalogViews; QMap mViewMap; QString _clientId; QString _pdfFileName; AddressProvider *mAddressProvider; KContacts::Addressee myContact; PrefsDialog *_prefsDialog; DocGuardedPtr _currentDoc; ReportGenerator _reportGenerator; bool _readOnlyMode; }; #endif kraft-0.97/src/portalhtmlview.cpp000066400000000000000000000032371410616450300171410ustar00rootroot00000000000000/*************************************************************************** portalhtmlview.cpp - show a html page in the portal ------------------- begin : Jan 2007 copyright : (C) 2007 Klaas Freitag ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include "portalhtmlview.h" #include #include PortalHtmlView::PortalHtmlView( QWidget *parent ) : HtmlView( parent ) { connect( this, SIGNAL(openUrl(QUrl)), this, SLOT(slotLinkClicked(QUrl))); } void PortalHtmlView::slotLinkClicked(const QUrl& url) { QUrlQuery q(url); const QString katName = q.queryItemValue(QLatin1String("kat")); const QString action = q.queryItemValue(QLatin1String("action")); if ( action == QLatin1String("open") ) { // qDebug () << "open catalog " << katName << endl; emit( openCatalog( katName ) ); } else { if( url.isValid() ) { QDesktopServices::openUrl(url); } } } kraft-0.97/src/portalhtmlview.h000066400000000000000000000024121410616450300166000ustar00rootroot00000000000000 /*************************************************************************** portalhtmlview.h - show a html page in the portal ------------------- begin : Jan 2007 copyright : (C) 2007 Klaas Freitag ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef PORTALHTMLVIEW_H #define PORTALHTMLVIEW_H #include "htmlview.h" class PortalHtmlView : public HtmlView { Q_OBJECT public: PortalHtmlView( QWidget *parent ); signals: void openCatalog( const QString& ); protected slots: void slotLinkClicked(const QUrl& url); }; #endif kraft-0.97/src/portalview.cpp000066400000000000000000000366401410616450300162600ustar00rootroot00000000000000/*************************************************************************** portalview.cpp - the main portal class ------------------- begin : 2004-05-09 copyright : (C) 2004 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ // include files for Qt #include #include #include #include #include #include #include #include "version.h" #include "kraftdb.h" #include "portalview.h" #include "portalhtmlview.h" #include "katalogman.h" #include "alldocsview.h" #include "documentman.h" #include "defaultprovider.h" #include "reportgenerator.h" #include "texttemplate.h" PortalView::PortalView(QWidget *parent, const char*) : QWidget( parent ), _allDocsView(nullptr), mCatalogBrowser(nullptr), mSystemBrowser(nullptr) { _contentsWidget = new QListWidget(this); _contentsWidget->setViewMode(QListView::IconMode); _contentsWidget->setIconSize(QSize(96, 84)); _contentsWidget->setMovement(QListView::Static); _contentsWidget->setMaximumWidth(128); _contentsWidget->setSpacing(12); _pagesWidget = new QStackedWidget(this); _pagesWidget->addWidget(documentDigests()); _pagesWidget->addWidget(new QWidget()); // doc timeline _pagesWidget->addWidget(katalogDetails()); // catalogs _sysPageIndx = _pagesWidget->addWidget(systemDetails()); // system createIcons(); _contentsWidget->setCurrentRow(0); QHBoxLayout *horizontalLayout = new QHBoxLayout; QVBoxLayout *vbox = new QVBoxLayout; vbox->addWidget(_contentsWidget); QPushButton *pb = new QPushButton(i18n("About Kraft")); pb->setIcon(QIcon::fromTheme("kraft", QIcon(":/kraft/global/kraft_small_arm.png"))); vbox->addWidget(pb); connect(pb, &QPushButton::clicked, this, &PortalView::displaySystemsTab); horizontalLayout->addLayout(vbox); horizontalLayout->addWidget(_pagesWidget, 1); setLayout(horizontalLayout); } PortalView::~PortalView( ) { } void PortalView::createIcons() { QListWidgetItem *documentsButton = new QListWidgetItem(_contentsWidget); documentsButton->setIcon(QIcon(":/kraft/document.svg")); documentsButton->setText(i18n("Documents")); documentsButton->setTextAlignment(Qt::AlignHCenter); documentsButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); QListWidgetItem *timeLineButton = new QListWidgetItem(_contentsWidget); timeLineButton->setIcon(QIcon(":/kraft/document-clock.svg")); timeLineButton->setText(i18n("Timeline")); timeLineButton->setTextAlignment(Qt::AlignHCenter); timeLineButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); QListWidgetItem *catButton = new QListWidgetItem(_contentsWidget); catButton->setIcon(QIcon(":/kraft/book-open.svg")); catButton->setText(i18n("Catalogs")); catButton->setTextAlignment(Qt::AlignHCenter); catButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); connect( _contentsWidget, &QListWidget::itemClicked, this, &PortalView::changePage); } void PortalView::changePage(QListWidgetItem *current) { if (!current) return; int indx = _contentsWidget->row(current); if( indx == 0 ) { // the flat documents list _allDocsView->setView( AllDocsView::FlatList ); } else if( indx == 1 ) { // the timeline _allDocsView->setView( AllDocsView::TreeView ); indx = 0; } _pagesWidget->setCurrentIndex(indx); } void PortalView::displaySystemsTab() { _pagesWidget->setCurrentIndex(_sysPageIndx); } QWidget* PortalView::katalogDetails() { QWidget *w = new QWidget; QBoxLayout *b = new QHBoxLayout; w->setLayout( b ); mCatalogBrowser = new PortalHtmlView( w ); mCatalogBrowser->setTitle( i18n( "Kraft Document Overview" ) ); mCatalogBrowser->setStylesheetFile( "catalogview.css" ); b->addWidget( mCatalogBrowser ); QString html; html = "

            " + i18n("Available Catalogs") + "

            "; html += "
            \n"; html += i18n( "No catalogs available." ); html += "
            "; mCatalogBrowser->displayContent( html ); connect( mCatalogBrowser, SIGNAL( openCatalog( const QString& ) ), SIGNAL( openKatalog( const QString& ) ) ); return w; } void PortalView::fillCatalogDetails() { if ( ! mCatalogBrowser ) return; const QStringList katalogNamen = KatalogMan::self()->allKatalogNames(); QString html; html = QStringLiteral("

            ") + i18n("Available Catalogs") + QStringLiteral("

            "); html += QStringLiteral("
            \n"); int cnt = 0; for(QStringList::ConstIterator namesIt = katalogNamen.begin(); namesIt != katalogNamen.end(); ++namesIt ) { QString katName = *namesIt; html += printKatLine( katName, cnt++ ); } html += QStringLiteral("
            \n"); mCatalogBrowser->displayContent( html ); } QString PortalView::printKatLine( const QString& name, int cnt ) const { QString urlName = name.toHtmlEscaped(); // qDebug () << "Converted Katalog name: " << urlName << endl; QString html; html += "
            "+urlName+"
            "; html += ""; html += i18n("Open"); html += ""; KatalogMan::CatalogDetails details = KatalogMan::self()->catalogDetails(name); html += "%1").arg(i18n("No templates yet.")); } else { QLocale *locale = DefaultProvider::self()->locale(); QString dateStr = locale->toString(details.maxModDate); html += QString("") + i18n("%1 templates in %2 chapters
            last modified at %3", details.countEntries, details.countChapters, dateStr) + QLatin1String(""); } #if 0 html += ""; html += i18n("XML Export"); html += ""; html += ""; html += i18n("Remove"); html += ""; #endif html += "\n"; return html; } QString PortalView::ptag( const QString& content, const QString& c ) const { QString html( "setLayout( b ); mSystemBrowser = new PortalHtmlView( w ); b->addWidget( mSystemBrowser ); mSystemBrowser->setStylesheetFile( "systemview.css" ); return w; } QString PortalView::systemView( const QString& htmlMsg ) const { if ( ! mSystemBrowser ) return QString (""); const QString templateName = ( htmlMsg.isNull() ? QString( "views/systemviewdetails.thtml" ) : QString ( "views/systemviewerror.thtml" ) ); const QString tmplFile = DefaultProvider::self()->locateFile( templateName ); // Note: This code is stolen from DocDigestDetailView::slotShowDocDetails // It should be refactored. TextTemplate tmpl; tmpl.setTemplateFileName(tmplFile); if( !tmpl.isOk() ) { return QString (""); } const QString logoFile = DefaultProvider::self()->locateFile("styles/pics/kraftapp_logo_trans.png" ); tmpl.setValue( "KRAFT_LOGO_FILE", logoFile ); tmpl.setValue( "KRAFT_WEBSITE", i18n( "Kraft Website" ) ); QDate d = QDate::currentDate(); tmpl.setValue( "KRAFT_COPYRIGHT_YEAR", QString::number(d.year()) ); tmpl.setValue( "KRAFT_LICENSE_TEXT", i18nc("The string is followed by a link to the GPL2 text", "Kraft is free software licensed under the")); tmpl.setValue( "KRAFT_GITHUB", i18nc("The string is followed by the link to github", "Kraft is maintained on ")); tmpl.setValue( "KRAFT_AUTHORS", i18n("Authors")); tmpl.setValue( "KRAFT_MAINTAINER", i18n("Developer and Maintainer")); tmpl.setValue( "KRAFT_DEVELOPER", i18n("Developer")); tmpl.setValue( "KRAFT_GRAPHICS", i18nc("The person who provided the logo graphics", "Logo design")); tmpl.setValue( "KRAFT_MANUAL", i18nc("The person who provided the user manual", "User Manual")); // kraft infos tmpl.setValue("KRAFT_INTRO_DESC", i18n("Kraft helps you to handle documents like quotes and invoices in your small business.")); tmpl.setValue( "KRAFT_WELCOME_LABEL", i18n( "Welcome to Kraft" ) ); tmpl.setValue( "KRAFT_VERSION_LABEL", i18n( "Kraft Version" ) ); tmpl.setValue( "KRAFT_VERSION", KRAFT_VERSION ); tmpl.setValue( "KRAFT_CODENAME_LABEL", i18n( "Codename" ) ); tmpl.setValue( "KRAFT_CODENAME", KRAFT_CODENAME ); const QString countryName = DefaultProvider::self()->locale()->nativeCountryName(); tmpl.setValue( "COUNTRY_SETTING_LABEL", i18n( "Country Setting" ) ); tmpl.setValue( "COUNTRY_SETTING", QString( "%1 (%2)" ).arg( countryName ).arg( DefaultProvider::self()->locale()->country() )); const QString languageName = DefaultProvider::self()->locale()->nativeLanguageName(); tmpl.setValue( "LANGUAGE_SETTING_LABEL", i18n( "Language Setting" ) ); tmpl.setValue( "LANGUAGE_SETTING", QString( "%1 (%2)" ).arg( languageName ).arg( DefaultProvider::self()->locale()->language() )); if ( !htmlMsg.isNull() ) { tmpl.setValue( "ERROR_TITLE_LABEL", i18n( "Kraft Initialisation Problem" ) ); QString errorMessage = i18n( "There is a initialisation error on your system. Kraft will not work that way." ); errorMessage += htmlMsg; tmpl.setValue( "ERROR_TEXT", errorMessage ); return tmpl.expand(); } // database infos tmpl.setValue( "DATABASE_TITLE_LABEL", i18n( "Database Information" ) ); tmpl.setValue( "DATABASE_NAME_LABEL", i18n( "Kraft database name" ) ); tmpl.setValue( "DATABASE_NAME", KraftDB::self()->databaseName() ); QString schemaVersion = QString::number( KraftDB::self()->currentSchemaVersion() ); if ( KraftDB::self()->currentSchemaVersion() != KRAFT_REQUIRED_SCHEMA_VERSION ) { schemaVersion += " - " + QString( "%1: %2" ).arg( i18n ( "Required Version" )) .arg( KRAFT_REQUIRED_SCHEMA_VERSION ); } tmpl.setValue( "DATABASE_SCHEMA_VERSION_LABEL", i18n( "Database schema version" ) ); tmpl.setValue( "DATABASE_SCHEMA_VERSION", schemaVersion ); tmpl.setValue( "DATABASE_DRIVER_LABEL", i18n( "Qt database driver" ) ); tmpl.setValue( "DATABASE_DRIVER", KraftDB::self()->qtDriver() ); bool dbOk = KraftDB::self()->getDB()->isOpen(); const QString databaseConnection = ( dbOk ? i18n("established") : QString( "%1" ).arg( i18n( "NOT AVAILABLE!" ) ) ); tmpl.setValue( "DATABASE_CONNECTION_LABEL", i18n( "Database connection" ) ); tmpl.setValue( "DATABASE_CONNECTION", databaseConnection ); if( dbOk ) { QSqlQuery q("SHOW VARIABLES like 'version';"); if( q.isActive() ) { q.next(); const QString version = q.value(1).toString(); tmpl.createDictionary("DATABASE_VERSION_SECTION"); tmpl.setValue( "DATABASE_VERSION_SECTION", "DATABASE_VERSION_LABEL", i18n( "Database Version" ) ); tmpl.setValue( "DATABASE_VERSION_SECTION", "DATABASE_VERSION", version ); } } // Akonadi and friends QScopedPointer aprov; aprov.reset( new AddressProvider); tmpl.setValue( "ADDRESSBOOK_BACKEND_LABEL", i18n( "Addressbook Backend" ) ); tmpl.setValue( "ADDRESSBOOK_BACKEND_TYPE_LABEL", i18n( "Backend type" ) ); const QString backendTypeValue = QString( "%1 (%2)").arg( aprov->backendName()) .arg(aprov->backendUp() ? i18n("running") : i18n("not running") ); tmpl.setValue( "ADDRESSBOOK_BACKEND_TYPE", backendTypeValue ); // external tools tmpl.setValue( "EXTERNAL_TOOLS_LABEL", i18n( "External Tools" ) ); tmpl.setValue( "RML2PDF_TOOL_LABEL", i18n( "RML to PDF conversion tool" ) ); QStringList trml2pdf = DefaultProvider::self()->findTrml2Pdf(); QString trml2pdfValue = (trml2pdf.count() ? trml2pdf.join(" ") : i18n("not found!") ); tmpl.setValue( "RML2PDF_TOOL", trml2pdfValue ); tmpl.setValue( "ICONV_TOOL_LABEL", i18n( "iconv tool for text import" ) ); tmpl.setValue( "ICONV_TOOL", DefaultProvider::self()->iconvTool() ); tmpl.setValue( "WEASYPRINT_TOOL_LABEL", i18n( "weasyprint for PDF generation" ) ); QString wp = DefaultProvider::self()->locateBinary("weasyprint"); if (wp.isEmpty()) { wp = i18n("not available"); } tmpl.setValue( "WEASYPRINT_TOOL", wp); // aknowledgement tmpl.setValue( "ICON_ACKNOWLEDGEMENT_LABEL", i18n("Some Icons are made by") ); tmpl.setValue( "ACKNOWLEGEMENT_LABEL", i18n( "Acknowledgements" ) ); return tmpl.expand(); } void PortalView::fillSystemDetails() { const QString html = systemView( QString() ); mSystemBrowser->displayContent( html ); } void PortalView::systemInitError( const QString& htmlMsg ) { const QString html = systemView( htmlMsg ); mSystemBrowser->displayContent( html ); } QWidget* PortalView::documentDigests() { QWidget *w = new QWidget; QBoxLayout *b = new QHBoxLayout; b->setContentsMargins( 0, 0, 0, 0 ); w->setLayout( b ); _allDocsView = new AllDocsView( w ); b->addWidget( _allDocsView ); connect( _allDocsView, SIGNAL( createDocument() ), this, SLOT( slotCreateDocument() ) ); connect( _allDocsView, SIGNAL( openDocument( const QString& ) ), SIGNAL( openDocument( const QString& ) ) ); connect( _allDocsView, SIGNAL( viewDocument( const QString& ) ), SIGNAL( viewDocument( const QString& ) ) ); connect( _allDocsView, SIGNAL( copyDocument( const QString& ) ), SIGNAL( copyDocument( const QString& ) ) ); connect( _allDocsView, SIGNAL( openArchivedDocument( const ArchDocDigest& ) ), SIGNAL( openArchivedDocument( const ArchDocDigest& ) ) ); connect( _allDocsView, SIGNAL( docSelected( const QString& ) ), SIGNAL( documentSelected( const QString& ) ) ); connect( _allDocsView, SIGNAL( openArchivedDocument( const ArchDocDigest& ) ), SIGNAL( archivedDocSelected( const ArchDocDigest& ) ) ); return w; } void PortalView::slotCreateDocument() { // this slot is called if the user wants to initiate the creation of a new doc // It is routed to higher layers. emit createDocument(); } void PortalView::slotBuildView() { // QApplication::setOverrideCursor( QCursor( Qt::WaitCursor ) ); _allDocsView->slotBuildView(); // QApplication::restoreOverrideCursor(); } /* END */ kraft-0.97/src/portalview.h000066400000000000000000000050461410616450300157210ustar00rootroot00000000000000/*************************************************************************** portalview.h ------------------- begin : 2004-05-09 copyright : (C) 2004 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef _PORTALVIEW_H #define _PORTALVIEW_H #include #include #include // include files #include "docguardedptr.h" /** * */ class QWidget; class AllDocsView; class dbID; class PortalHtmlView; class ArchDocDigest; class PortalView : public QWidget { Q_OBJECT public: PortalView (QWidget *parent=0, const char *name=0 ); ~PortalView(); AllDocsView* docDigestView() { return _allDocsView; } void systemInitError( const QString& ); QString ptag( const QString&, const QString& c = QString() ) const; public slots: void slotBuildView(); void fillCatalogDetails(); void fillSystemDetails(); void displaySystemsTab(); protected slots: void slotCreateDocument(); private slots: void changePage(QListWidgetItem *current); signals: void openKatalog( const QString& ); void katalogToXML( const QString& ); void createDocument(); void openDocument( const QString& ); void copyDocument( const QString& ); void viewDocument( const QString& ); void openArchivedDocument( const ArchDocDigest& ); void documentSelected( const QString& ); void archivedDocSelected( const ArchDocDigest& ); private: QString printKatLine( const QString&, int ) const; void createIcons(); QWidget *katalogDetails(); QWidget *systemDetails(); QWidget *documentDigests(); QString systemView( const QString& ) const; AllDocsView *_allDocsView; PortalHtmlView *mCatalogBrowser; PortalHtmlView *mSystemBrowser; QListWidget *_contentsWidget; QStackedWidget *_pagesWidget; int _sysPageIndx; }; #endif /* END */ kraft-0.97/src/positionviewwidget.cpp000066400000000000000000000513521410616450300200240ustar00rootroot00000000000000/*************************************************************************** positionviewwidget - inherited class for doc position views. ------------------- begin : 2006-02-20 copyright : (C) 2006 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include #include #include #include #include #include #include #include #include "positionviewwidget.h" #include "unitmanager.h" #include "geld.h" #include "kraftsettings.h" #include "defaultprovider.h" #include "kraftdb.h" #include "itemtagdialog.h" #include "tagman.h" PositionViewWidget::PositionViewWidget() :QWidget(), Ui_positionWidget(), mModified( false ), m_skipModifiedSignal( false ), mToDelete(false), mOrdNumber(0), mPositionPtr( 0 ), mExecPopup( new QMenu( this ) ) , mStateSubmenu( 0 ), mState( Active ), mKind( Normal ), mPositionPriceValid( false ), mLocale( 0 ) { setupUi( this ); m_sbUnitPrice->setMinimum( -999999.99 ); m_sbUnitPrice->setMaximum( 999999.99 ); m_sbUnitPrice->setDecimals( 2 ); const QString currSymbol = DefaultProvider::self()->locale()->currencySymbol(); m_sbUnitPrice->setSuffix(" " + currSymbol); m_sbAmount->setMinimum( -999999.99 ); m_sbAmount->setMaximum( 999999.99 ); m_sbAmount->setDecimals( 2 ); mDiscountPercent->setMinimum( -100.0 ); mDiscountPercent->setMaximum( 9999.99 ); mDiscountPercent->setDecimals( 2 ); pbExec->setCheckable( false ); pbExec->setIcon( QIcon::fromTheme( "configure") ); pbTagging->setCheckable( false ); pbTagging->setIcon( QIcon::fromTheme( "flag" ) ); connect( m_sbAmount, SIGNAL( valueChanged( double )), this, SLOT( slotRefreshPrice( ) ) ); connect( m_sbUnitPrice, SIGNAL( valueChanged( double )), this, SLOT( slotRefreshPrice( ) ) ); connect( mDiscountPercent, SIGNAL( valueChanged( double ) ), this, SLOT( slotRefreshPrice() ) ); connect( pbExec, SIGNAL( pressed() ), this, SLOT( slotExecButtonPressed() ) ); connect( pbTagging, SIGNAL( pressed() ), this, SLOT( slotTaggingButtonPressed() ) ); /* modified signals */ connect( m_cbUnit, SIGNAL( activated(int) ), this, SLOT( slotModified() ) ); connect( m_teFloskel, SIGNAL( textChanged() ), this, SLOT( slotModified() ) ); connect( m_sbAmount, SIGNAL( valueChanged(double)), this, SLOT( slotModified() ) ); connect( m_sbUnitPrice, SIGNAL( valueChanged(double)), this, SLOT( slotModified() ) ); connect( mDiscountPercent, SIGNAL( valueChanged( double ) ), this, SLOT( slotModified() ) ); connect( mDiscountTag, SIGNAL( activated( int ) ), this, SLOT( slotModified() ) ); mExecPopup->setTitle(i18n("Item Actions") ); // state submenu: mStateSubmenu = mExecPopup->addMenu(i18n( "Item Kind" )); mStateSubmenu->addAction( i18n( "Normal" ), this, SIGNAL( positionStateNormal() ) ); mStateSubmenu->addAction( QIcon::fromTheme( "kraft_alternative" ), i18n( "Alternative" ), this, SIGNAL( positionStateAlternative() ) ); mStateSubmenu->addAction( QIcon::fromTheme( "kraft_demand" ), i18n( "On Demand" ), this, SIGNAL( positionStateDemand() ) ); // mExecPopup->addSeparator(); // mTaxSubMenu mTaxSubmenu = mExecPopup->addMenu(i18n( "Tax" )); QActionGroup *agroup = new QActionGroup( this ); agroup->setExclusive ( true ); mNilTaxAction = new QAction( QIcon::fromTheme("kraft_notax"), i18n("Taxfree Item"), this ); connect( mNilTaxAction, SIGNAL(triggered()), this, SLOT(slotSetNilTax()) ); mNilTaxAction->setCheckable( true ); agroup->addAction( mNilTaxAction ); mTaxSubmenu->addAction( mNilTaxAction ); mRedTaxAction = new QAction( QIcon::fromTheme("kraft_redtax"), i18n("Reduced Tax"), this ); connect( mRedTaxAction, SIGNAL(triggered()), this, SLOT(slotSetReducedTax())); mRedTaxAction->setCheckable( true ); agroup->addAction( mRedTaxAction ); mTaxSubmenu->addAction( mRedTaxAction ); mFullTaxAction = new QAction( QIcon::fromTheme("kraft_fulltax"), i18n("Full Tax"), this ); connect( mFullTaxAction, SIGNAL(triggered()), this, SLOT(slotSetFullTax())); mFullTaxAction->setCheckable( true ); agroup->addAction( mFullTaxAction ); mTaxSubmenu->addAction( mFullTaxAction ); mExecPopup->addSeparator(); mExecPopup->addAction( QIcon::fromTheme("arrow-up"), i18n("Move Up"), this, SIGNAL( moveUp() ) ); mExecPopup->addAction( QIcon::fromTheme("arrow-down"), i18n("Move Down"), this, SIGNAL( moveDown() ) ); mLockId = mExecPopup->addAction( QIcon::fromTheme("object-locked"), i18n("Lock Item"), this, SIGNAL( lockPosition() ) ); mUnlockId = mExecPopup->addAction( QIcon::fromTheme("object-unlocked"), i18n("Unlock Item"), this, SIGNAL( unlockPosition() ) ); mDeleteId = mExecPopup->addAction( QIcon::fromTheme("edit-delete"), i18n("Delete Item"), this, SIGNAL( deletePosition() ) ); connect( this, SIGNAL( positionStateNormal() ), this, SLOT( slotSetPositionNormal() ) ); connect( this, SIGNAL( positionStateAlternative() ), this, SLOT( slotSetPositionAlternative() ) ); connect( this, SIGNAL( positionStateDemand() ), this, SLOT( slotSetPositionDemand() ) ); connect( this, SIGNAL( lockPosition() ), this, SLOT( slotLockPosition() ) ); connect( this, SIGNAL( unlockPosition() ), this, SLOT( slotUnlockPosition() ) ); connect( mExecPopup, SIGNAL( aboutToShow() ), this, SLOT( slotMenuAboutToShow() ) ); connect( mExecPopup, SIGNAL( aboutToHide() ), this, SLOT( slotMenuAboutToHide() ) ); mUnlockId->setEnabled(false); lStatus->setPixmap( QPixmap() ); lKind->setPixmap( QPixmap() ); this->setAutoFillBackground(true); this->setBaseSize(this->width(), 100); this->layout()->setMargin( 6 ); } void PositionViewWidget::setDocPosition( DocPositionBase *dp) { if( ! dp ) { qCritical() << "setDocPosition got empty position!" << endl; return; } DocPosition *pos = static_cast(dp); mPositionPtr = pos; m_skipModifiedSignal = true; m_teFloskel->setText( pos->text() ); lStatus->hide(); lKind->hide(); AttributeMap amap = dp->attributes(); QString unit = pos->unit().einheitSingular(); m_cbUnit->setCurrentIndex(m_cbUnit->findText( unit )); if( dp->type() == DocPositionBase::Position ) { positionDetailStack->setCurrentWidget( positionPage ); m_sbAmount->blockSignals( true ); m_sbAmount->setValue( pos->amount() ); m_sbAmount->blockSignals( false ); m_sbUnitPrice->blockSignals( true ); m_sbUnitPrice->setValue( pos->unitPrice().toDouble() ); m_sbUnitPrice->blockSignals( false ); if ( amap.contains( DocPosition::Kind ) ) { Attribute kind = amap[DocPosition::Kind]; const QString kindStr = kind.value().toString(); if ( kindStr == kindString( Normal ) ) { slotSetPositionNormal(); } else if ( kindStr == kindString( Alternative ) ) { slotSetPositionAlternative(); } else if ( kindStr == kindString( Demand ) ) { slotSetPositionDemand(); } else { // qDebug () << "Unknown position kind!" << endl; } } // qDebug () << "Setting position ptr. in viewwidget: " << pos << endl; } else if ( dp->type() == DocPositionBase::ExtraDiscount ) { positionDetailStack->setCurrentWidget( discountPage ); // qDebug() << " " << dp->type()<< endl; Attribute discount = amap[DocPosition::Discount]; mDiscountPercent->setValue( discount.value().toDouble() ); QString selTag; if ( amap.contains( DocPosition::ExtraDiscountTagRequired ) ) { Attribute tagSelector = amap[DocPosition::ExtraDiscountTagRequired]; selTag = tagSelector.value().toString(); } /* Fill and set the extra discount selection combo */ const QString allPos = i18n( "All items" ); mDiscountTag->addItem( allPos ); // , i18n( "Overall Position Discount" ) ); QStringList taglist = TagTemplateMan::self()->allTagTemplates(); QString currentEntry = allPos; for ( QStringList::Iterator tagIt = taglist.begin(); tagIt != taglist.end(); ++tagIt ) { QString tagger; TagTemplate tmpl = TagTemplateMan::self()->getTagTemplate( *tagIt ); QPixmap pix( 16, 12 ); pix.fill( tmpl.color() ); tagger = i18n( "%1-tagged items", *tagIt ); mDiscountTag->addItem(pix, tagger); if ( selTag == *tagIt ) { currentEntry = tagger; } } mDiscountTag->setCurrentIndex(mDiscountTag->findText( currentEntry )); } else { // qDebug () << "unknown doc position type " << dp->type()<< endl; } slotSetOverallPrice( currentPrice() ); // set tags marked mTags = dp->tags(); slotUpdateTagToolTip(); slotSetTax( dp->taxType() ); m_skipModifiedSignal = false; } void PositionViewWidget::slotShowPrice( bool show ) { m_sumLabel->setVisible(show); m_sbUnitPrice->setVisible(show); } void PositionViewWidget::slotUpdateTagToolTip() { QString tip; bool first = true; if ( mTags.count() == 1 ) { tip = i18n( "Tag: %1", mTags.first() ); } else if ( mTags.count() > 1 ) { tip = i18n( "Tags:
              " ); for ( QStringList::Iterator it = mTags.begin(); it != mTags.end(); ++it ) { if ( first ) { tip += QString( "
            • %1
            • " ).arg( *it ); first = false; } else { tip += QString( "
            • %1
            • " ).arg( *it ); } } tip += "
            "; } else { tip = i18n( "No tags assigned yet." ); } pbTagging->setToolTip( tip ); } QString PositionViewWidget::extraDiscountTagRestriction() { QStringList taglist = TagTemplateMan::self()->allTagTemplates(); int currentItem = mDiscountTag->currentIndex(); if ( currentItem > 0 && currentItem <= taglist.count() ) { // subtract one for the "all items" entry in the combo box at first position currentItem -= 1; return taglist[currentItem]; } else { // qDebug () << "taglist index possibly out of range!"; } return QString(); } void PositionViewWidget::slotTaggingButtonPressed() { // qDebug () << "opening tagging dialog" << endl; ItemTagDialog dia( 0 ); dia.setPositionTags( mTags ); if ( dia.exec() ) { mTags = dia.getSelectedTags(); slotUpdateTagToolTip(); slotModified(); update(); // qDebug () << "Selected tags: " << mTags.join( ", " ) << endl; } } void PositionViewWidget::slotSetNilTax() { slotSetTax( DocPositionBase::TaxNone ); } void PositionViewWidget::slotSetReducedTax() { slotSetTax( DocPositionBase::TaxReduced ); } void PositionViewWidget::slotSetFullTax() { slotSetTax( DocPositionBase::TaxFull ); } void PositionViewWidget::slotSetTax( DocPosition::TaxType tt ) { mTax = tt; QString icon; if( tt == DocPositionBase::TaxFull ) { icon = QString::fromLatin1("kraft_fulltax"); mFullTaxAction->setChecked( true ); } else if( tt == DocPositionBase::TaxReduced ) { icon = QString::fromLatin1("kraft_redtax"); mRedTaxAction->setChecked( true ); } else if( tt == DocPositionBase::TaxNone ) { icon = QString::fromLatin1("kraft_notax"); mNilTaxAction->setChecked( true ); } mTaxSubmenu->setIcon( QIcon::fromTheme( icon )); emit positionModified(); } void PositionViewWidget::slotAllowIndividualTax( bool allow ) { mFullTaxAction->setEnabled(allow); mRedTaxAction->setEnabled(allow); mNilTaxAction->setEnabled(allow); mTaxSubmenu->setEnabled( allow ); } DocPositionBase::TaxType PositionViewWidget::taxType() const { return mTax; } void PositionViewWidget::slotExecButtonPressed() { // qDebug () << "Opening Context Menu over exec button" << endl; // set bg-color mExecPopup->popup( QWidget::mapToGlobal( pbExec->pos() ) ); } void PositionViewWidget::slotMenuAboutToShow() { QPalette palette; palette.setColor(this->backgroundRole(), QColor("#757476")); this->setPalette(palette); } void PositionViewWidget::slotMenuAboutToHide() { // qDebug () << "Set normal again" << endl; QPalette palette; setPalette( palette ); pbExec->setChecked(false); } void PositionViewWidget::slotLockPosition( ) { slotSetState( Locked ); } void PositionViewWidget::slotUnlockPosition( ) { slotSetState( Active ); } void PositionViewWidget::slotEnableKindMenu( bool s ) { mStateSubmenu->setEnabled( s ); } QString PositionViewWidget::stateString( const State& state ) const { QString str; if( state == Active ) { str = i18n( "Active" ); } else if( state == New ) { str = i18n( "New" ); } else if( state == Deleted ) { str = i18n( "Deleted" ); } else if( state == Locked ) { str = i18n( "Locked" ); } else { str = i18n( "Unknown" ); } return str; } void PositionViewWidget::slotSetState( State state ) { mState = state; // qDebug () << "Setting new widget state " << stateString( state ) << endl; if( state == Active ) { mLockId->setEnabled( true ); mUnlockId->setEnabled( false ); lStatus->hide(); lStatus->setPixmap( QPixmap() ); mToDelete = false; slotSetEnabled( true ); } else if( state == New ) { lStatus->setPixmap( QIcon::fromTheme("filenew").pixmap(QSize(20,20))); lStatus->show(); } else if( state == Deleted ) { lStatus->setPixmap( QIcon::fromTheme( "remove" ).pixmap(QSize(20,20)) ); lStatus->show(); mToDelete = true; slotSetEnabled( false ); } else if( state == Locked ) { mLockId->setEnabled( false ); mUnlockId->setEnabled( true ); slotSetEnabled( false ); lStatus->setPixmap( QIcon::fromTheme( "encrypted" ).pixmap(QSize(20,20))); lStatus->show(); } } void PositionViewWidget::setOrdNumber(int o) { mOrdNumber = o; if( mModified ) { QColor c( "darkred" ); QPalette palette = m_labelPosition->palette(); palette.setColor(m_labelPosition->foregroundRole(), c); m_labelPosition->setPalette(palette); } m_labelPosition->setText( QString("%1.").arg( mOrdNumber ) ); } void PositionViewWidget::slotSetEnabled( bool doit ) { if( !doit ) { m_sbAmount->setEnabled( false ); m_sbUnitPrice->setEnabled( false ); m_labelPosition->setEnabled( false ); m_teFloskel->setEnabled( false ); m_sumLabel->setEnabled( false ); m_cbUnit->setEnabled( false ); } else { m_sbAmount->setEnabled( true ); m_sbUnitPrice->setEnabled( true ); m_labelPosition->setEnabled( true ); m_teFloskel->setEnabled( true ); m_sumLabel->setEnabled( true ); m_cbUnit->setEnabled( true ); } } bool PositionViewWidget::priceValid() { bool isValid = true; if ( position()->type() == DocPosition::ExtraDiscount ) { isValid = mPositionPriceValid; } return isValid; } void PositionViewWidget::setCurrentPrice( Geld g ) { // do nothing for normal positions if ( position()->type() == DocPosition::ExtraDiscount ) { mPositionPrice = g; mPositionPriceValid = true; } } Geld PositionViewWidget::currentPrice() { Geld sum; if ( mKind == Normal ) { if ( position()->type() == DocPosition::ExtraDiscount ) { sum = mPositionPrice; if ( ! mPositionPriceValid ) { qWarning() << "Asking for price of Discount item, but invalid!" << endl; } } else { double amount = m_sbAmount->value(); Geld g( m_sbUnitPrice->value() ); sum = g * amount; } } return sum; } Geld PositionViewWidget::unitPrice() { Geld p( m_sbUnitPrice->value() ); return p; } void PositionViewWidget::slotRefreshPrice() { const Geld sum = currentPrice(); slotSetOverallPrice( sum ); emit priceChanged( sum ); } void PositionViewWidget::slotSetOverallPrice( Geld g ) { // if ( mPositionPtr->type() == DocPosition::ExtraDiscount ) { // m_sumLabel->setText( "--" ); // } else { m_sumLabel->setText( g.toString() ); // } } void PositionViewWidget::slotModified( bool emitSignal ) { Q_UNUSED(emitSignal) if(m_skipModifiedSignal) return; // qDebug () << "Modified Position!" << endl; mModified = true; m_labelPosition->setStyleSheet("font-weight: bold; color: red"); emit positionModified(); } PositionViewWidget::~PositionViewWidget() { } PositionViewWidgetList::PositionViewWidgetList() : QList() { // setAutoDelete( true ); } PositionViewWidget* PositionViewWidgetList::widgetFromPosition( DocPositionGuardedPtr ptr) { PositionViewWidgetListIterator it( *this ); while( it.hasNext() ) { PositionViewWidget *pvw = it.next(); if( pvw ->position() == ptr ) { return pvw; } } return 0; } Geld PositionViewWidgetList::nettoPrice() { Geld res; PositionViewWidgetListIterator it( *this ); while( it.hasNext() ) { PositionViewWidget *pvw = it.next(); res += pvw->currentPrice(); } return res; } void PositionViewWidget::slotSetPositionNormal() { lKind->hide(); lKind->setPixmap( QPixmap() ); mKind = Normal; cleanKindString(); slotRefreshPrice(); emit positionModified(); } void PositionViewWidget::cleanKindString() { QString current = m_teFloskel->toPlainText(); bool touched = false; if ( current.startsWith( kindLabel( Alternative ) ) ) { current.remove( 0, QString( kindLabel( Alternative ) ).length() ); touched = true; } else if ( current.startsWith( kindLabel( Demand ) ) ) { current.remove( 0, QString( kindLabel( Demand ) ).length() ); touched = true; } if ( touched ) { m_teFloskel->setText( current ); } } void PositionViewWidget::slotSetPositionAlternative() { lKind->show(); lKind->setToolTip( i18n( "This is an alternative item.

            " " Use the position toolbox to change the item type." ) ); lKind->setPixmap( QIcon::fromTheme( "kraft_alternative" ).pixmap(QSize(20,20))); mKind = Alternative; slotRefreshPrice(); cleanKindString(); m_teFloskel->setText( kindLabel( Alternative ) + m_teFloskel->toPlainText() ); emit positionModified(); } void PositionViewWidget::slotSetPositionDemand() { lKind->show(); lKind->setToolTip( i18n( "This item is either completely optional or its " "amount varies depending on the needs.

            " "Use the item toolbox to change the item type." ) ); lKind->setPixmap( QIcon::fromTheme("kraft_demand").pixmap(QSize(20,20))); mKind = Demand; slotRefreshPrice(); cleanKindString(); m_teFloskel->setText( kindLabel( Demand ) + m_teFloskel->toPlainText() ); emit positionModified(); } // The technical label QString PositionViewWidget::kindString( Kind k ) const { Kind kind = k; if ( kind == Invalid ) kind = mKind; if ( kind == Normal ) return QString::fromLatin1( "Normal" ); if ( kind == Demand ) return QString::fromLatin1( "Demand" ); if ( kind == Alternative ) return QString::fromLatin1( "Alternative" ); return QString::fromLatin1( "unknown" ); } // The label that is prepended to a positions text QString PositionViewWidget::kindLabel( Kind k ) const { Kind kind = k; if ( kind == Invalid ) kind = mKind; QString re; if ( kind == Normal ) { re = KraftSettings::self()->normalLabel(); if ( re.isEmpty() ) re = i18n( "Normal" ); } if ( kind == Demand ) { re = KraftSettings::self()->demandLabel(); if ( re.isEmpty() ) re = i18n( "Demand" ); } if ( kind == Alternative ) { re = KraftSettings::self()->alternativeLabel(); if ( re.isEmpty() ) re = i18n( "Alternative" ); } if ( ! re.endsWith( ": " ) ) { re += QString::fromLatin1( ": " ); } return re; } void PositionViewWidget::paintEvent ( QPaintEvent*) { QScopedPointer painter(new QPainter( this )); // visualize the tags const QStringList taglist = tagList(); if ( taglist.count() ) { int share = ( height() - 24 ) / taglist.count(); int cnt = 0; for ( QStringList::ConstIterator it = taglist.begin(); it != taglist.end(); ++it ) { const QString tag(*it); TagTemplate tagTemplate = TagTemplateMan::self()->getTagTemplate( tag ); const QColor c = tagTemplate.color(); // qDebug() << "color: " << c.red() << ", " << c.green() << ", " << c.blue() << endl; painter->setBrush( c ); int starty = 6+cnt*share; qDrawShadeLine( painter.data(), QPoint(3, starty), QPoint(3, starty+share-1), tagTemplate.palette(), false, 1, 4 ); cnt++; } } } kraft-0.97/src/positionviewwidget.h000066400000000000000000000106061410616450300174660ustar00rootroot00000000000000/*************************************************************************** postionviewwidget - inherited class for doc position views. ------------------- begin : 2006-02-20 copyright : (C) 2006 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef POSITIONVIEWWIDGET_H #define POSITIONVIEWWIDGET_H #include #include #include #include #include #include "geld.h" #include "ui_positionwidget.h" #include "docposition.h" /** @author Klaas Freitag */ class KMenu; class QAction; class Geld; class QLocale; class DosPositionGuardedPtr; class PositionViewWidget : public QWidget, public Ui_positionWidget { Q_OBJECT public: enum State { Active, New, Deleted, Locked }; enum Kind { Normal, Demand, Alternative, Invalid }; PositionViewWidget( ); PositionViewWidget( int ); void setDocPosition(DocPositionBase*); virtual ~PositionViewWidget(); bool modified() { return mModified; } int ordNumber() { return mOrdNumber; } void setOrdNumber( int ); bool deleted() { return mToDelete; } DocPositionGuardedPtr position(){ return mPositionPtr; } State state() { return mState; } Kind kind() { return mKind; } QString kindString( Kind = Invalid ) const; QString stateString( const State& state ) const; QString kindLabel( Kind ) const; void cleanKindString(); Geld currentPrice(); bool priceValid(); void setCurrentPrice( Geld ); Geld unitPrice(); QStringList tagList() { return mTags; } QString extraDiscountTagRestriction(); DocPositionBase::TaxType taxType() const; public slots: void slotSetOverallPrice( Geld ); void slotRefreshPrice(); void slotModified( bool emitSignal = true ); void slotExecButtonPressed(); void slotTaggingButtonPressed(); void slotMenuAboutToHide(); void slotMenuAboutToShow(); void slotSetState( State ); void slotSetEnabled( bool ); void slotEnableKindMenu( bool ); void slotAllowIndividualTax( bool ); void slotSetTax( DocPosition::TaxType ); void slotShowPrice( bool show ); // hide the price entries for certain doc types. protected slots: void slotLockPosition(); void slotUnlockPosition(); void slotSetPositionNormal(); void slotSetPositionAlternative(); void slotSetPositionDemand(); void slotUpdateTagToolTip(); void paintEvent ( QPaintEvent* ); void slotSetNilTax(); void slotSetReducedTax(); void slotSetFullTax(); signals: void positionModified(); void deletePosition(); void moveUp(); void moveDown(); void lockPosition(); void unlockPosition(); void priceChanged( const Geld& ); void positionStateNormal(); void positionStateAlternative(); void positionStateDemand(); private: bool mModified; bool m_skipModifiedSignal; bool mToDelete; int mOrdNumber; DocPositionGuardedPtr mPositionPtr; QMenu *mExecPopup; QMenu *mStateSubmenu; QMenu *mTaxSubmenu; QStringList mTags; QAction * mDeleteId; QAction * mLockId; QAction * mUnlockId; QAction * mNilTaxAction; QAction * mRedTaxAction; QAction * mFullTaxAction; Geld mPositionPrice; // only used for Discount items to store the result State mState; Kind mKind; bool mPositionPriceValid; QLocale *mLocale; DocPosition::TaxType mTax; }; class PositionViewWidgetList : public QList { public: PositionViewWidgetList(); PositionViewWidget* widgetFromPosition( DocPositionGuardedPtr ); Geld nettoPrice(); }; typedef QListIterator PositionViewWidgetListIterator; #endif kraft-0.97/src/positionwidget.ui000066400000000000000000000205521410616450300167620ustar00rootroot00000000000000 positionWidget 0 0 520 157 0 1 400 0 2 2 4 0 0 1. false true 0 0 25 16777215 false 0 0 25 16777215 false D Qt::AlignCenter false Qt::Vertical QSizePolicy::Expanding 22 5 D Qt::AlignCenter false 0 1 150 110 true 0 9999900.000000000000000 0.000000000000000 1 0.010000000000000 2 99999.000000000000000 0.000000000000000 0.100000000000000 -0.100000000000000 0.010000000000000 2 0 % of the sum of false Qt::Horizontal QSizePolicy::Expanding 28 21 75 true textLabel7 false Qt::Horizontal kraft-0.97/src/prefsdialog.cpp000066400000000000000000000630231410616450300163560ustar00rootroot00000000000000/*************************************************************************** prefsdialog.cpp - the preferences Dialog ------------------- begin : Sun Jul 3 2004 copyright : (C) 2004 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "prefsdialog.h" #include "prefswages.h" #include "prefsunits.h" #include "databasesettings.h" #include "kraftsettings.h" #include "kraftdb.h" #include "kraftdoc.h" #include "defaultprovider.h" #include "doctype.h" #include "doctypeedit.h" #include "taxeditdialog.h" #include "documentman.h" #include "impviewwidgets.h" #include "texttemplate.h" #include "htmlview.h" #include "addressselectordialog.h" #include "addressprovider.h" #include "format.h" #include "kcontacts/vcardconverter.h" // ################################################################################ PrefsDialog::PrefsDialog( QWidget *parent) :QDialog( parent ) { setModal( true ); setWindowTitle( i18n( "Configure Kraft" ) ); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); QHBoxLayout *mainLayout = new QHBoxLayout; _navigationBar = new QListWidget(this); _navigationBar->setViewMode(QListView::IconMode); _navigationBar->setIconSize(QSize(96, 64)); _navigationBar->setMovement(QListView::Static); //_navigationBar->setSpacing(6); _navigationBar->setCurrentRow(0); _navigationBar->setFixedWidth(195); setLayout(mainLayout); QVBoxLayout *vbox = new QVBoxLayout; _pagesWidget = new QStackedWidget(this); vbox->addWidget( _pagesWidget ); vbox->addWidget( buttonBox ); mainLayout->addWidget(_navigationBar); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); mainLayout->addLayout(vbox); okButton->setDefault(true); setMinimumWidth(700); _maxNavBarTextWidth = 0; addDialogPage( docTab(), QIcon::fromTheme( "edit-copy"), i18n( "Document Defaults" )); addDialogPage( taxTab(), QIcon::fromTheme( "accessories-text-editor" ), i18n("Taxes")); addDialogPage( doctypeTab(), QIcon::fromTheme( "folder-documents"), i18n( "Document Types" )); mPrefsWages = new PrefsWages(this); addDialogPage(mPrefsWages, QIcon::fromTheme( "help-donate" ), i18n( "Wages" )); mPrefsUnits = new PrefsUnits(this); addDialogPage(mPrefsUnits, QIcon::fromTheme( "chronometer" ), i18n("Units")); addDialogPage( whoIsMeTab(), QIcon::fromTheme( "user-identity" ), i18n( "Own Identity" )); readConfig(); connect( _navigationBar, &QListWidget::itemClicked, this, &PrefsDialog::changePage); } void PrefsDialog::changePage(QListWidgetItem *current) { if (!current) return; int indx = _navigationBar->row(current); _pagesWidget->setCurrentIndex(indx); } int PrefsDialog::addDialogPage( QWidget *w, const QIcon& icon, const QString& title) { QListWidgetItem *listWidgetItem = new QListWidgetItem(_navigationBar); listWidgetItem->setIcon(icon); listWidgetItem->setText(title); listWidgetItem->setTextAlignment(Qt::AlignCenter); listWidgetItem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); listWidgetItem->setSizeHint( QSize(170, 100)); _navigationBar->addItem(listWidgetItem); QWidget *w_with_title = new QWidget; QVBoxLayout *layout = new QVBoxLayout; layout->addWidget(new QLabel(QStringLiteral("

            ")+title+QStringLiteral("

            "))); w_with_title->setLayout(layout); layout->addWidget(w); int indx = _pagesWidget->addWidget(w_with_title); return indx; } QWidget* PrefsDialog::taxTab() { QWidget *topWidget = new QWidget; QVBoxLayout *vboxLay = new QVBoxLayout; // vboxLay->setSpacing( spacingHint() ); QLabel *label; label = new QLabel(i18n("Tax rates beginning at date:")); vboxLay->addWidget( label ); mTaxModel = new QSqlTableModel(this); mTaxModel->setTable("taxes"); mTaxModel->setSort(3, Qt::DescendingOrder); mTaxModel->setEditStrategy(QSqlTableModel::OnManualSubmit); mTaxModel->select(); mTaxModel->setHeaderData(0, Qt::Horizontal, i18n("ID")); mTaxModel->setHeaderData(1, Qt::Horizontal, i18n("Full Tax [%]")); mTaxModel->setHeaderData(2, Qt::Horizontal, i18n("Reduced Tax [%]")); mTaxModel->setHeaderData(3, Qt::Horizontal, i18n("Start Date")); mTaxTreeView = new ImpTreeView; vboxLay->addWidget( mTaxTreeView ); mTaxTreeView->setModel(mTaxModel); mTaxTreeView->setItemDelegate(new TaxItemDelegate()); mTaxTreeView->hideColumn(0); mTaxTreeView->header()->moveSection(3, 1); mTaxTreeView->header()->stretchLastSection(); mTaxTreeView->setColumnWidth(3, 200); mTaxTreeView->resizeColumnToContents(2); mTaxTreeView->resizeColumnToContents(1); connect( mTaxTreeView, SIGNAL(clicked(QModelIndex)), SLOT( slotTaxSelected(QModelIndex) ) ); QHBoxLayout *butLay = new QHBoxLayout; butLay->addStretch( 1 ); QPushButton *but = new QPushButton( QIcon::fromTheme("list-add"), i18n( "Add" )); connect( but, SIGNAL( clicked() ), SLOT( slotAddTax() ) ); butLay->addWidget( but ); mDelTax = new QPushButton( QIcon::fromTheme("list-remove"), i18n( "Remove" ) ); connect( mDelTax, SIGNAL( clicked() ), SLOT( slotDeleteTax() ) ); butLay->addWidget( mDelTax ); mDelTax->setEnabled( false ); vboxLay->addLayout( butLay ); topWidget->setLayout( vboxLay ); return topWidget; } QWidget* PrefsDialog::whoIsMeTab() { QWidget *topWidget = new QWidget; QVBoxLayout *vboxLay = new QVBoxLayout; QLabel *label = new QLabel(i18n("Select the identity of the sending entity of documents. That's your companies address.")); label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); vboxLay->addWidget( label ); _tabWidget = new QTabWidget; vboxLay->addWidget(_tabWidget); // == Tab that displays the Addressbook widget QWidget *w = new QWidget; QVBoxLayout *t1Lay = new QVBoxLayout; mIdentityView = new HtmlView; mIdentityView->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::MinimumExpanding); t1Lay->addWidget(mIdentityView); QHBoxLayout *butLay = new QHBoxLayout; butLay->addStretch( 1 ); _pbChangeIdentity = new QPushButton(i18n("Select Identity...")); connect( _pbChangeIdentity, SIGNAL(clicked()), SLOT(slotChangeIdentity()) ); butLay->addWidget(_pbChangeIdentity); t1Lay->addLayout( butLay ); w->setLayout(t1Lay); _tabWidget->insertTab(0, w, i18n("From AddressBook")); // == Tab that displays the manual widget QWidget *w1 = new QWidget; ui.setupUi(w1); _tabWidget->insertTab(1, w1, QIcon(), i18n("Manual Entry")); ui.nameLabel->setText( KContacts::Addressee::formattedNameLabel() ); ui.orgLabel->setText( KContacts::Addressee::organizationLabel()); ui.streetLabel->setText(KContacts::Addressee::businessAddressStreetLabel()); ui.postCodeLabel->setText(KContacts::Addressee::businessAddressPostalCodeLabel()); ui.cityLabel->setText(KContacts::Addressee::businessAddressLocalityLabel()); ui.phoneLabel->setText(KContacts::Addressee::businessPhoneLabel()); ui.faxLabel->setText(KContacts::Addressee::businessFaxLabel()); ui.mobileLabel->setText(KContacts::Addressee::mobilePhoneLabel()); ui.emailLabel->setText(KContacts::Addressee::emailLabel()); ui.websiteLabel->setText(KContacts::Addressee::urlLabel()); _tabWidget->insertTab(1, w1, i18n("Manual Address")); topWidget->setLayout( vboxLay ); return topWidget; } void PrefsDialog::slotChangeIdentity() { AddressSelectorDialog dialog(this); if( dialog.exec() ) { _newIdentity = dialog.addressee(); if( ! _newIdentity.isEmpty() ) { setMyIdentity(_newIdentity, true); } } } void PrefsDialog::slotAddTax() { TaxEditDialog *dialog = new TaxEditDialog(mTaxModel, this); dialog->show(); } void PrefsDialog::slotDeleteTax() { if ( mTaxTreeView->currentIndex().isValid() ) { int row = mTaxTreeView->currentIndex().row(); //mTaxTreeView->setRowHidden( row, mTaxTreeView->rootIndex(), true ); mTaxModel->removeRows(row, 1); } } void PrefsDialog::slotTaxSelected(QModelIndex) { bool state = false; if ( mTaxTreeView->currentIndex().isValid() ) { state = true; } mDelTax->setEnabled( state ); } QWidget* PrefsDialog::docTab() { QLabel *label; QWidget *topWidget = new QWidget; QVBoxLayout *vboxLay = new QVBoxLayout; topWidget->setLayout( vboxLay ); QGridLayout *topLayout = new QGridLayout; vboxLay->addLayout( topLayout ); label = new QLabel(i18n("&Default document type on creation:"), this ); topLayout->addWidget(label, 0,0); mCbDocTypes = new QComboBox; label->setBuddy( mCbDocTypes ); mCbDocTypes->setToolTip( i18n( "New documents default to the selected type." ) ); topLayout->addWidget( mCbDocTypes, 0, 1 ); mCbDocTypes->insertItems(-1, DocType::allLocalised() ); QLabel *f = new QLabel(this); f->setFrameStyle( QFrame::HLine | QFrame::Sunken ); vboxLay->addWidget( f ); QHBoxLayout *butLay = new QHBoxLayout; QLabel *l = new QLabel( i18n( "Default &Tax for Documents:" ), this ); butLay->addWidget( l ); mCbDefaultTaxType = new QComboBox(this); butLay->addWidget( mCbDefaultTaxType ); l->setBuddy( mCbDefaultTaxType ); mCbDefaultTaxType->setToolTip( i18n( "The default tax setting for all documents." ) ); mCbDefaultTaxType->insertItem( 0, i18n("Display no tax at all")); mCbDefaultTaxType->insertItem( 1, i18n("Calculate reduced tax for all items" )); mCbDefaultTaxType->insertItem( 2, i18n("Calculate full tax for all items" ) ); // mCbDefaultTaxType->insertItem( 3, i18n("Calculate on individual item tax rate" )); vboxLay->addLayout( butLay ); f = new QLabel(this); f->setFrameStyle( QFrame::HLine | QFrame::Sunken ); vboxLay->addWidget( f ); butLay = new QHBoxLayout; l = new QLabel( i18n( "Document Date Format:" ), this ); butLay->addWidget( l ); mCbDateFormats = new QComboBox(this); butLay->addWidget( mCbDateFormats ); l->setBuddy( mCbDateFormats); const QDate d = QDate::currentDate(); mCbDateFormats->setToolTip( i18n( "The default date format for documents." ) ); QString formattedDate = d.toString(Qt::ISODate); mCbDateFormats->insertItem( 0, i18n("ISO-Format: %1", formattedDate)); formattedDate = d.toString(Qt::DefaultLocaleShortDate); mCbDateFormats->insertItem( 1, i18n("Short-Date: %1", formattedDate)); formattedDate = d.toString(Qt::DefaultLocaleLongDate); mCbDateFormats->insertItem( 2, i18n("Long-Date: %1", formattedDate)); formattedDate = d.toString(Qt::RFC2822Date); mCbDateFormats->insertItem( 3, i18n("RFC 2822-Format: %1", formattedDate)); formattedDate = d.toString("dd.MM.yyyy"); mCbDateFormats->insertItem( 4, i18n("\"German Format\": %1", formattedDate)); mCbDateFormats->insertItem( 5, i18n("Custom Setting in Settingsfile")); vboxLay->addLayout( butLay ); // space eater QWidget *spaceEater = new QWidget; spaceEater->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ) ); vboxLay->addWidget( spaceEater ); return topWidget; } QWidget* PrefsDialog::doctypeTab() { QWidget *topWidget = new QWidget; QVBoxLayout *vboxLay = new QVBoxLayout; topWidget->setLayout(vboxLay); vboxLay->setSpacing( 0 ); // spacingHint() ); mDocTypeEdit = new DocTypeEdit; vboxLay->addWidget( mDocTypeEdit ); connect( mDocTypeEdit, SIGNAL( removedType( const QString& ) ), SLOT( slotDocTypeRemoved( const QString& ) ) ); return topWidget; } void PrefsDialog::slotDocTypeRemoved( const QString& type ) { // check if the default document type is still there QString currDefault = mCbDocTypes->currentText(); if ( currDefault == type ) { QMessageBox msgBox; msgBox.setText(i18n( "The old default doc type for new documents was just deleted." "Please check the setting in the Document Defaults in the " "Kraft preferences Dialog." )); msgBox.setInformativeText(i18n("Document Default Change")); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setDefaultButton(QMessageBox::Ok); msgBox.exec(); } for ( int i=0; i < mCbDocTypes->count(); i++ ) { if ( mCbDocTypes->itemText( i ) == type ) { mCbDocTypes->removeItem( i ); continue; } } } void PrefsDialog::readConfig() { QString t = KraftSettings::self()->doctype(); if ( t.isEmpty() ) t = DefaultProvider::self()->docType(); mCbDocTypes->setCurrentIndex( mCbDocTypes->findText( t )); mCbDefaultTaxType->setCurrentIndex( KraftSettings::self()->defaultTaxType()-1 ); int index {5}; const QString dFormat = KraftSettings::self()->dateFormat(); if (dFormat == Format::DateFormatIso) { // iso index = 0; } else if (dFormat == Format::DateFormatShort) { // short index = 1; } else if (dFormat == Format::DateFormatLong) { // long index = 2; } else if (dFormat == Format::DateFormatRFC) { // RFC index = 3; } else if (dFormat == Format::DateFormatGerman) { // German index = 4; } if (index == 5 && dFormat.isEmpty()) { // default case - no entry // HACK: If it is german, choose the "german" format const QString ln = DefaultProvider::self()->locale()->name(); if( ln == QStringLiteral("de_DE")) { index = 4; } else { index = 1; // Short locale aware. } } mCbDateFormats->setCurrentIndex(index); } void PrefsDialog::writeIdentity() { /* * Save either the manually added address, or the Addressbook-ID * If the user fills in the manual form, the addressbook ID is removed. * FIXME: The handling of the ownIdentity should be refactored to its * own class. */ if(_tabWidget->currentIndex() == 1 /* manually entered */ ) { KContacts::Addressee add; add.setFormattedName(ui.leName->text()); add.setOrganization(ui.leOrganization->text()); KContacts::Address workAddress; workAddress.setStreet(ui.leStreet->text()); workAddress.setPostalCode(ui.lePostcode->text()); workAddress.setLocality(ui.leCity->text()); workAddress.setType(KContacts::Address::Work); add.insertAddress(workAddress); add.insertPhoneNumber(PhoneNumber(ui.lePhone->text(), KContacts::PhoneNumber::Work)); add.insertPhoneNumber(PhoneNumber(ui.leFax->text(), KContacts::PhoneNumber::Fax)); add.insertPhoneNumber(PhoneNumber(ui.leMobile->text(), KContacts::PhoneNumber::Cell)); ResourceLocatorUrl resUrl; resUrl.setUrl(QUrl(ui.leWebsite->text())); add.setUrl(resUrl); add.insertEmail(ui.leEmail->text(), true /* prefered */ ); VCardConverter vcc; QByteArray vcard = vcc.createVCard(add); QString file = QStandardPaths::writableLocation( QStandardPaths::AppDataLocation ); file += "/myidentity.vcd"; QFile f ( file ); if (f.open(QIODevice::WriteOnly | QIODevice::Text)) { f.write(vcard); f.close(); qDebug() << "Saved own identity to " << file; KraftSettings::self()->setUserName( QString() ); KraftSettings::self()->setUserUid( QString() ); KraftSettings::self()->save(); } } else { /* AddressBook */ KraftSettings::self()->setUserName( _newIdentity.name() ); KraftSettings::self()->setUserUid( _newIdentity.uid() ); KraftSettings::self()->save(); } emit newOwnIdentity(_newIdentity.uid(), _newIdentity); } void PrefsDialog::writeConfig() { KraftSettings::self()->setDoctype( mCbDocTypes->currentText() ); KraftSettings::self()->setDefaultTaxType( 1+mCbDefaultTaxType->currentIndex() ); int dateFormat = mCbDateFormats->currentIndex(); QString dateFormatString; if (dateFormat == 0) { // iso dateFormatString = Format::DateFormatIso; } else if (dateFormat == 1) { // short dateFormatString = Format::DateFormatShort; } else if (dateFormat == 2) { // long dateFormatString = Format::DateFormatLong; } else if (dateFormat == 3) { // RFC dateFormatString = Format::DateFormatRFC; } else if (dateFormat == 4) { // German dateFormatString = Format::DateFormatGerman; } if (dateFormatString.isEmpty()) { // do not touch! } else { KraftSettings::self()->setDateFormat(dateFormatString); } KraftSettings::self()->save(); } void PrefsDialog::writeTaxes() { mTaxModel->submitAll(); } PrefsDialog::~PrefsDialog() { } void PrefsDialog::accept() { mDocTypeEdit->saveDocTypes(); mPrefsWages->save(); mPrefsUnits->save(); writeTaxes(); writeConfig(); writeIdentity(); QDialog::accept(); } #define IDENTITY_TAG(X) QLatin1String(X) #define QL1(X) QStringLiteral(X) void PrefsDialog::fillManualIdentityForm(const KContacts::Addressee& addressee) { ui.leName->setText(addressee.formattedName()); ui.leStreet->setText(addressee.address(Address::Work).street()); ui.leCity->setText(addressee.address(Address::Work).locality()); ui.lePostcode->setText(addressee.address(Address::Work).postalCode()); ui.leEmail->setText(addressee.preferredEmail()); ui.leFax->setText(addressee.phoneNumber(PhoneNumber::Fax).number()); ui.leOrganization->setText(addressee.organization()); ui.lePhone->setText(addressee.phoneNumber(PhoneNumber::Work).number()); ui.leMobile->setText(addressee.phoneNumber(PhoneNumber::Cell).number()); ui.leWebsite->setText(addressee.url().url().toDisplayString()); } void PrefsDialog::setMyIdentity( const KContacts::Addressee& addressee, bool backendUp ) { // Note: This code is stolen from DocDigestDetailView::slotShowDocDetails // It should be refactored. const QString tmplFile = DefaultProvider::self()->locateFile( "views/identity.thtml" ); TextTemplate tmpl; tmpl.setTemplateFileName(tmplFile); if( !tmpl.isOk() ) { return; } if( ! tmpl.errorString().isEmpty() ) { mIdentityView->displayContent( QString("

            Unable to find template identity.trml

            %1

            ") .arg(tmpl.errorString())); return; } QString addressBookInfo; _pbChangeIdentity->setEnabled(backendUp); QPalette p; QColor c = p.color(QPalette::Normal, QPalette::ToolTipBase); tmpl.setValue(IDENTITY_TAG("CSS_WARN_BACKGROUND_COLOR"), c.name()); c = p.color(QPalette::Normal, QPalette::QPalette::Base); tmpl.setValue(IDENTITY_TAG("CSS_IDENTITY_IMAGE_BACKGROUND"), "#ea4e1d"); // c.name()); if( !backendUp ) { addressBookInfo = i18n("The identity can not be found."); tmpl.createDictionary(QL1("NO_IDENTITY")); tmpl.setValue(QL1("NO_IDENTITY_WRN"), i18n("

            Kraft Addressbook Integration down.

            " "

            The address book backend is not up and running.

            " "

            Please check your addressbook integration setup.

            ")); } if( addressee.isEmpty() ) { addressBookInfo = i18n("The identity is not listed in an address book."); tmpl.createDictionary(QL1("NO_IDENTITY")); tmpl.setValue(QL1("NO_IDENTITY_WRN"), i18n("

            Kraft does not know your identity.

            " "

            Please pick one from the address books by clicking on the Button below.

            " "

            Not having an identity selected can make your documents look incomplete.

            ")); } else { const QString origin = addressee.custom( CUSTOM_ADDRESS_MARKER ); if( origin.isEmpty() || origin == "manual") { // it is an manually added address. fillManualIdentityForm(addressee); _tabWidget->setTabIcon(1, QIcon::fromTheme("checkmark")); _tabWidget->setTabIcon(0, QIcon()); _tabWidget->setCurrentIndex(1); } else { _tabWidget->setTabIcon(0, QIcon::fromTheme("checkmark")); _tabWidget->setTabIcon(1, QIcon()); _tabWidget->setCurrentIndex(0); // it is an address from the address book addressBookInfo = i18n("Your identity can be found in the address books."); tmpl.createDictionary(QL1("IDENTITY")); tmpl.setValue( QL1("IDENTITY"), IDENTITY_TAG("IDENTITY_NAME"), addressee.realName() ); tmpl.setValue( QL1("IDENTITY"), IDENTITY_TAG("IDENTITY_ORGANISATION"), addressee.organization() ); tmpl.setValue( QL1("IDENTITY"), IDENTITY_TAG("IDENTITY_URL"), addressee.url().toString() ); tmpl.setValue( QL1("IDENTITY"), IDENTITY_TAG("IDENTITY_EMAIL"), addressee.preferredEmail() ); tmpl.setValue( QL1("IDENTITY"), IDENTITY_TAG("IDENTITY_WORK_PHONE"), addressee.phoneNumber(PhoneNumber::Work).number()); tmpl.setValue( QL1("IDENTITY"), IDENTITY_TAG("IDENTITY_MOBILE_PHONE"), addressee.phoneNumber(PhoneNumber::Cell).number()); tmpl.setValue( QL1("IDENTITY"), IDENTITY_TAG("IDENTITY_FAX"), addressee.phoneNumber(PhoneNumber::Fax).number()); tmpl.setValue( QL1("IDENTITY"), IDENTITY_TAG("WORK_PHONE_LABEL"), i18n("Work Phone") ); tmpl.setValue( QL1("IDENTITY"), IDENTITY_TAG("FAX_LABEL"), i18n("Fax") ); tmpl.setValue( QL1("IDENTITY"), IDENTITY_TAG("MOBILE_PHONE_LABEL"), i18n("Cell Phone") ); KContacts::Address myAddress; myAddress = addressee.address( KContacts::Address::Pref ); QString addressType = i18n("preferred address"); if( myAddress.isEmpty() ) { myAddress = addressee.address( KContacts::Address::Home ); addressType = i18n("home address"); } if( myAddress.isEmpty() ) { myAddress = addressee.address( KContacts::Address::Work ); addressType = i18n("work address"); } if( myAddress.isEmpty() ) { myAddress = addressee.address( KContacts::Address::Postal ); addressType = i18n("postal address"); } if( myAddress.isEmpty() ) { myAddress = addressee.address( KContacts::Address::Intl ); addressType = i18n("international address"); } if( myAddress.isEmpty() ) { myAddress = addressee.address( KContacts::Address::Dom ); addressType = i18n("domestic address"); } if( myAddress.isEmpty() ) { addressType = i18n("unknown"); // qDebug () << "WRN: Address is still empty!"; } tmpl.setValue( QL1("IDENTITY"), IDENTITY_TAG( "IDENTITY_POSTBOX" ), myAddress.postOfficeBox() ); tmpl.setValue( QL1("IDENTITY"), IDENTITY_TAG( "IDENTITY_EXTENDED" ), myAddress.extended() ); tmpl.setValue( QL1("IDENTITY"), IDENTITY_TAG( "IDENTITY_STREET" ), myAddress.street() ); tmpl.setValue( QL1("IDENTITY"), IDENTITY_TAG( "IDENTITY_LOCALITY" ), myAddress.locality() ); tmpl.setValue( QL1("IDENTITY"), IDENTITY_TAG( "IDENTITY_REGION" ), myAddress.region() ); tmpl.setValue( QL1("IDENTITY"), IDENTITY_TAG( "IDENTITY_POSTCODE" ), myAddress.postalCode() ); tmpl.setValue( QL1("IDENTITY"), IDENTITY_TAG( "IDENTITY_COUNTRY" ), myAddress.country() ); tmpl.setValue( QL1("IDENTITY"), IDENTITY_TAG( "IDENTITY_REGION" ), myAddress.region() ); tmpl.setValue( QL1("IDENTITY"), IDENTITY_TAG( "IDENTITY_LABEL" ), myAddress.label() ); tmpl.setValue( QL1("IDENTITY"), IDENTITY_TAG( "IDENTITY_ADDRESS_TYPE" ), QL1("(")+addressType+QL1(")") ); tmpl.setValue( QL1("IDENTITY"), IDENTITY_TAG("ADDRESSBOOK_INFO"), addressBookInfo ); } } const QString ex = tmpl.expand(); mIdentityView->displayContent(ex); } TaxItemDelegate::TaxItemDelegate(QObject * parent) : QItemDelegate(parent) {} void TaxItemDelegate::paint ( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const { if(index.column() == 1 || index.column() == 2) { double percentage = index.data(Qt::DisplayRole).toDouble(); // QString string = DefaultProvider::self()->locale()->formatNumber(QString::number(percentage), true, 1); QString string = DefaultProvider::self()->locale()->toString(percentage); drawDisplay(painter, option, option.rect, string); } else if(index.column() == 3) { QDate date = index.data(Qt::DisplayRole).toDate(); // QString string = DefaultProvider::self()->locale()->formatDate(date); QString string = DefaultProvider::self()->locale()->toString(date); drawDisplay(painter, option, option.rect, string); } else { QItemDelegate::paint(painter, option, index); } } kraft-0.97/src/prefsdialog.h000066400000000000000000000067571410616450300160360ustar00rootroot00000000000000/*************************************************************************** prefsdialog.h - the preferences Dialog ------------------- begin : Sun Jul 3 2004 copyright : (C) 2004 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef PREFSDIALOG_H #define PREFSDIALOG_H #include #include #include "ui_identity.h" #include "doctypeedit.h" #include "doctype.h" #include "taxeditdialog.h" class QLineEdit; class QLabel; class QPushButton; class QComboBox; class QCheckBox; class QSqlTableModel; class QTreeView; class QPainter; class QStyleOptionViewItem; class QStackedWidget; class QModelIndex; class KUrlRequester; class ImpTreeView; class PrefsWages; class PrefsUnits; class HtmlView; // ################################################################################ class PrefsDialog : public QDialog { Q_OBJECT public: PrefsDialog(QWidget *parent); ~PrefsDialog(); void setMyIdentity(const KContacts::Addressee& , bool backendUp); int addPage( QWidget *w, const QIcon& icon, const QString& title); protected: void readConfig(); void writeConfig(); protected slots: void accept(); void slotAddTax(); void slotDeleteTax(); void slotTaxSelected(QModelIndex); void slotDocTypeRemoved( const QString& ); void slotChangeIdentity(); void changePage(QListWidgetItem *current); signals: void newOwnIdentity(const QString&, KContacts::Addressee); private: int addDialogPage( QWidget *w, const QIcon& icon, const QString& title); QWidget *docTab(); QWidget* doctypeTab(); QWidget *taxTab(); void writeTaxes(); void writeIdentity(); QWidget *whoIsMeTab(); void fillManualIdentityForm(const KContacts::Addressee& addressee); QComboBox *m_databaseDriver; QLineEdit *m_leHost; QLineEdit *m_leUser; QLineEdit *m_leName; QLineEdit *m_lePasswd; KUrlRequester *m_leFile; QLabel *m_statusLabel; QWidget *m_mysqlpart; QWidget *m_sqlitepart; QStackedWidget *m_databaseconfigparts; QComboBox *mCbDocTypes; QComboBox *mCbDefaultTaxType; QComboBox *mCbDateFormats; QPushButton *_pbChangeIdentity; DocTypeEdit *mDocTypeEdit; PrefsWages *mPrefsWages; PrefsUnits *mPrefsUnits; KContacts::Addressee _newIdentity; QPushButton *mDelTax; ImpTreeView *mTaxTreeView; QSqlTableModel *mTaxModel; HtmlView *mIdentityView; QListWidget *_navigationBar; QStackedWidget *_pagesWidget; QTabWidget *_tabWidget; Ui::manualOwnIdentity ui; int _maxNavBarTextWidth; }; class TaxItemDelegate : public QItemDelegate { Q_OBJECT public: TaxItemDelegate(QObject * parent = 0); virtual void paint ( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const; }; #endif kraft-0.97/src/prefsunits.cpp000066400000000000000000000145101410616450300162560ustar00rootroot00000000000000/*************************************************************************** prefsunits.cpp - the units tab in the prefs dialog ------------------- begin : Feb 26 2010 copyright : (C) 2010 by Thomas Richard email : thomas.richard@proan.be ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "defaultprovider.h" #include "impviewwidgets.h" #include "geld.h" #include "unitmanager.h" #include "prefsunits.h" PrefsUnits::PrefsUnits(QWidget* parent) : QWidget(parent) { QVBoxLayout *vboxLay = new QVBoxLayout; mUnitsModel = new QSqlTableModel(this); mUnitsModel->setTable("units"); mUnitsModel->setEditStrategy(QSqlTableModel::OnManualSubmit); mUnitsModel->select(); mUnitsModel->setHeaderData(0, Qt::Horizontal, i18n("ID")); mUnitsModel->setHeaderData(1, Qt::Horizontal, i18n("Short")); mUnitsModel->setHeaderData(2, Qt::Horizontal, i18n("Long")); mUnitsModel->setHeaderData(3, Qt::Horizontal, i18n("Short plural")); mUnitsModel->setHeaderData(4, Qt::Horizontal, i18n("Long plural")); mProxyModel = new QSortFilterProxyModel(this); mProxyModel->setSourceModel(mUnitsModel); mUnitsTreeView = new ImpTreeView; vboxLay->addWidget( mUnitsTreeView ); mUnitsTreeView->setModel(mProxyModel); mUnitsTreeView->hideColumn(0); mUnitsTreeView->header()->setResizeMode(QHeaderView::ResizeToContents); mUnitsTreeView->setEditTriggers(ImpTreeView::NoEditTriggers); connect( mUnitsTreeView, SIGNAL(clicked(QModelIndex)), SLOT( slotUnitSelected(QModelIndex) ) ); connect( mUnitsTreeView, SIGNAL(doubleClicked(QModelIndex)), SLOT(slotEditUnit(QModelIndex))); QHBoxLayout *butLay = new QHBoxLayout; butLay->addStretch( 1 ); QPushButton *but = new QPushButton( QIcon::fromTheme("list-add"), i18n( "Add" )); connect( but, SIGNAL( clicked() ), SLOT( slotAddUnit() ) ); butLay->addWidget( but ); mEditUnit = new QPushButton( QIcon::fromTheme("document-edit"), i18n( "Edit" )); connect( mEditUnit, SIGNAL( clicked() ), SLOT( slotEditUnit() ) ); butLay->addWidget( mEditUnit ); mEditUnit->setEnabled(false); mDelUnit = new QPushButton( QIcon::fromTheme("list-remove"), i18n( "Remove" ) ); connect( mDelUnit, SIGNAL( clicked() ), SLOT( slotDeleteUnit() ) ); butLay->addWidget( mDelUnit ); mDelUnit->setEnabled( false ); vboxLay->addLayout( butLay ); this->setLayout( vboxLay ); } PrefsUnits::~PrefsUnits() { } void PrefsUnits::save() { bool ok = mUnitsModel->submitAll(); if( ! ok ) { QString err = mUnitsModel->lastError().text(); // qDebug () << "SQL Error: " << err; } } void PrefsUnits::slotAddUnit() { UnitsEditDialog *dialog = new UnitsEditDialog(mUnitsModel, -1, this); dialog->show(); } void PrefsUnits::slotEditUnit(QModelIndex /* index */ ) { if ( mUnitsTreeView->currentIndex().isValid() ) { int row = mUnitsTreeView->currentIndex().row(); UnitsEditDialog *dialog = new UnitsEditDialog(mUnitsModel, row, this); dialog->show(); } } void PrefsUnits::slotDeleteUnit() { if ( mUnitsTreeView->currentIndex().isValid() ) { int row = mUnitsTreeView->currentIndex().row(); mUnitsModel->removeRows(row, 1); } } void PrefsUnits::slotUnitSelected(QModelIndex) { bool state = false; if ( mUnitsTreeView->currentIndex().isValid() ) { state = true; } mEditUnit->setEnabled( state ); mDelUnit->setEnabled( state ); } UnitsEditDialog::UnitsEditDialog( QAbstractItemModel *model, int row, QWidget *parent ) : QDialog( parent ) { setObjectName( "UNITS_EDIT_DIALOG" ); setModal( true ); setWindowTitle( i18n( "Edit a unit" ) ); QWidget *mainWidget = new QWidget(this); QVBoxLayout *mainLayout = new QVBoxLayout; setLayout(mainLayout); mainLayout->addWidget(mainWidget); QWidget *w = new QWidget( this ); mainLayout->addWidget(w); mBaseWidget = new Ui::UnitsEditBase( ); mBaseWidget->setupUi( w ); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); mainLayout->addWidget(buttonBox); mModel = model; mapper = new QDataWidgetMapper(this); mapper->setSubmitPolicy(QDataWidgetMapper::ManualSubmit); mapper->setModel(model); mapper->addMapping(mBaseWidget->mUnitShort, 1); mapper->addMapping(mBaseWidget->mUnitLong, 2); mapper->addMapping(mBaseWidget->mUnitPluShort, 3); mapper->addMapping(mBaseWidget->mUnitPluLong, 4); if(row == -1) { //Insert a new row at the end int row = model->rowCount(); if( model->insertRow(row) ) { int indx = UnitManager::self()->nextFreeId(); model->setData( model->index(row, 0), indx ); mapper->toLast(); } } else { mBaseWidget->mLabel->setText(i18n("

            Edit unit

            ")); mapper->setCurrentIndex(row); } mRow = row; } void UnitsEditDialog::accept() { bool ok = mapper->submit(); if(!ok) { qDebug () << "UnitsEditDialog Mapper submit result: " << ok; } QDialog::accept(); deleteLater(); } void UnitsEditDialog::reject() { if(mRow == -1) mModel->removeRow(mModel->rowCount()-1); QDialog::reject(); deleteLater(); } kraft-0.97/src/prefsunits.h000066400000000000000000000041341410616450300157240ustar00rootroot00000000000000/*************************************************************************** prefsunits.h - the units tab in the prefs dialog ------------------- begin : Feb 28 2010 copyright : (C) 2010 by Thomas Richard email : thomas.richard@proan.be ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef PREFSUNITS_H #define PREFSUNITS_H #include #include #include #include "ui_unitseditbase.h" class QModelIndex; class QPushButton; class ImpTreeView; class QAbstractItemModel; class QDataWidgetMapper; class QSqlTableModel; class QSortFilterProxyModel; class PrefsUnits : public QWidget { Q_OBJECT public: PrefsUnits(QWidget* parent); ~PrefsUnits(); void save(); public slots: void slotAddUnit(); void slotEditUnit(QModelIndex index = QModelIndex()); void slotDeleteUnit(); void slotUnitSelected(QModelIndex); private: QPushButton *mDelUnit; QPushButton *mEditUnit; ImpTreeView *mUnitsTreeView; QSqlTableModel *mUnitsModel; QSortFilterProxyModel *mProxyModel; }; class UnitsEditDialog: public QDialog, protected Ui::UnitsEditBase { Q_OBJECT public: UnitsEditDialog( QAbstractItemModel *model, int row, QWidget *parent ); public slots: void accept(); void reject(); private: Ui::UnitsEditBase *mBaseWidget; QDataWidgetMapper *mapper; QAbstractItemModel *mModel; int mRow; }; #endif kraft-0.97/src/prefswages.cpp000066400000000000000000000205771410616450300162340ustar00rootroot00000000000000/*************************************************************************** prefswages.cpp - the wages tab in the prefs dialog ------------------- begin : Feb 26 2010 copyright : (C) 2010 by Thomas Richard email : thomas.richard@proan.be ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include "defaultprovider.h" #include "impviewwidgets.h" #include "geld.h" #include "defaultprovider.h" #include "prefswages.h" PrefsWages::PrefsWages(QWidget* parent) : QWidget(parent) { QVBoxLayout *vboxLay = new QVBoxLayout; mWagesModel = new QSqlTableModel(this); mWagesModel->setTable("stdSaetze"); mWagesModel->setSort(3, Qt::AscendingOrder); mWagesModel->setEditStrategy(QSqlTableModel::OnManualSubmit); mWagesModel->select(); mWagesModel->setHeaderData(0, Qt::Horizontal, i18n("ID")); mWagesModel->setHeaderData(1, Qt::Horizontal, i18n("Code")); mWagesModel->setHeaderData(2, Qt::Horizontal, i18n("Price")); mWagesModel->setHeaderData(3, Qt::Horizontal, i18n("Sortkey")); mProxyModel = new QSortFilterProxyModel(this); mProxyModel->setSourceModel(mWagesModel); mWagesTreeView = new ImpTreeView; vboxLay->addWidget( mWagesTreeView ); mWagesTreeView->setModel(mProxyModel); mWagesTreeView->setItemDelegate(new WagesItemDelegate()); mWagesTreeView->hideColumn(0); mWagesTreeView->hideColumn(3); mWagesTreeView->header()->stretchLastSection(); mWagesTreeView->setColumnWidth(1, 200); mWagesTreeView->resizeColumnToContents(2); mWagesTreeView->resizeColumnToContents(1); mWagesTreeView->setEditTriggers(ImpTreeView::NoEditTriggers); connect( mWagesTreeView, SIGNAL(clicked(QModelIndex)), SLOT( slotWageSelected(QModelIndex) ) ); connect( mWagesTreeView, SIGNAL(doubleClicked(QModelIndex)), SLOT(slotEditWage(QModelIndex))); QHBoxLayout *butLay = new QHBoxLayout; butLay->addStretch( 1 ); mUp = new QPushButton( QIcon::fromTheme("arrow-up"), i18n( "Up" )); connect( mUp, SIGNAL( clicked() ), SLOT( slotUp() ) ); butLay->addWidget( mUp ); mUp->setEnabled(false); mDown = new QPushButton( QIcon::fromTheme("arrow-down"), i18n( "Down" )); connect( mDown, SIGNAL( clicked() ), SLOT( slotDown() ) ); butLay->addWidget( mDown ); mDown->setEnabled(false); QPushButton *but = new QPushButton( QIcon::fromTheme("list-add"), i18n( "Add" )); connect( but, SIGNAL( clicked() ), SLOT( slotAddWage() ) ); butLay->addWidget( but ); mEditWage = new QPushButton( QIcon::fromTheme("document-edit"), i18n( "Edit" )); connect( mEditWage, SIGNAL( clicked() ), SLOT( slotEditWage() ) ); butLay->addWidget( mEditWage ); mEditWage->setEnabled(false); mDelWage = new QPushButton( QIcon::fromTheme("list-remove"), i18n( "Remove" ) ); connect( mDelWage, SIGNAL( clicked() ), SLOT( slotDeleteWage() ) ); butLay->addWidget( mDelWage ); mDelWage->setEnabled( false ); vboxLay->addLayout( butLay ); this->setLayout( vboxLay ); } PrefsWages::~PrefsWages() { } void PrefsWages::save() { mWagesModel->submitAll(); } void PrefsWages::slotAddWage() { WagesEditDialog *dialog = new WagesEditDialog(mWagesModel, -1, this); dialog->show(); } void PrefsWages::slotEditWage(QModelIndex /* index */ ) { if ( mWagesTreeView->currentIndex().isValid() ) { int row = mWagesTreeView->currentIndex().row(); WagesEditDialog *dialog = new WagesEditDialog(mWagesModel, row, this); dialog->show(); } } void PrefsWages::slotDeleteWage() { if ( mWagesTreeView->currentIndex().isValid() ) { int row = mWagesTreeView->currentIndex().row(); mWagesModel->removeRows(row, 1); } } void PrefsWages::slotWageSelected(QModelIndex) { bool state = false; if ( mWagesTreeView->currentIndex().isValid() ) { state = true; } mEditWage->setEnabled( state ); mDelWage->setEnabled( state ); mUp->setEnabled( state ); mDown->setEnabled( state ); if(mWagesTreeView->currentIndex().row() == 0) mUp->setEnabled(false); if(mWagesTreeView->currentIndex().row() == (mProxyModel->rowCount() - 1)) mDown->setEnabled(false); } void PrefsWages::slotUp() { if ( mWagesTreeView->currentIndex().isValid() ) { int row = mWagesTreeView->currentIndex().row(); if(row != 0) { mProxyModel->setData(mProxyModel->index(row, 3), row, Qt::DisplayRole); mProxyModel->setData(mProxyModel->index(row, 3), row, Qt::EditRole); mProxyModel->setData(mProxyModel->index(row-1, 3), row + 1, Qt::DisplayRole); mProxyModel->setData(mProxyModel->index(row-1, 3), row + 1, Qt::EditRole); mProxyModel->sort(3, Qt::AscendingOrder); slotWageSelected(mWagesTreeView->currentIndex()); } } } void PrefsWages::slotDown() { if ( mWagesTreeView->currentIndex().isValid() ) { int row = mWagesTreeView->currentIndex().row(); if(row != (mProxyModel->rowCount() - 1)) { mProxyModel->setData(mProxyModel->index(row, 3), row + 2, Qt::DisplayRole); mProxyModel->setData(mProxyModel->index(row, 3), row + 2, Qt::EditRole); mProxyModel->setData(mProxyModel->index(row+1, 3), row + 1, Qt::DisplayRole); mProxyModel->setData(mProxyModel->index(row+1, 3), row + 1, Qt::EditRole); mProxyModel->sort(3, Qt::AscendingOrder); slotWageSelected(mWagesTreeView->currentIndex()); } } } WagesEditDialog::WagesEditDialog( QAbstractItemModel *model, int row, QWidget *parent ) : QDialog( parent ) { setObjectName( "WAGES_EDIT_DIALOG" ); setModal( true ); setWindowTitle( i18n( "Edit a wage group" ) ); QWidget *mainWidget = new QWidget; QVBoxLayout *mainLayout = new QVBoxLayout; QWidget *w = new QWidget; mainLayout->addWidget(w); mBaseWidget = new Ui::WagesEditBase( ); mBaseWidget->setupUi( w ); mBaseWidget->mWage->setSuffix( DefaultProvider::self()->currencySymbol() ); mBaseWidget->mWage->setMinimum( 0 ); mBaseWidget->mWage->setMaximum( 100000 ); mBaseWidget->mWage->setDecimals( 2 ); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); setLayout(mainLayout); mainLayout->addWidget(mainWidget); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); mainLayout->addWidget(buttonBox); mModel = model; mapper = new QDataWidgetMapper(this); mapper->setSubmitPolicy(QDataWidgetMapper::ManualSubmit); mapper->setModel(model); mapper->addMapping(mBaseWidget->mGroupName, 1); mapper->addMapping(mBaseWidget->mWage, 2); if(row == -1) { //Insert a new row at the end model->insertRow(model->rowCount()); mapper->toLast(); } else { mBaseWidget->mLabel->setText(i18n("

            Edit wage group

            ")); mapper->setCurrentIndex(row); } mRow = row; } void WagesEditDialog::accept() { mapper->submit(); QDialog::accept(); } void WagesEditDialog::reject() { if(mRow == -1) mModel->removeRow(mModel->rowCount()-1); QDialog::reject(); } WagesItemDelegate::WagesItemDelegate(QObject * parent) : QItemDelegate(parent) {} void WagesItemDelegate::paint ( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const { if(index.column() == 2) { Geld wage = index.data(Qt::DisplayRole).toDouble(); QString string = wage.toString(); drawDisplay(painter, option, option.rect, string); } else { QItemDelegate::paint(painter, option, index); } } kraft-0.97/src/prefswages.h000066400000000000000000000046321410616450300156730ustar00rootroot00000000000000/*************************************************************************** prefswages.h - the wages tab in the prefs dialog ------------------- begin : Feb 26 2010 copyright : (C) 2010 by Thomas Richard email : thomas.richard@proan.be ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef PREFSWAGES_H #define PREFSWAGES_H #include #include #include #include "ui_wageseditbase.h" class QModelIndex; class QPushButton; class ImpTreeView; class QAbstractItemModel; class QDataWidgetMapper; class QSqlTableModel; class QSortFilterProxyModel; class PrefsWages : public QWidget { Q_OBJECT public: PrefsWages(QWidget* parent); ~PrefsWages(); void save(); public slots: void slotAddWage(); void slotEditWage(QModelIndex index = QModelIndex()); void slotDeleteWage(); void slotWageSelected(QModelIndex); void slotUp(); void slotDown(); private: QPushButton *mDelWage; QPushButton *mEditWage; QPushButton *mUp; QPushButton *mDown; ImpTreeView *mWagesTreeView; QSqlTableModel *mWagesModel; QSortFilterProxyModel *mProxyModel; }; class WagesEditDialog: public QDialog, protected Ui::WagesEditBase { Q_OBJECT public: WagesEditDialog( QAbstractItemModel *model, int row, QWidget *parent ); public slots: void accept(); void reject(); private: Ui::WagesEditBase *mBaseWidget; QDataWidgetMapper *mapper; QAbstractItemModel *mModel; int mRow; }; class WagesItemDelegate : public QItemDelegate { Q_OBJECT public: WagesItemDelegate(QObject * parent = 0); virtual void paint ( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const; }; #endif kraft-0.97/src/reportgenerator.cpp000066400000000000000000000262631410616450300173060ustar00rootroot00000000000000/*************************************************************************** Report Generator based on Reportlab ------------------- begin : July 2006 copyright : (C) 2006 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "reportgenerator.h" #include "kraftdoc.h" #include "kraftdb.h" #include "unitmanager.h" #include "dbids.h" #include "kraftsettings.h" #include "docposition.h" #include "einheit.h" #include "archiveman.h" #include "archdoc.h" #include "documentman.h" #include "texttemplate.h" #include "defaultprovider.h" #include "doctype.h" #include "addressprovider.h" #include "grantleetemplate.h" #include "documenttemplate.h" #include "pdfconverter.h" namespace { QString saveToTempFile( const QString& doc ) { if ( ! doc.isEmpty() ) { QTemporaryFile temp; temp.setAutoRemove( false ); if ( temp.open() ) { QTextStream s(&temp); // The following explicit coding settings were needed for Qt 4.7.3, former Qt versions // seemed to default on UTF-8. Try to comment the following two lines for older Qt versions // if needed and see if the trml file on the disk still is UTF-8 encoded. QTextCodec *codec = QTextCodec::codecForName("UTF-8"); s.setCodec( codec ); s << doc; temp.close(); } else { // qDebug () << "ERROR: Could not open temporar file"; } qDebug () << "Wrote rml to " << temp.fileName(); return temp.fileName(); } return QString(); } } ReportGenerator::ReportGenerator() : _useGrantlee(true), mProcess(nullptr) { mAddressProvider = new AddressProvider(this); connect(mAddressProvider, &AddressProvider::lookupResult, this, &ReportGenerator::slotAddresseeFound); } ReportGenerator::~ReportGenerator() { // qDebug () << "ReportGen is destroyed!"; } /* * docID: document ID * dbId: database ID of the archived doc. * * This is the starting point of a report creation. */ void ReportGenerator::createDocument( ReportFormat format, const QString& docID, dbID archId ) { mDocId = docID; mArchId = archId; _requestedFormat = format; if( mProcess && mProcess->state() != QProcess::NotRunning ) { qDebug() << "===> WRN: Process still running, try again later."; emit failure(i18n("Document generation process is still running.")); return; } // now the addressee search through the address provider is finished. // Rendering can be started. _archDoc.loadFromDb(archId); // the next call also sets the watermark options _tmplFile = findTemplateFile( _archDoc.docType() ); if ( _tmplFile.isEmpty() ) { qDebug () << "tmplFile is empty, exit reportgenerator!"; return; } else { qDebug () << "Using this template: " << _tmplFile; } lookupCustomerAddress(); } void ReportGenerator::lookupCustomerAddress() { const QString clientUid = _archDoc.clientUid(); KContacts::Addressee contact; if( ! clientUid.isEmpty() ) { AddressProvider::LookupState state = mAddressProvider->lookupAddressee( clientUid ); switch( state ) { case AddressProvider::LookupFromCache: contact = mAddressProvider->getAddresseeFromCache(clientUid); break; case AddressProvider::LookupNotFound: case AddressProvider::ItemError: case AddressProvider::BackendError: // set an empty contact break; case AddressProvider::LookupOngoing: case AddressProvider::LookupStarted: // Not much to do, just wait and let the addressprovider // hit the slotAddresseFound return; } } slotAddresseeFound(clientUid, contact); } void ReportGenerator::slotAddresseeFound( const QString&, const KContacts::Addressee& contact ) { mCustomerContact = contact; // now the three pillars archDoc, myContact and mCustomerContact are defined. QFileInfo fi(_tmplFile); if (!fi.exists()) { emit failure(i18n("Template file is not accessible.")); return; } const QString ext = fi.completeSuffix(); QString output; QScopedPointer templateEngine; QPointer converter; if (QString::compare(ext, QStringLiteral("trml"), Qt::CaseInsensitive) == 0) { // use the old ctemplate engine with reportlab. templateEngine.reset(new CTemplateDocumentTemplate(_tmplFile)); converter = new ReportLabPDFConverter; } else { // use Grantlee. templateEngine.reset(new GrantleeDocumentTemplate(_tmplFile)); converter = new WeasyPrintPDFConverter; } converter->setTemplatePath(fi.path()); // expand the template... const QString expanded = templateEngine->expand(&_archDoc, myContact, mCustomerContact); if (expanded.isEmpty()) { emit failure(i18n("The template conversion failed.")); delete converter; return; } // ... and save to a tempoarary file const QString tempFile = saveToTempFile(expanded); if (tempFile.isEmpty()) { emit failure(i18n("Saving to temporar file failed.")); delete converter; return; } QString fullOutputPath = targetFileName(); if (mMergeIdent == "1" || mMergeIdent == "2") { // check if the watermark file exists QFileInfo fi(mWatermarkFile); if (!mWatermarkFile.isEmpty() && fi.isReadable()) { QTemporaryFile tmpFile; tmpFile.open(); tmpFile.close(); // PDF merge is required. Write to temp file fullOutputPath = tmpFile.fileName() + QStringLiteral(".pdf"); } else { mMergeIdent = "0"; qDebug() << "Can not read watermark file, generating without" << mWatermarkFile; } } // Now there is the completed, expanded document source. connect( converter, &PDFConverter::docAvailable, this, &ReportGenerator::slotPdfDocAvailable); connect( converter, &PDFConverter::converterError, this, &ReportGenerator::slotConverterError); converter->convert(tempFile, fullOutputPath); } void ReportGenerator::slotPdfDocAvailable(const QString& file) { QObject *s = sender(); qDebug() << "The document is finished!:" << file; s->deleteLater(); // check for the watermark requirements if (mMergeIdent == "1" || mMergeIdent == "2") { // check if the watermark file exists mergePdfWatermark(file); } else { emit docAvailable(_requestedFormat, file, mCustomerContact); } } void ReportGenerator::mergePdfWatermark(const QString& file) { mProcess = new QProcess(); connect(mProcess, QOverload::of(&QProcess::finished), this, &ReportGenerator::pdfMergeFinished); const QString prg = DefaultProvider::self()->locateKraftTool(QStringLiteral("watermarkpdf.py")); if (!prg.isEmpty()) { mProcess->setProgram( QStringLiteral("python3") ); QStringList args; args << prg; args << QStringLiteral("-m") << mMergeIdent; args << QStringLiteral("-o") << targetFileName(); args << mWatermarkFile; args << file; mProcess->setArguments(args); mProcess->start( ); } } void ReportGenerator::pdfMergeFinished(int exitCode, QProcess::ExitStatus exitStatus) { if (exitStatus == QProcess::ExitStatus::NormalExit && exitCode == 0) { const QString fileName = targetFileName(); const QString tmpFile = mProcess->arguments().last(); QFile::remove(tmpFile); mProcess->deleteLater(); mProcess = nullptr; emit docAvailable(_requestedFormat, fileName, mCustomerContact); } else { slotConverterError(PDFConverter::ConvError::PDFMergerError); } } void ReportGenerator::slotConverterError(PDFConverter::ConvError err) { QObject *s = sender(); QString errMsg; switch(err) { case PDFConverter::ConvError::NoError: errMsg = i18n("No converter error."); break; case PDFConverter::ConvError::TrmlToolFail: errMsg = i18n("The ReportLab based converter script can not be executed."); break; case PDFConverter::ConvError::UnknownError: errMsg = i18n("An unknown error happened."); break; case PDFConverter::ConvError::NoReportLabMod: errMsg = i18n("The ReportLab python module is not installed."); break; case PDFConverter::ConvError::SourceFileFail: errMsg = i18n("The source file can not be read."); break; case PDFConverter::ConvError::TargetFileError: errMsg = i18n("The target can not be opened to write."); break; case PDFConverter::ConvError::TargetFileMissing: errMsg = i18n("The target file does not exist."); break; case PDFConverter::ConvError::WeasyPrintNotFound: errMsg = i18n("The WeasyPrint tool is not installed."); break; case PDFConverter::ConvError::PDFMergerError: errMsg = i18n("The PDF merger utility failed."); break; } emit failure(errMsg); s->deleteLater(); } QString ReportGenerator::targetFileName() const { ArchDocDigest dig = _archDoc.toDigest(); return dig.pdfArchiveFileName(); } QString ReportGenerator::findTemplateFile( const QString& type ) { DocType dType( type ); const QString tmplFile = dType.templateFile(); if ( tmplFile.isEmpty() ) { emit failure(i18n("There is not template defined for %1.").arg(dType.name())); } else { // check if file exists QFileInfo fi(tmplFile); if (!fi.isFile()) { emit failure(i18n("The template file %1 for document type %2 does not exist.").arg(tmplFile).arg(dType.name())); return QString(); } if (!fi.isReadable()) { emit failure(i18n("The template file %1 for document type %2 can not be read.").arg(tmplFile).arg(dType.name())); return QString(); } } mMergeIdent = dType.mergeIdent(); mWatermarkFile = dType.watermarkFile(); return tmplFile; } void ReportGenerator::setMyContact( const KContacts::Addressee& contact ) { myContact = contact; } kraft-0.97/src/reportgenerator.h000066400000000000000000000064161410616450300167510ustar00rootroot00000000000000/*************************************************************************** reportgenerator.h - report generation ------------------- begin : July 2006 copyright : (C) 2006 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef REPORTGENERATOR_H #define REPORTGENERATOR_H #include #include #include #include #include #include "kraftdoc.h" #include "archdoc.h" #include "pdfconverter.h" class dbID; class KJob; class QFile; class AddressProvider; class TextTemplate; enum class ReportFormat { PDF, PDFMail, HTML }; class ReportGenerator : public QObject { Q_OBJECT public: ReportGenerator(); ~ReportGenerator(); signals: void docAvailable( ReportFormat, const QString& file, const KContacts::Addressee& customerContact); void failure(const QString&); public slots: void createDocument(ReportFormat, const QString&, dbID ); void setMyContact( const KContacts::Addressee& ); private slots: void slotPdfDocAvailable(const QString& file); void slotConverterError(PDFConverter::ConvError err); void mergePdfWatermark(const QString &file); void pdfMergeFinished(int exitCode, QProcess::ExitStatus exitStatus); private: QString findTemplateFile( const QString& ); void lookupCustomerAddress(); QString _tmplFile; ArchDoc _archDoc; protected: protected slots: void slotAddresseeFound( const QString&, const KContacts::Addressee& ); private: void convertTemplate( const QString& ); void fillupTemplateFromArchive( const dbID& ); void contactToTemplate( TextTemplate*, const QString&, const KContacts::Addressee& ); QString registerDictionary( const QString&, const QString& ) const; QString registerTag( const QString&, const QString& ) const; QString registerDictTag( const QString&, const QString&, const QString& ) const; QString targetFileName() const; QString escapeTrml2pdfXML( const QString& str ) const; QString rmlString( const QString& str, const QString& paraStyle = QString() ) const; bool _useGrantlee; QString mErrors; QString mMergeIdent; bool mHavePdfMerge; QString mWatermarkFile; QString mDocId; dbID mArchId; long mOutputSize; KContacts::Addressee mCustomerContact; KContacts::Addressee myContact; QPointer mProcess; QFile mFile; QDataStream mTargetStream; AddressProvider *mAddressProvider; ReportFormat _requestedFormat; }; #endif kraft-0.97/src/setupassistant.cpp000066400000000000000000000730271410616450300171560ustar00rootroot00000000000000/*************************************************************************** setupassistant - assistant to setup kraft from scratch ------------------- begin : 2009-12-26 copyright : (C) 2009 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include #include #include #include #include #include "setupassistant.h" #include "databasesettings.h" #include "defaultprovider.h" #include "kraftdb.h" #include "addressselectorwidget.h" #include "kraftsettings.h" WelcomePage::WelcomePage(QWidget *parent) :QWizardPage(parent) { setTitle( i18n("Welcome to the Kraft Setup Assistant")); QVBoxLayout *vbox = new QVBoxLayout; setLayout( vbox ); QWidget *w = new QWidget; vbox->addWidget( w ); ui.setupUi(w); } void WelcomePage::setWelcomeText( const QString& txt ) { ui.mStatusText->setText( txt ); } // --------------------------------------------------------------------------- DbSelectPage::DbSelectPage(QWidget *parent) :QWizardPage(parent) { setTitle(i18n("Select the Database Backend")); QVBoxLayout *vbox = new QVBoxLayout; setLayout( vbox ); QWidget *w = new QWidget; vbox->addWidget( w ); ui.setupUi(w); registerField("SelectedDbDriverSqlite", ui.mRbSqlite3); registerField("SelectedDbDriverMySql", ui.mRbMySQL); } int DbSelectPage::nextId() const { if( ui.mRbSqlite3->isChecked() ) { return SetupAssistant::sqlitePageNo; } else { return SetupAssistant::mySqlPageNo; } } QString DbSelectPage::selectedDriver() const { QString re = "QSQLITE"; if( field("SelectedDbDriverMySql").toBool() ) { re = "QMYSQL"; } return re; } // --------------------------------------------------------------------------- SqLiteDetailsPage::SqLiteDetailsPage(QWidget *parent) :QWizardPage(parent) { setTitle(i18n("Sqlite File Name")); QVBoxLayout *vbox = new QVBoxLayout; setLayout(vbox); QWidget *w = new QWidget; vbox->addWidget( w ); ui.setupUi(w); // ui.mFileUrl->setText(DatabaseSettings::self()->dbFile()); registerField("DefaultSqliteStorage", ui.mRbDefault ); registerField("SqliteStorageFile", ui._fileName); // Preset the sqlite storage const QString fileName = DatabaseSettings::self()->dbFile(); if( ! fileName.isEmpty()) { ui.mRbCustom->setChecked(true); setField("SqliteStorageFile", fileName); } } QUrl SqLiteDetailsPage::url() { QString fileName; if( ui.mRbDefault->isChecked() ) { fileName = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); } else { fileName = ui._fileName->text(); } if( ! fileName.endsWith("/")) fileName += QLatin1String("/"); fileName += QLatin1String("kraft.db"); return QUrl::fromLocalFile(fileName); } bool SqLiteDetailsPage::validatePage() { return qobject_cast(wizard())->handleSqLiteDetails(); } int SqLiteDetailsPage::nextId() const { if( KraftDB::self()->databaseExists() ) { return SetupAssistant::upgradeDbPageNo; } else { return SetupAssistant::createDbPageNo; } } // --------------------------------------------------------------------------- MysqlDetailsPage::MysqlDetailsPage(QWidget *parent) :QWizardPage(parent) { setTitle(i18n("MySql Detail Information")); QVBoxLayout *vbox = new QVBoxLayout; setLayout( vbox ); QWidget *w = new QWidget; vbox->addWidget( w ); ui.setupUi(w); registerField("MySqlHost", ui.mMysqlHost); registerField("MySqlUser", ui.mMysqUser ); registerField("MySqlDbName", ui.mMysqlDbName); registerField("MySqlPwd", ui.mMysqlPwd ); reloadSettings(); } void MysqlDetailsPage::reloadSettings() { setField("MySqlHost", DatabaseSettings::self()->dbServerName()); setField("MySqlUser", DatabaseSettings::self()->dbUser()); setField("MySqlDbName", DatabaseSettings::self()->dbDatabaseName()); setField("MySqlPwd", DatabaseSettings::self()->dbPassword()); } int MysqlDetailsPage::nextId() const { if( KraftDB::self()->databaseExists() ) { return SetupAssistant::upgradeDbPageNo; } else { return SetupAssistant::createDbPageNo; } } bool MysqlDetailsPage::validatePage() { bool re = qobject_cast(wizard())->handleMysqlDetails(); return re; } // --------------------------------------------------------------------------- CreateDbPage::CreateDbPage(QWidget *parent) :QWizardPage(parent) { setTitle(i18n("Create Database")); QVBoxLayout *vbox = new QVBoxLayout; setLayout( vbox ); QWidget *w = new QWidget; vbox->addWidget( w ); ui.setupUi(w); registerField("CreateDbStatusText", ui.mCreateStatus); } void CreateDbPage::setStatusText( const QString& t ) { ui.mCreateStatus->setText( t ); } void CreateDbPage::setFillCmdsCount( int cnt ) { mFills = 0; ui.mFillProgress->setMaximum( cnt ); ui.mFillProgress->setValue( 0 ); ui.mFillCounter->setText( i18n("0/%1", cnt) ); } void CreateDbPage::setFillCmdsCurrent( int cnt ) { ui.mFillProgress->setValue( cnt ); } void CreateDbPage::setCreateCmdsCount( int cnt ) { mCreates = 0; ui.mCreateProgress->setMaximum( cnt ); ui.mCreateProgress->setValue( 0 ); ui.mCreateCounter->setText( i18n("0/%1", cnt)); } void CreateDbPage::setCreateCmdsCurrent( int cnt ) { ui.mCreateProgress->setValue( cnt ); } void CreateDbPage::slotStatusMessage( const QString& msg ) { // qDebug () << "############### success: " << msg; ui.mCreateStatus->setText( msg ); } void CreateDbPage::slotCountCreateProgress( bool res ) { if( res ) { mCreates++; ui.mCreateProgress->setValue( mCreates ); ui.mCreateCounter->setText( i18n("%1/%2", mCreates, ui.mCreateProgress->maximum() ) ); } } void CreateDbPage::slotCountFillProgress( bool res ) { if( res ) { mFills++; ui.mFillProgress->setValue( mFills ); ui.mFillCounter->setText( i18n("%1/%2", mFills, ui.mFillProgress->maximum() ) ); } } int CreateDbPage::nextId() const { return SetupAssistant::upgradeDbPageNo; } // --------------------------------------------------------------------------- UpgradeDbPage::UpgradeDbPage(QWidget *parent) :QWizardPage(parent) { setTitle(i18n("Upgrade the Database")); QVBoxLayout *vbox = new QVBoxLayout; setLayout( vbox ); QWidget *w = new QWidget; vbox->addWidget( w ); ui.setupUi(w); } void UpgradeDbPage::slotSetStatusText( const QString& txt ) { ui.mUpgradeStatus->setText( txt ); } void UpgradeDbPage::slotSetOverallCount( int cnt ) { mUpgrades = 0; ui.mUpgradeProgress->setMaximum( cnt ); ui.mUpgradeProgress->setValue( 0 ); updateCounter(); } void UpgradeDbPage::updateCounter() { ui.mUpgradeCounter->setText( i18n("%1/%2", mUpgrades, ui.mUpgradeProgress->maximum() )); } void UpgradeDbPage::slotCountFillProgress( bool res ) { if( res ) { mUpgrades++; ui.mUpgradeProgress->setValue( mUpgrades ); updateCounter(); } } int UpgradeDbPage::nextId() const { return SetupAssistant::ownAddressPageNo; } // --------------------------------------------------------------------------- OwnAddressPage::OwnAddressPage(QWidget *parent) :QWizardPage(parent) { setTitle(i18n("Your Company Address")); QVBoxLayout *vbox = new QVBoxLayout; QTabWidget *tabWidget = new QTabWidget; QLabel *l = new QLabel; l->setText( i18n("Select your companies address either from the address book or enter it manually. It is set as a consigner on the documents.") ); vbox->addWidget( l ); vbox->addWidget(tabWidget); // == The AddressSelector page QWidget *w = new QWidget; tabWidget->addTab(w, i18n("Select from Addressbook")); QVBoxLayout *vbox1 = new QVBoxLayout; mAddresses = new AddressSelectorWidget(this); vbox1->addWidget( mAddresses ); w->setLayout( vbox1 ); setLayout(vbox); connect( mAddresses, SIGNAL( addressSelected(KContacts::Addressee)), SLOT( gotMyAddress( KContacts::Addressee ) ) ); // == The manual page QWidget *w1 = new QWidget; ui.setupUi(w1); int id = tabWidget->addTab(w1, i18n("Manual Entry")); ui.nameLabel->setText( KContacts::Addressee::formattedNameLabel() ); ui.orgLabel->setText( KContacts::Addressee::organizationLabel()); ui.streetLabel->setText(KContacts::Addressee::businessAddressStreetLabel()); ui.postCodeLabel->setText(KContacts::Addressee::businessAddressPostalCodeLabel()); ui.cityLabel->setText(KContacts::Addressee::businessAddressLocalityLabel()); ui.phoneLabel->setText(KContacts::Addressee::businessPhoneLabel()); ui.faxLabel->setText(KContacts::Addressee::businessFaxLabel()); ui.mobileLabel->setText(KContacts::Addressee::mobilePhoneLabel()); ui.emailLabel->setText(KContacts::Addressee::emailLabel()); ui.websiteLabel->setText(KContacts::Addressee::urlLabel()); if( !mAddresses->backendUp() ) { tabWidget->setCurrentIndex(id); } } OwnAddressPage::~OwnAddressPage() { delete mAddresses; } void OwnAddressPage::gotMyAddress(const KContacts::Addressee& addressee) { mMe = addressee; } void OwnAddressPage::saveOwnName() { if( ! mMe.isEmpty() ) { KraftSettings::self()->setUserName( mMe.name() ); KraftSettings::self()->setUserUid( mMe.uid() ); KraftSettings::self()->save(); } else { // check for the manual. KContacts::Addressee add; add.setFormattedName(ui.leName->text()); add.setOrganization(ui.leOrganization->text()); KContacts::Address workAddress; workAddress.setStreet(ui.leStreet->text()); workAddress.setPostalCode(ui.lePostcode->text()); workAddress.setLocality(ui.leCity->text()); workAddress.setType(KContacts::Address::Work); add.insertAddress(workAddress); add.insertPhoneNumber(PhoneNumber(ui.lePhone->text(), KContacts::PhoneNumber::Work)); add.insertPhoneNumber(PhoneNumber(ui.leFax->text(), KContacts::PhoneNumber::Fax)); add.insertPhoneNumber(PhoneNumber(ui.leMobile->text(), KContacts::PhoneNumber::Cell)); ResourceLocatorUrl resUrl; resUrl.setUrl(QUrl(ui.leWebsite->text())); add.setUrl(resUrl); add.insertEmail(ui.leEmail->text(), true /* prefered */ ); VCardConverter vcc; QByteArray vcard = vcc.createVCard(add); QString file = QStandardPaths::writableLocation( QStandardPaths::AppDataLocation ); file += "/myidentity.vcd"; QFile f ( file ); if (f.open(QIODevice::WriteOnly | QIODevice::Text)) { f.write(vcard); f.close(); qDebug() << "Saved own identity to " << file; } } } int OwnAddressPage::nextId() const { return SetupAssistant::finalStatusPageNo; } // --------------------------------------------------------------------------- FinalStatusPage::FinalStatusPage(QWidget *parent) :QWizardPage(parent) { setTitle(i18n("Final Status")); QVBoxLayout *vbox = new QVBoxLayout; setLayout( vbox ); //TODO PORT QT5 vbox->setSpacing( QDialog::spacingHint() ); //TODO PORT QT5 vbox->setMargin( QDialog::marginHint() ); QWidget *w = new QWidget; vbox->addWidget( w ); ui.setupUi(w); ui.mStatusText->setTextFormat( Qt::RichText ); } void FinalStatusPage::slotSetStatusText( const QString& txt ) { ui.mStatusText->setText( txt ); } int FinalStatusPage::nextId() const { return -1; // final page } // --------------------------------------------------------------------------- SetupAssistant::SetupAssistant( QWidget *parent ) :QWizard( parent ), mMode( Reinit ) { setPage( welcomePageNo, new WelcomePage); setPage( dbSelectPageNo, new DbSelectPage); setPage( mySqlPageNo, new MysqlDetailsPage); setPage( sqlitePageNo, new SqLiteDetailsPage); setPage( createDbPageNo, new CreateDbPage); setPage( upgradeDbPageNo, new UpgradeDbPage); setPage( ownAddressPageNo, new OwnAddressPage); setPage( finalStatusPageNo, new FinalStatusPage); connect( this, SIGNAL( currentIdChanged( int) ), this, SLOT( slotCurrentPageChanged( int) ) ); resize( QSize( 450, 260 ) ); } /* * Current Database Setup Wizard +----------------+ check if db already exists ----------------------------- > | MySQL Page ----------------------------------+ -/ +--------+-------+ v +------------+ +--------------/ | +----------------+ +---------------+ | Welcome --->| DB Select | +--->+---------> | create DB Page +--->| upgrade DB | +------------+ +--------------\ | +----------------+ +-----------+---+ -\ +---+------------+ ^ | > | SQLite Page ----------------------------------+ | +----------------+ | | +---------------+ +-----------v---+ | Final Status |<---+ Own Address | +---------------+ +---------------+ */ QString SetupAssistant::defaultSqliteFilename() const { const QString path = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation); QFileInfo fi(path, "kraft.db"); if( !fi.dir().exists() ) { if (! fi.dir().mkpath(path) ) { qDebug() << "Failed to create directory "<< path << "for sqlite db"; return QString(); } } return fi.filePath(); } void SetupAssistant::slotCurrentPageChanged( int currId ) { qDebug() << "Page changed to " << currId; if( currId == dbSelectPageNo ) { } else if( currId == mySqlPageNo ) { // TODO: set the mysql datails } else if( currId == sqlitePageNo) { // TODO set the sqlite filename } else if( currId == createDbPageNo ) { if( mSqlBackendDriver == QLatin1String("QMYSQL") ) { const QString dbName = field("MySqlDbName").toString(); if(!KraftDB::self()->dbConnect( QLatin1String("QMYSQL"), dbName, field("MySqlUser").toString(), field("MySqlHost").toString(), field("MySqlPwd").toString() ) ) { setField("CreateDbStatusText", i18n( "

            Can't connect to your database. Are you sure your credentials are correct and the database exists?

            ") ); return; } } else { QString filename = field("SqliteStorageFile").toString(); if(filename.isEmpty()) { filename = defaultSqliteFilename(); setField("SqliteStorageFile", filename); } if( !KraftDB::self()->dbConnect( QLatin1String("QSQLITE"), filename, QString(), QString(), QString()) ) { setField("CreateDbStatusText", i18n("

            Can't open your database file, check the permissions and such.")); } } if( !KraftDB::self()->databaseExists() ) { // qDebug () << "Start to create the database"; startDatabaseCreation(); } else { // qDebug () << "CreateDB-Page: Database already existing"; setField("CreateDbStatusText", i18n( "

            The database is already existing, no action needs to be taken here.

            " "

            Please hit next to proceed.

            " ) ); } } if( currId == upgradeDbPageNo ) { if( KraftDB::self()->databaseExists() ) { // qDebug () << "start to update the database"; startDatabaseUpdate(); } else { // qDebug () << "Strange problem at dbupdate: DB does not exist"; } } if( currId == finalStatusPageNo ) { finalizePage(); } } void SetupAssistant::done( int result ) { if( result > 0 ) { // store the stakeholders own name for picking the sender address qobject_cast(page(ownAddressPageNo))->saveOwnName(); const QString selectedDriver = qobject_cast(page(dbSelectPageNo))->selectedDriver(); DatabaseSettings::self()->setDbDriver( selectedDriver ); if( selectedDriver == QLatin1String("QSQLITE") ) { const QString file = field("SqliteStorageFile").toString(); DatabaseSettings::self()->setDbFile(file); } if( selectedDriver == "QMYSQL" ) { DatabaseSettings::self()->setDbDatabaseName( field("MySqlDbName").toString() ); DatabaseSettings::self()->setDbUser( field("MySqlUser").toString() ); const QString host = field("MySqlHost").toString(); DatabaseSettings::self()->setDbServerName( host ); DatabaseSettings::self()->setDbPassword( field("MySqlPwd").toString() ); } DatabaseSettings::self()->save(); qDebug () << "Database backend config written"; } QWizard::done(result); } void SetupAssistant::finalizePage() { QString txt; if( mErrors.isEmpty() ) { txt = i18n( "

            The database setup was successfully completed.

            " ); txt += i18n("

            You can start to work with Kraft now. Please do not forget to

            "); txt += "
              "; txt += i18n("
            • adjust various settings in the Kraft Preferences dialog.
            • " ); txt += i18n("
            • Check the Catalog chapter list.
            • " ); txt += i18n("
            • Make your business and have fun.
            • " ); txt += "
            "; txt += i18n("

            If you press Finish now, the new database configuration is stored in Krafts configuration.

            "); } else { foreach( QString err, mErrors ) { txt += "

            " + err + "

            "; } } // qDebug() << "this is the status text: " << txt; qobject_cast(page(finalStatusPageNo))->slotSetStatusText( txt ); } void SetupAssistant::startDatabaseUpdate() { CreateDbPage *mCreateDbPage = qobject_cast(page(createDbPageNo)); UpgradeDbPage *mUpgradeDbPage = qobject_cast(page(upgradeDbPageNo)); if( ! KraftDB::self()->isOk() ) { mCreateDbPage->setStatusText( i18n("The Database can not be connected. Please check the database credentials.")); button(NextButton)->setEnabled(false); return; } if( !KraftDB::self()->databaseExists() ) { mCreateDbPage->setStatusText( i18n("The database core tables do not exist. Please check initial setup.")); button(NextButton)->setEnabled(false); return; } button(NextButton)->setEnabled(true); if( KraftDB::self()->currentSchemaVersion() == KraftDB::self()->requiredSchemaVersion() ) { mUpgradeDbPage->slotSetStatusText( i18n("Database is up to date. No upgrade is required.")); return; } // Database really needs update mUpgradeDbPage->slotSetStatusText( i18n("Parse Update Commands...")); int overallCmdCount = 0; QList commandLists; int currentVer = KraftDB::self()->currentSchemaVersion(); if( currentVer == -1 ) currentVer = 1; // set to initial version while ( currentVer < KraftDB::self()->requiredSchemaVersion() ) { ++currentVer; // qDebug () << "######### Reading " << migrateFilename; const SqlCommandList cmds = KraftDB::self()->parseCommandFile( currentVer ); commandLists.append(cmds); overallCmdCount += cmds.count(); qDebug() << "Appending" << cmds.count() << "commands for version" << currentVer; } mUpgradeDbPage->slotSetOverallCount( overallCmdCount ); // qDebug () << "4."; connect( KraftDB::self(), SIGNAL( statusMessage( const QString& ) ), mUpgradeDbPage, SLOT( slotSetStatusText( const QString& ) ) ); connect( KraftDB::self(), SIGNAL( processedSqlCommand( bool ) ), mUpgradeDbPage, SLOT( slotCountFillProgress( bool ) ) ); int doneOverallCmds = 0; bool errors = false; currentVer = KraftDB::self()->currentSchemaVersion(); for( SqlCommandList cmdList : commandLists ) { currentVer++; int goodCmds = KraftDB::self()->processSqlCommands( cmdList ); doneOverallCmds += goodCmds; if( goodCmds != cmdList.count() ) { qDebug () << "Only performned " << goodCmds << " out of " << cmdList.count(); errors = true; break; } else { qDebug () << goodCmds << " commands performed well, version is " << currentVer << ", listversion:" << cmdList.number(); KraftDB::self()->setSchemaVersion( QString::number( currentVer )); } } if( errors ) { mUpgradeDbPage->slotSetStatusText( i18n("The Upgrade failed!") );; } else { mUpgradeDbPage->slotSetStatusText( i18n("The Upgrade succeeded, the current schema version is %1!", KraftDB::self()->requiredSchemaVersion() ) );; } disconnect( mUpgradeDbPage, SLOT( slotSetStatusText( const QString& ))); } void SetupAssistant::startDatabaseCreation() { CreateDbPage *mCreateDbPage = qobject_cast(page(createDbPageNo)); if( ! KraftDB::self()->isOk() ) { mCreateDbPage->setStatusText( i18n("The Database can not be connected. Please check the database credentials!")); button(NextButton)->setEnabled(false); return; } button(NextButton)->setEnabled(true); mCreateDbPage->setStatusText( i18n("Parse Create Commands...") ); SqlCommandList createCommands = KraftDB::self()->parseCommandFile( "create_schema.sql"); QString dbFill( "fill_schema_en.sql" ); if ( DefaultProvider::self()->locale()->country() == QLocale::Germany ) { dbFill = "fill_schema_de.sql"; } mCreateDbPage->setStatusText( i18n( "Parse database fillup commands..." ) ); SqlCommandList fillCommands = KraftDB::self()->parseCommandFile( dbFill ); mCreateDbPage->setCreateCmdsCount( createCommands.count() ); mCreateDbPage->setCreateCmdsCurrent( 0 ); mCreateDbPage->setFillCmdsCount( fillCommands.count() ); mCreateDbPage->setFillCmdsCurrent( 0 ); connect( KraftDB::self(), SIGNAL( statusMessage( const QString& ) ), mCreateDbPage, SLOT( slotStatusMessage( const QString& ) ) ); connect( KraftDB::self(), SIGNAL( processedSqlCommand( bool ) ), mCreateDbPage, SLOT( slotCountCreateProgress( bool ) ) ); mCreateDbPage->setStatusText( i18n( "Processing database creation commands...") ); int creates = KraftDB::self()->processSqlCommands( createCommands ); bool res = true; if( creates != createCommands.count() ) { // qDebug () << "NOT all create commands succeeded!"; res = false; } else { // qDebug () << creates << "(=All) create commands succeeded!"; // lets do the fillup disconnect( KraftDB::self(), SIGNAL(processedSqlCommand(bool)),0,0 ); connect( KraftDB::self(), SIGNAL( processedSqlCommand( bool ) ), mCreateDbPage, SLOT( slotCountFillProgress( bool ) ) ); mCreateDbPage->setStatusText( i18n( "Process database fillup commands..." ) ); creates = KraftDB::self()->processSqlCommands( fillCommands ); if( creates != fillCommands.count() ) { qDebug() << "Could not execute all fill commands"; res = false; } } if( res ) { mCreateDbPage->setStatusText( i18n( "Successfully finished commands." ) ); } else { mCreateDbPage->setStatusText( i18n( "Failed to perform all commands." ) ); // FIXME: Disable next button } disconnect( KraftDB::self(), SIGNAL(statusMessage( const QString&)),0 ,0 ); disconnect( KraftDB::self(), SIGNAL(processedSqlCommand(bool)),0 ,0 ); } bool SetupAssistant::handleSqLiteDetails() { DbSelectPage *mDbSelectPage = qobject_cast(page(dbSelectPageNo)); QString file = field("SqliteStorageFile").toString(); qDebug () << "The SqlLite database file is " << file; mSqlBackendDriver = mDbSelectPage->selectedDriver(); // qDebug () << "The database driver is " << mSqlBackendDriver; bool re = KraftDB::self()->dbConnect( mSqlBackendDriver, file, QString(), QString(), QString() ); return re; } bool SetupAssistant::handleMysqlDetails() { DbSelectPage *mDbSelectPage = qobject_cast(page(dbSelectPageNo)); mSqlBackendDriver = mDbSelectPage->selectedDriver(); QString hostName = field("MySqlHost").toString(); QString databaseName = field("MySqlDbName").toString(); QString userName = field("MySqlUser").toString(); QString password = field("MySqlPwd").toString(); return KraftDB::self()->dbConnect( mSqlBackendDriver, databaseName, userName, hostName, password ); } bool SetupAssistant::init( Mode mode ) { bool startDialog = false; QString text; QString configOrigin; mMode = mode; text = QLatin1String("

            ") + i18n("This assistant guides you through the basic settings of your Kraft installation.") + QLatin1String("

            "); bool hitNextClosing = true; if( mMode == Reinit ) { startDialog = true; } else if( mode == Update ) { if( QStandardPaths::locate(QStandardPaths::GenericConfigLocation, "kraftdatabaserc" ).isEmpty() ) { // migration failed and we do not have a config file. All from scratch configOrigin = i18n("There was no database configuration found."); } else { configOrigin = i18n("A valid current database configuration file was found."); // qDebug () << "A standard KDE Platform 4.x database config file is there."; } const QString dbDriver = DatabaseSettings::self()->dbDriver().toUpper(); QString dbName = DatabaseSettings::self()->dbDatabaseName(); if( dbDriver == QLatin1String("QSQLITE")) { dbName = DatabaseSettings::self()->dbFile(); } if( KraftDB::self()->dbConnect( dbDriver, dbName, DatabaseSettings::self()->dbUser(), DatabaseSettings::self()->dbServerName(), DatabaseSettings::self()->dbPassword()) ) { // try to connect with default values // qDebug () << "The database can be opened!"; if( KraftDB::self()->databaseExists() ) { // qDebug () << "The database exists."; if( KraftDB::self()->currentSchemaVersion() < KraftDB::self()->requiredSchemaVersion() ) { // qDebug () << "Need a database schema update."; startDialog = true; configOrigin += QLatin1String(" ") + i18n("The database schema version is too low. " "It will be updated."); } else if( KraftDB::self()->currentSchemaVersion() > KraftDB::self()->requiredSchemaVersion() ) { configOrigin += QLatin1Char(' ') + i18n("The current database schema version is too high. Leaving untouched! "); // qDebug () << "Database Schema is OK. Nothing to do for StartupAssistant"; } } else { // qDebug () << "The database is not existing. It needs to be recreated."; startDialog = true; text = i18n( "

            The database can be opened, but does not contain valid content.

            " "

            A new database can be created automatically from scratch.

            "); } } else { // unable to connect to the database at all startDialog = true; hitNextClosing = false; text = i18n( "

            Kraft failed to connect to the configured database.

            " ); if( KraftDB::self()->qtDriver().toUpper() == "QMYSQL" ) { text += i18n( "

            Please check the database server setup and restart Kraft to connect." ); } else { text += i18n("

            Please check the database file."); } text += " " + i18n( "or create a new database by hitting next.

            " ); } } if( startDialog ) { WelcomePage *welcomePage = qobject_cast(page(welcomePageNo)); if( hitNextClosing ) text += i18n("

            Please hit next and follow the instructions.

            "); welcomePage->setWelcomeText( configOrigin + text ); } return startDialog ; } SetupAssistant::~SetupAssistant() { } kraft-0.97/src/setupassistant.h000066400000000000000000000120441410616450300166130ustar00rootroot00000000000000/*************************************************************************** setupassistant - assistant to setup kraft from scratch ------------------- begin : 2009-12-26 copyright : (C) 2009 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef SETUPASSISTANT_H #define SETUPASSISTANT_H #include #include #include #include "ui_statuspage.h" #include "ui_dbselect.h" #include "ui_mysqldetails.h" #include "ui_createdb.h" #include "ui_upgradedb.h" #include "ui_sqlitedetails.h" #include "ui_identity.h" #include "kraftcat_export.h" class QUrl; class AddressSelectorWidget; using namespace KContacts; class WelcomePage:public QWizardPage { Q_OBJECT public: WelcomePage( QWidget *parent = 0 ); void setWelcomeText( const QString& ); private: Ui::statusPage ui; }; // --------------------------------------------------------------------------- class DbSelectPage:public QWizardPage { Q_OBJECT public: DbSelectPage( QWidget *parent = 0 ); QString selectedDriver() const; int nextId() const; private: Ui::dbSelectForm ui; }; // --------------------------------------------------------------------------- class SqLiteDetailsPage:public QWizardPage { Q_OBJECT public: SqLiteDetailsPage( QWidget *parent = 0 ); QUrl url(); int nextId() const; bool validatePage(); private: Ui::sqLiteDetailsForm ui; }; // --------------------------------------------------------------------------- class MysqlDetailsPage:public QWizardPage { Q_OBJECT public: MysqlDetailsPage( QWidget *parent = 0 ); void reloadSettings(); int nextId() const; bool validatePage(); private: Ui::mySqlDetailsForm ui; }; // --------------------------------------------------------------------------- class CreateDbPage:public QWizardPage { Q_OBJECT public: CreateDbPage( QWidget *parent = 0 ); void setStatusText( const QString& ); void setCreateCmdsCount( int ); void setFillCmdsCount( int ); void setCreateCmdsCurrent( int ); void setFillCmdsCurrent( int ); int nextId() const; public slots: void slotStatusMessage( const QString& ); void slotCountCreateProgress( bool ); void slotCountFillProgress( bool ); private: Ui::createDbForm ui; int mCreates; int mFills; }; // --------------------------------------------------------------------------- class UpgradeDbPage:public QWizardPage { Q_OBJECT public: UpgradeDbPage( QWidget *parent = 0 ); int nextId() const; public slots: void slotSetStatusText( const QString& ); void slotSetOverallCount( int ); void slotCountFillProgress( bool ); private: void updateCounter(); Ui::upgradeDbForm ui; int mUpgrades; }; // --------------------------------------------------------------------------- class OwnAddressPage:public QWizardPage { Q_OBJECT public: OwnAddressPage( QWidget *parent=0 ); ~OwnAddressPage(); void saveOwnName(); int nextId() const; private: AddressSelectorWidget *mAddresses; KContacts::Addressee mMe; Ui::manualOwnIdentity ui; private slots: void gotMyAddress( const KContacts::Addressee& addressee); }; // --------------------------------------------------------------------------- class FinalStatusPage:public QWizardPage { Q_OBJECT public: FinalStatusPage( QWidget *parent = 0 ); int nextId() const; public slots: void slotSetStatusText( const QString& ); private: Ui::statusPage ui; }; // --------------------------------------------------------------------------- class SetupAssistant: public QWizard { Q_OBJECT public: enum { welcomePageNo, dbSelectPageNo, mySqlPageNo, sqlitePageNo, createDbPageNo, upgradeDbPageNo, finalStatusPageNo, ownAddressPageNo }; enum Mode{ Reinit, Update }; SetupAssistant( QWidget *parent = 0 ); bool init( Mode ); ~SetupAssistant(); bool handleSqLiteDetails(); bool handleMysqlDetails(); public slots: void done( int ); private slots: void slotCurrentPageChanged(int currId); private: void startDatabaseCreation(); void startDatabaseUpdate(); void finalizePage(); QString defaultSqliteFilename() const; Mode mMode; QStringList mErrors; QString mSqlBackendDriver; }; #endif // SETUPASSISTANT_H kraft-0.97/src/sqlitedetails.ui000066400000000000000000000041251410616450300165570ustar00rootroot00000000000000 sqLiteDetailsForm 0 0 421 245 Please enter the SQLite Database Settings. Pick a filename to name the SQLite database file or leave the default setting. true store the database file at default place. true buttonGroup select a file name: buttonGroup Qt::Vertical 20 107 kraft-0.97/src/statuspage.ui000066400000000000000000000015211410616450300160650ustar00rootroot00000000000000 statusPage 0 0 404 185 TextLabel Qt::RichText Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop true kraft-0.97/src/stdsatzman.cpp000066400000000000000000000064151410616450300162510ustar00rootroot00000000000000/*************************************************************************** stdsatzman - ------------------- begin : 2004-13-09 copyright : (C) 2004 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ // include files for Qt #include #include #include #include #include // include files for KDE #include "stdsatzman.h" #include "kraftdb.h" Q_GLOBAL_STATIC(StdSatzMan, mSelf) StdSatz::StdSatz(): m_dbId(0) { } StdSatz::StdSatz( int id ): m_dbId(id) { } StdSatz::StdSatz( int id, const QString& name, Geld g ): m_dbId(id), m_name(name), m_value(g) { } /* * ********** Stundensatz Duration ********** */ StdSatzDuration::StdSatzDuration() :mDuration( -1 ) { } StdSatzDuration::StdSatzDuration( const StdSatz& std, int dur ) :StdSatz( std ), mDuration( dur ) { } /* * ********** Stundensatz Manager ********** */ StdSatzMan *StdSatzMan::self() { return mSelf; } StdSatzMan::StdSatzMan( ) { load(); } QStringList StdSatzMan::allStdSaetze() { QStringList list; load(); StdSatzVector::iterator it; for( it = mStdSaetze.begin(); it != mStdSaetze.end(); ++it ) { QString n = (*it).getName(); if( !n.isEmpty()) list << n; } return list; } StdSatz StdSatzMan::getStdSatz( const QString& name ) { load(); StdSatzVector::iterator it; for( it = mStdSaetze.begin(); it != mStdSaetze.end(); ++it ) { if( (*it).getName() == name ) return (*it); } return StdSatz(); } StdSatz StdSatzMan::getStdSatz( dbID id ) { load(); StdSatzVector::iterator it; for( it = mStdSaetze.begin(); it != mStdSaetze.end(); ++it ) { dbID dbid = (*it).getId(); if( dbid == id ) return (*it); } return StdSatz(); } StdSatzMan::~StdSatzMan( ) { } void StdSatzMan::load() { /* noetige Groesse rausfinden */ int max = -1; QSqlQuery q("SELECT count(*) from stdSaetze;"); if( q.isActive()) { q.next(); max = q.value(0).toInt(); } // qDebug () << "Groesse fuer Stundensatzliste: " << max << endl; mStdSaetze.resize( max ); /* Daten laden */ q.prepare("SELECT stdSaetzeID, name, price FROM stdSaetze ORDER BY sortKey"); q.exec(); while( q.next() ) { int satzID = q.value(0).toInt(); // qDebug () << "Neue StdSatz ID " << satzID << endl; // resize if index is to big. StdSatz ss( satzID, q.value(1).toString(), Geld( q.value(2).toDouble())); mStdSaetze.append(ss); } } /* END */ kraft-0.97/src/stdsatzman.h000066400000000000000000000045371410616450300157210ustar00rootroot00000000000000/*************************************************************************** stdsatzman - ------------------- begin : 2004-13-09 copyright : (C) 2004 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef _STDSATZMAN_H #define _STDSATZMAN_H /* * Hour rate management: There are different named hour rates such as Master * or helper with a different amount of euros. The cost per hour can be adjusted * document globally. */ // include files #include #include "geld.h" #include "dbids.h" class QString; /** * das Stundensatzobjekt: definiert durch id und namen */ class StdSatz { public: StdSatz(); /** * Konstruktur basierend auf der Datenbank id */ StdSatz(int id); StdSatz( int id, const QString& name, Geld g ); dbID getId() { return m_dbId; } QString getName() { return m_name; } Geld getPreis() { return m_value; } private: dbID m_dbId; QString m_name; Geld m_value; }; class StdSatzDuration : public StdSatz { public: StdSatzDuration(); StdSatzDuration( const StdSatz&, int ); int duration() { return mDuration; } void setDuration( int d ) { mDuration = d; } private: int mDuration; }; typedef QVector StdSatzVector; /** * der Stundensatzmanager */ class StdSatzMan { public: virtual ~StdSatzMan(); static StdSatzMan *self(); QStringList allStdSaetze(); StdSatz getStdSatz( const QString& name ); StdSatz getStdSatz( dbID id ); // static StdSatzMan *mSelf; StdSatzMan(); private: void load(); StdSatzVector mStdSaetze; }; #endif /* END */ kraft-0.97/src/stockmaterial.cpp000066400000000000000000000066631410616450300167300ustar00rootroot00000000000000/*************************************************************************** material - ------------------- begin : 2004-05-05 copyright : (C) 2004 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ // include files for Qt #include #include // include files for KDE #include #include #include "stockmaterial.h" #include "unitmanager.h" #include "materialsaverbase.h" #include "materialsaverdb.h" #include "defaultprovider.h" #include "format.h" #include "kraftsettings.h" StockMaterial::StockMaterial( ): CatalogTemplate(), m_amount( 0 ), m_dbid( -1 ) { } StockMaterial::StockMaterial( int dbid, int matChap, QString mat, int unitID, double perPack, Geld pIn, Geld pOut ): CatalogTemplate(), m_chapter(matChap), m_amount(perPack), m_dbid(dbid), m_ePrice(pIn), m_vPrice(pOut) { this->setUnitId(unitID); this->setText(mat); } StockMaterial::~StockMaterial( ) { } MaterialSaverBase* StockMaterial::getSaver() { return MaterialSaverDB::self(); } bool StockMaterial::save() { MaterialSaverBase *saver = getSaver(); if ( saver ) { saver->saveTemplate( this ); return true; } return false; } #if 0 QString StockMaterial::description() const { return m_descr; } void StockMaterial::setDescription( const QString& str ) { m_descr = str; } #endif double StockMaterial::getAmountPerPack() { return m_amount; } void StockMaterial::setAmountPerPack( double am ) { m_amount = am; } int StockMaterial::getID() { return m_dbid; } void StockMaterial::setID( int id ) { m_dbid = id; } KContacts::Addressee StockMaterial::getSupplier() { KContacts::Addressee a; return a; } void StockMaterial::setSupplier( KContacts::Addressee *a ) { if( a ) m_delivererUID = a->uid(); } Geld StockMaterial::purchPrice() { return m_ePrice; } Geld StockMaterial::salesPrice() { return m_vPrice; } Geld StockMaterial::unitPrice() { return salesPrice() / m_amount; } void StockMaterial::setPurchPrice( Geld g ) { m_ePrice = g; } void StockMaterial::setSalesPrice( Geld g ) { m_vPrice = g; } void StockMaterial::setLastModified( QDate dt ) { mLastModified = dt; } void StockMaterial::setEnterDate( QDate dt ) { mEnteredDate = dt; } QString StockMaterial::lastModified() { return Format::toDateString( mLastModified, KraftSettings::self()->dateFormat()); } QString StockMaterial::entered() { return Format::toDateString( mEnteredDate, KraftSettings::self()->dateFormat() ); } void StockMaterial::saveChapterId() { MaterialSaverBase *saver = getSaver(); if( saver ) { saver->saveTemplateChapter( this ); } } /* END */ kraft-0.97/src/stockmaterial.h000066400000000000000000000052561410616450300163720ustar00rootroot00000000000000/*************************************************************************** material - Material for calculations ------------------- begin : 2004-05-05 copyright : (C) 2004 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef _MATERIAL_H #define _MATERIAL_H // include files #include #include #include "kraftglobals.h" #include "einheit.h" #include "catalogtemplate.h" /** * */ class Einheit; class MaterialSaverBase; class QDate; class QDateTime; class StockMaterial : public CatalogTemplate { public: StockMaterial(); StockMaterial( int dbid, int matChap, QString mat, int unitID, double perPack, Geld pIn, Geld pOut ); ~StockMaterial(); #if 0 // currently not used. QString description() const; void setDescription( const QString& ); #endif double getAmountPerPack(); void setAmountPerPack( double am ); int getID(); void setID( int ); int chapter() { return m_chapter; } void setChapter( int c ) { m_chapter = c; } KContacts::Addressee getSupplier(); void setSupplier( KContacts::Addressee *supp ); Geld purchPrice(); Geld salesPrice(); Geld unitPrice(); void setPurchPrice( Geld ); void setSalesPrice( Geld ); void setLastModified( QDate ); void setEnterDate( QDate ); QString lastModified(); QString entered(); bool save(); protected: MaterialSaverBase* getSaver(); void saveChapterId(); private: // QString m_descr; int m_chapter; // per package: double m_amount; int m_unit; int m_dbid; // FIXME: introduce supplier list QString m_delivererUID; Geld m_ePrice; // price for bying Geld m_vPrice; // price for selling QDate mLastModified; QDate mEnteredDate; }; class StockMaterialList : public QList { public: StockMaterialList() : QList() { } }; typedef QListIterator StockMaterialListIterator; #endif /* END */ kraft-0.97/src/tagman.cpp000066400000000000000000000112621410616450300153240ustar00rootroot00000000000000/*************************************************************************** TagTemplateManager - Manage the tag templates ------------------- begin : June 2008 copyright : (C) 2008 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ // include files for Qt #include #include #include #include #include #include "tagman.h" #include "kraftdb.h" /* * ********** Tag Template ********** */ TagTemplate::TagTemplate() { } TagTemplate::TagTemplate( const dbID& id, const QString& name, const QString& desc, const QString& col ) : mId( id ), mName( name ), mDesc( desc ), mColor( col ) { } QPalette TagTemplate::palette() const { QPalette palette; palette.setColor( QPalette::Light, mColor.light() ); palette.setColor( QPalette::Dark, mColor.dark() ); palette.setColor( QPalette::Mid, mColor ); return palette; } bool TagTemplate::operator!= ( const TagTemplate& tt ) const { return !( mName == tt.mName && mDesc == tt.mDesc && mColor == tt.mColor ); } /* * ********** Tag Template Manager ********** */ Q_GLOBAL_STATIC(TagTemplateMan, mSelf) TagTemplateMan *TagTemplateMan::self() { return mSelf; } TagTemplateMan::TagTemplateMan( ) { load(); } QStringList TagTemplateMan::allTagTemplates() { QStringList list; TagTemplateValueVector::iterator it; for( it = mTagTmpl.begin(); it != mTagTmpl.end(); ++it ) { QString n = (*it).name(); if( !n.isEmpty()) list << n; } return list; } TagTemplate TagTemplateMan::getTagTemplate( const QString& name ) { TagTemplateValueVector::iterator it; for( it = mTagTmpl.begin(); it != mTagTmpl.end(); ++it ) { if( (*it).name() == name ) return (*it); } return TagTemplate(); } TagTemplate TagTemplateMan::getTagTemplateFromId( const QString& id ) { TagTemplateValueVector::iterator it; for( it = mTagTmpl.begin(); it != mTagTmpl.end(); ++it ) { if( (*it).dbId().toString() == id ) return (*it); } return TagTemplate(); } TagTemplateMan::~TagTemplateMan( ) { } bool TagTemplateMan::writeTemplate( const TagTemplate& tt ) { bool ret = true; int cnt = 0; if ( tt.dbId().isOk() ) { QSqlQuery q; q.prepare( "UPDATE tagTemplates SET name=:name, description=:desc, color=:col " "WHERE tagTmplID=:id" ); q.bindValue( ":name", tt.name() ); q.bindValue( ":desc", tt.description() ); q.bindValue( ":col", tt.color().name() ); q.bindValue( ":id", tt.dbId().toString() ); q.exec(); cnt = q.numRowsAffected(); } if ( cnt == -1 ) { qCritical() << "DB does not know the number of affected rows, poor!" << endl; ret = false; } else if ( cnt == 0 ) { // qDebug () << "need to insert the tag template into db" << endl; QSqlQuery qi; qi.prepare( "INSERT INTO tagTemplates (name, sortKey, description, color) VALUES " "( :name, :sortKey, :desc, :col )" ); qi.bindValue( ":sortKey", 0 ); qi.bindValue( ":name", tt.name() ); qi.bindValue( ":desc", tt.description() ); qi.bindValue( ":col", tt.color().name() ); qi.exec(); } if ( ret ) { load(); } return ret; } void TagTemplateMan::deleteTemplate( const dbID& id ) { if ( id.isOk() ) { QSqlQuery q; q.prepare( "DELETE FROM tagTemplates WHERE tagTmplID=:id" ); q.bindValue( ":id", id.toString() ); q.exec(); load(); } } void TagTemplateMan::load() { mTagTmpl.clear(); /* read tag templates from db */ /* FIXME: The sortKey sort is not working because the sortKey is not correctly set on write */ /* With the initial db setup come useful sortKeys, thats why we still sort for it. */ QSqlQuery q1( "SELECT tagTmplID, name, description, color FROM tagTemplates ORDER BY sortKey, name" ); while( q1.next()) { dbID id( q1.value(0).toInt() ); TagTemplate tt ( id, q1.value(1).toString(), q1.value(2).toString(), q1.value(3).toString() ); mTagTmpl.append( tt ); } } /* END */ kraft-0.97/src/tagman.h000066400000000000000000000044701410616450300147740ustar00rootroot00000000000000/*************************************************************************** tag manager - create, edit and remove tags. ------------------- begin : June 2008 copyright : (C) 2008 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef _TAGMAN_H #define _TAGMAN_H // include files #include #include "geld.h" #include "dbids.h" class QString; class QColor; class QPalette; /** * das Stundensatzobjekt: definiert durch id und namen */ class TagTemplate { public: TagTemplate(); TagTemplate( const dbID&, const QString&, const QString&, const QString& ); QString name() const { return mName; } QColor color() const { return mColor; } QPalette palette() const; dbID dbId() const { return mId; } QString description() const { return mDesc; } void setName( const QString& n ) { mName = n; } void setDescription( const QString& d ) { mDesc = d; } void setColor( const QColor& c ) { mColor = c; } bool operator!= ( const TagTemplate& tt ) const; private: dbID mId; QString mName; QString mDesc; QColor mColor; }; typedef QVector TagTemplateValueVector; /** * Tag Template Manager */ class TagTemplateMan { public: ~TagTemplateMan(); static TagTemplateMan *self(); QStringList allTagTemplates(); TagTemplate getTagTemplate( const QString& ); TagTemplate getTagTemplateFromId( const QString& ); bool writeTemplate( const TagTemplate& ); void deleteTemplate( const dbID& ); TagTemplateMan(); private: void load(); TagTemplateValueVector mTagTmpl; }; #endif /* END */ kraft-0.97/src/tagtemplatesdialog.cpp000066400000000000000000000202271410616450300177300ustar00rootroot00000000000000/*************************************************************************** tagtemplatesdialog.h - Edit tag templates ------------------- begin : Sep 2008 copyright : (C) 2008 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "tagtemplatesdialog.h" #include "defaultprovider.h" #include "tagman.h" TagTemplateEditor::TagTemplateEditor( QWidget *parent ) : QDialog( parent ) { setObjectName("TAG_TEMPLATES_EDITOR"); setModal( true ); setWindowTitle( i18n("Edit Tag Template" )); QVBoxLayout *mainLayout = new QVBoxLayout; setLayout(mainLayout); QVBoxLayout *w = new QVBoxLayout( this ); w->addWidget(new QLabel( QString::fromLatin1( "

            " ) + i18n( "Edit a Tag Template" ) + QString::fromLatin1( "

            " ))); w->addWidget( new QLabel( i18n( "Adjust settings for name, color and description." ))); QHBoxLayout *h1 = new QHBoxLayout; h1->addWidget( new QLabel( i18n( "Name:" ) )); mNameEdit = new QLineEdit; h1->addWidget(mNameEdit); w->addLayout(h1); // QHBox *h2 = new QHBox( w ); w->addWidget(new QLabel( i18n( "Description:" ))); mDescriptionEdit = new QTextEdit; w->addWidget(mDescriptionEdit); QHBoxLayout *h2 = new QHBoxLayout; h2->addStretch(1); h2->addWidget(new QLabel( i18n( "Associated Color:" ))); mColorButton = new QPushButton; h2->addWidget(mColorButton); connect( mColorButton, SIGNAL(clicked(bool)), SLOT(slotColorSelect(bool))); w->addLayout(h2); mainLayout->addLayout(w); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); mOkButton = buttonBox->button(QDialogButtonBox::Ok); mOkButton->setDefault(true); mOkButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); mainLayout->addWidget(buttonBox); } TagTemplateEditor::~TagTemplateEditor() { } void TagTemplateEditor::slotColorSelect(bool) { mColor = QColorDialog::getColor(mOrigTemplate.color(), this); setColorButton(); mOkButton->setFocus(); } void TagTemplateEditor::setColorButton() { QPixmap pix(32, 32); QPainter painter(&pix); painter.setBrush(QBrush(mColor)); painter.drawRect( QRect(0 ,0 , 32, 32)); mColorButton->setIcon(QIcon(pix)); } void TagTemplateEditor::setTemplate( const TagTemplate& tt ) { mOrigTemplate = tt; mNameEdit->setText( tt.name() ); mDescriptionEdit->setText( tt.description() ); mColor = tt.color(); setColorButton(); } TagTemplate TagTemplateEditor::currentTemplate() { TagTemplate tt = mOrigTemplate; tt.setName( mNameEdit->text() ); tt.setDescription( mDescriptionEdit->toPlainText() ); tt.setColor(mColor); return tt; } // ################################################################################ TagTemplatesDialog::TagTemplatesDialog( QWidget *parent ) : QDialog( parent ) { setObjectName( "TAG_TEMPLATES_DIALOG" ); setModal( true ); setWindowTitle( i18n("Edit Tag Templates" ) ); QVBoxLayout *mainLayout = new QVBoxLayout; mainLayout->addWidget(new QLabel( QString::fromLatin1( "

            " ) + i18n( "Edit Tag Templates" ) + QString::fromLatin1( "

            " ))); mainLayout->addWidget(new QLabel( i18n( "Add, edit and remove tag templates for use in the documents." ))); mListView = new QTreeWidget; // mListView->setItemMargin( 3 ); // mListView->setAlternateBackground( QColor( "#dffdd0" ) ); // mListView->headerItem()->hide(); mListView->setRootIsDecorated( false ); mListView->setSelectionMode( QAbstractItemView::SingleSelection ); QStringList headers; headers << i18n( "Tag" ); headers << i18n( "Color" ); headers << i18n( "Description" ); mListView->setHeaderLabels( headers ); mListView->setAllColumnsShowFocus( true ); mListView->setSelectionMode( QAbstractItemView::SingleSelection ); connect( mListView, SIGNAL( itemSelectionChanged() ), this, SLOT( slotSelectionChanged() ) ); mainLayout->addWidget(mListView); setTags(); QHBoxLayout *buttBox = new QHBoxLayout; mAddButton = new QPushButton( i18n( "Add..." )); buttBox->addWidget(mAddButton); mEditButton = new QPushButton( i18n( "Edit..." )); buttBox->addWidget(mEditButton); mEditButton->setEnabled( false ); mDeleteButton = new QPushButton( i18n( "Delete..." )); buttBox->addWidget(mDeleteButton); mDeleteButton->setEnabled( false ); mainLayout->addLayout(buttBox); connect( mAddButton, SIGNAL( clicked() ), SLOT( slotAddTemplate() ) ); connect( mEditButton, SIGNAL( clicked() ), SLOT( slotEditTemplate() ) ); connect( mDeleteButton, SIGNAL( clicked() ), SLOT( slotDeleteTemplate() ) ); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); mainLayout->addWidget(buttonBox); setLayout(mainLayout); slotSelectionChanged(); } TagTemplatesDialog::~TagTemplatesDialog() { } void TagTemplatesDialog::slotAddTemplate() { TagTemplateEditor dia( this ); if ( dia.exec() ) { TagTemplateMan::self()->writeTemplate( dia.currentTemplate() ); setTags(); } } void TagTemplatesDialog::slotEditTemplate() { TagTemplateEditor dia( this ); TagTemplate curr = currentTemplate(); dia.setTemplate( curr ); if ( dia.exec() ) { TagTemplate tt = dia.currentTemplate(); if ( tt != curr ) { TagTemplateMan::self()->writeTemplate( tt ); setTags(); } } } void TagTemplatesDialog::slotDeleteTemplate() { QMessageBox msgBox; msgBox.setText(i18n( "Do you really want to delete the template?")); msgBox.setStandardButtons(QMessageBox::Yes| QMessageBox::No); msgBox.setDefaultButton(QMessageBox::Yes); int ret = msgBox.exec(); if ( ret == QMessageBox::Yes) { TagTemplateMan::self()->deleteTemplate( currentTemplate().dbId() ); setTags(); } } void TagTemplatesDialog::slotSelectionChanged() { bool state = false; if ( mListView->selectedItems().size() ) { state = true; } mEditButton->setEnabled( state ); mDeleteButton->setEnabled( state ); } TagTemplate TagTemplatesDialog::currentTemplate() { QTreeWidgetItem *item = mListView->currentItem(); if ( item ) { QString templName = mItemMap[item]; return TagTemplateMan::self()->getTagTemplate( templName ); } return TagTemplate(); } void TagTemplatesDialog::setTags() { mListView->clear(); QStringList tags = TagTemplateMan::self()->allTagTemplates(); foreach( const QString t, tags ) { TagTemplate templ = TagTemplateMan::self()->getTagTemplate( t ); // TagItem *item = new QListViewItem( mListView, templ.name(), QCheckListItem::CheckBox ); QTreeWidgetItem *item = new QTreeWidgetItem( mListView ); item->setText( 1, templ.name() ); QPixmap pix( 16, 12 ); pix.fill( templ.color() ); item->setIcon( 0, pix ); // item->setColorGroup( templ.colorGroup() ); item->setText( 2, templ.description() ); mItemMap[item] = t; } } kraft-0.97/src/tagtemplatesdialog.h000066400000000000000000000041251410616450300173740ustar00rootroot00000000000000/*************************************************************************** tagtemplatedit.h - Edit tag templates ------------------- begin : Sep 2008 copyright : (C) 2008 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef TAGTEMPLATESDIALOG_H #define TAGTEMPLATESDIALOG_H #include #include #include "tagman.h" class QWidget; class QTreeWidget; class QTreeWidgetItem; class QStringList; class QPushButton; class QLineEdit; class QTextEdit; class TagTemplateEditor: public QDialog { Q_OBJECT public: TagTemplateEditor( QWidget* ); ~TagTemplateEditor(); void setTemplate( const TagTemplate& ); TagTemplate currentTemplate(); private slots: void slotColorSelect(bool); void setColorButton(); private: TagTemplate mOrigTemplate; QLineEdit *mNameEdit; QTextEdit *mDescriptionEdit; QPushButton *mColorButton; QPushButton *mOkButton; QColor mColor; }; class TagTemplatesDialog: public QDialog { Q_OBJECT public: TagTemplatesDialog( QWidget* ); ~TagTemplatesDialog( ); TagTemplate currentTemplate(); protected slots: void slotSelectionChanged(); void slotAddTemplate(); void slotEditTemplate(); void slotDeleteTemplate(); protected: void setTags( ); private: QTreeWidget *mListView; QMap mItemMap; QPushButton *mAddButton; QPushButton *mEditButton; QPushButton *mDeleteButton; }; #endif kraft-0.97/src/taxeditbase.ui000066400000000000000000000050651410616450300162110ustar00rootroot00000000000000 TaxEditBase 0 0 348 158 <h2>Add a Tax Rate</h2> false Start-Date: false Qt::Horizontal QSizePolicy::Expanding 71 20 &Reduced Tax Rate: false mReducedTax &Full Tax Rate: false mFullTax kraft-0.97/src/taxeditdialog.cpp000066400000000000000000000070471410616450300167050ustar00rootroot00000000000000/*************************************************************************** taxeditdialog.h - edit tax rates ------------------- begin : Apr 9 2009 copyright : (C) 2009 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include #include #include #include #include #include #include #include #include "taxeditdialog.h" TaxEditDialog::TaxEditDialog( QSqlTableModel *taxModel, QWidget *parent ) : QDialog( parent ) { setObjectName( "TAX_EDIT_DIALOG" ); setModal( true ); setWindowTitle( i18n( "Edit Tax Rates" ) ); QVBoxLayout *mainLayout = new QVBoxLayout; setLayout(mainLayout); QWidget *w = new QWidget; mainLayout->addWidget(w); mBaseWidget = new Ui::TaxEditBase( ); mBaseWidget->setupUi( w ); mBaseWidget->mDateWidget->setDate( QDate::currentDate() ); mBaseWidget->mFullTax->setSuffix( "%" ); mBaseWidget->mReducedTax->setSuffix( "%" ); mBaseWidget->mFullTax->setRange( 0,100.0 ); mBaseWidget->mFullTax->setDecimals( 1 ); mBaseWidget->mReducedTax->setRange( 0, 100.0 ); mBaseWidget->mReducedTax->setDecimals( 1 ); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); mainLayout->addWidget(buttonBox); this->model = taxModel; mapper = new QDataWidgetMapper(this); mapper->setSubmitPolicy(QDataWidgetMapper::AutoSubmit); mapper->setModel(taxModel); mapper->addMapping(mBaseWidget->mFullTax, 1); mapper->addMapping(mBaseWidget->mReducedTax, 2); mapper->addMapping(mBaseWidget->mDateWidget, 3); model->insertRow(model->rowCount()); mapper->toLast(); } void TaxEditDialog::accept() { mapper->submit(); //Check if the inserted date already exists, if so update the existing record and delete this record for(int i = 0; i < model->rowCount() - 1; ++i) { if (model->index(i, 3).data(Qt::DisplayRole).toDate() == mBaseWidget->mDateWidget->date() ) { //Check if the row isn't removed QString headerdata = model->headerData(i, Qt::Vertical, Qt::DisplayRole).toString(); if(headerdata != "!") { model->setData(model->index(i, 1, QModelIndex()), mBaseWidget->mFullTax->value(), Qt::EditRole); model->setData(model->index(i, 2, QModelIndex()), mBaseWidget->mReducedTax->value(), Qt::EditRole); model->removeRow(model->rowCount()-1); } } } QDialog::accept(); } void TaxEditDialog::reject() { model->removeRow(model->rowCount()-1); QDialog::reject(); } kraft-0.97/src/taxeditdialog.h000066400000000000000000000031061410616450300163420ustar00rootroot00000000000000/*************************************************************************** taxeditdialog.h - edit tax rates ------------------- begin : Apr 9 2009 copyright : (C) 2009 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef TAXEDITDIALOG_H #define TAXEDITDIALOG_H #include #include #include #include #include "ui_taxeditbase.h" /** * @author Klaas Freitag */ // ################################################################################ class TaxEditDialog: public QDialog, protected Ui::TaxEditBase { Q_OBJECT public: TaxEditDialog( QSqlTableModel *taxModel, QWidget *parent ); public slots: void accept(); void reject(); private: Ui::TaxEditBase *mBaseWidget; QDataWidgetMapper *mapper; QSqlTableModel *model; }; #endif kraft-0.97/src/templateprovider.cpp000066400000000000000000000030621410616450300174420ustar00rootroot00000000000000/*************************************************************************** templateprovider - base class for the template provider classes. ------------------- begin : 2007-05-02 copyright : (C) 2007 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include "templateprovider.h" #include "doctext.h" #include "textselection.h" TemplateProvider::TemplateProvider( QWidget *parent ) : QObject(), mParent( parent ), mTextSelection(nullptr) { } TemplateProvider::~TemplateProvider() { } void TemplateProvider::slotSetDocType( const QString& str ) { mDocType = str; } void TemplateProvider::setSelection( TextSelection *sel ) { mTextSelection = sel; } DocText TemplateProvider::currentText() { DocText dt; if ( mTextSelection ) { return mTextSelection->currentDocText(); } return dt; } kraft-0.97/src/templateprovider.h000066400000000000000000000032071410616450300171100ustar00rootroot00000000000000/*************************************************************************** templateprovider - base class for the template provider classes. ------------------- begin : 2007-05-02 copyright : (C) 2007 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef TEMPLATEPROVIDER_H #define TEMPLATEPROVIDER_H #include class QWidget; class TextSelection; class DocText; class TemplateProvider : public QObject { Q_OBJECT public: TemplateProvider( QWidget* ); ~TemplateProvider(); virtual void setSelection( TextSelection* ); virtual DocText currentText(); public slots: virtual void slotNewTemplate() = 0; virtual void slotEditTemplate() = 0; virtual void slotDeleteTemplate() = 0; virtual void slotTemplateToDocument() = 0; void slotSetDocType( const QString& ); protected: QWidget *mParent; QString mDocType; TextSelection *mTextSelection; }; #endif kraft-0.97/src/templates/000077500000000000000000000000001410616450300153455ustar00rootroot00000000000000kraft-0.97/src/templates/cpp_template000066400000000000000000000024441410616450300177510ustar00rootroot00000000000000/*************************************************************************** |FILENAME| - ------------------- begin : |DATE| copyright : (C) |YEAR| by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef |HEADER_DEF| #define |HEADER_DEF| #ifdef HAVE_CONFIG_H #include #endif // include files for Qt // include files for KDE #include #include class |CLASSNAME| : public _what_ { Q_OBJECT public: |CLASSNAME|(); ~|CLASSNAME|(); private: } #endif /* END */ kraft-0.97/src/templates/header_template000066400000000000000000000017271410616450300204220ustar00rootroot00000000000000/*************************************************************************** |FILENAME| - ------------------- begin : |DATE| copyright : (C) |YEAR| by |AUTHOR| email : |EMAIL| ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ kraft-0.97/src/templatesaverbase.cpp000066400000000000000000000027101410616450300175620ustar00rootroot00000000000000/*************************************************************************** templatesaverbase - ------------------- begin : 2005-20-01 copyright : (C) 2005 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ // include files for Qt // include files for KDE #include #include "templatesaverbase.h" TemplateSaverBase::TemplateSaverBase( ) { } bool TemplateSaverBase::saveTemplate(FloskelTemplate*) { return false; } void TemplateSaverBase::saveTemplateChapter( FloskelTemplate* ) { // do nothing } TemplateSaverBase::~TemplateSaverBase( ) { } CalculationsSaverBase::CalculationsSaverBase() { } CalculationsSaverBase::CalculationsSaverBase( TargetType ) { } /* END */ kraft-0.97/src/templatesaverbase.h000066400000000000000000000032161410616450300172310ustar00rootroot00000000000000/*************************************************************************** templatesaverbase - Base class of a template save class ------------------- begin : 2005-20-00 copyright : (C) 2005 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef _TEMPLATESAVERBASE_H #define _TEMPLATESAVERBASE_H // include files #include /** * */ class FloskelTemplate; class dbID; class CalcPartList; class TemplateSaverBase { public: TemplateSaverBase(); virtual ~TemplateSaverBase(); virtual bool saveTemplate( FloskelTemplate* ); virtual void saveTemplateChapter( FloskelTemplate* ); private: }; class CalculationsSaverBase { public: enum TargetType { Template, Document }; CalculationsSaverBase(); virtual ~CalculationsSaverBase() { } CalculationsSaverBase( TargetType ); virtual bool saveCalculations( CalcPartList, dbID ) = 0; }; #endif /* END */ kraft-0.97/src/templatesaverdb.cpp000066400000000000000000000303051410616450300172360ustar00rootroot00000000000000/*************************************************************************** templatesaverdb - ------------------- begin : 2005-20-00 copyright : (C) 2005 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ // include files for Qt #include #include #include // include files for KDE #include #include "kraftdb.h" #include "kraftglobals.h" #include "dbids.h" #include "templatesaverdb.h" #include "calcpart.h" #include "floskeltemplate.h" #include "timecalcpart.h" #include "fixcalcpart.h" #include "materialcalcpart.h" #include "stockmaterial.h" bool CalculationsSaverDB::saveFixCalcPart( FixCalcPart *cp, dbID parentID ) { bool result = true; QSqlTableModel model; model.setTable(mTableFixCalc); int cpId = cp->getDbID().toInt(); model.setFilter("FCalcID=" + QString::number( cpId )); model.select(); // qDebug () << "CalcFix calcpart-ID is " << cpId << endl; if( cpId < 0 ) { // no db entry yet => INSERT if( !cp->isToDelete() ) { QSqlRecord buffer = model.record(); fillFixCalcBuffer( &buffer, cp ); buffer.setValue( "TemplID", parentID.toInt() ); model.insertRecord(-1, buffer); model.submitAll(); dbID id = KraftDB::self()->getLastInsertID(); // qDebug () << "Setting db-ID " << id.toString() << endl; cp->setDbID(id); } else { // qDebug () << "new element, but set to delete" << endl; } } else { if( cp->isToDelete() ) { // qDebug () << "deleting fix calc part " << cpId << endl; // delete this calcpart. if ( model.rowCount() > 0 ) { int cnt = model.rowCount(); model.removeRows(0, cnt); model.submitAll(); // qDebug () << "Amount of deleted entries: " << cnt << endl; } } else { // der Datensatz ist bereits in der Datenbank => UPDATE if( model.rowCount() > 0 ) { QSqlRecord buffer = model.record(0); buffer.setValue( "modDate", KraftDB::self()->currentTimeStamp() ); fillFixCalcBuffer(& buffer, cp ); model.setRecord(0, buffer); model.submitAll(); } else { qCritical() << "Can not select FCalcID, corrupt data!" << endl; } } } return result; } void CalculationsSaverDB::fillFixCalcBuffer( QSqlRecord *buffer, FixCalcPart *cp ) { if( ! (buffer && cp )) return; buffer->setValue( "name", cp->getName() ); buffer->setValue( "amount", cp->getMenge() ); buffer->setValue( "price", cp->unitPreis().toDouble() ); buffer->setValue( "percent", cp->getProzentPlus() ); buffer->setValue( "modDate", KraftDB::self()->currentTimeStamp() ); } bool CalculationsSaverDB::saveMaterialCalcPart( MaterialCalcPart *cp, dbID parentID ) { bool result = true; if( !cp ) return result; QSqlTableModel model; model.setTable( mTableMatCalc ); model.setEditStrategy(QSqlTableModel::OnManualSubmit); int cpId = cp->getDbID().toInt(); model.setFilter("MCalcID=" + QString::number( cpId )); model.select(); // qDebug () << "Saving material calcpart id=" << cpId << endl; if( cpId < 0 ) { // no entry in database yet, need to insert QSqlRecord buffer = model.record(); fillMatCalcBuffer( &buffer, cp ); buffer.setValue( "TemplID", parentID.toInt() ); model.insertRecord(-1, buffer); model.submitAll(); dbID id = KraftDB::self()->getLastInsertID(); cp->setDbID(id); } else { // there is an db entry, update needed if(cp->isToDelete()) { // This calcpart must be deleted if( model.rowCount() > 0) { model.removeRow(0); model.submitAll(); } } else { // don't delete, update! if( model.rowCount() > 0) { QSqlRecord buffer = model.record(0); buffer.setValue( "modDate", KraftDB::self()->currentTimeStamp() ); fillMatCalcBuffer( &buffer, cp ); model.setRecord(0, buffer); model.submitAll(); } else { qCritical() << "Can not select MCalcID, corrupt data!" << endl; } } } return result; } void CalculationsSaverDB::fillMatCalcBuffer( QSqlRecord *buffer, MaterialCalcPart *cp ) { if( !(buffer && cp)) return; buffer->setValue("materialID", cp->getMaterial()->getID()); buffer->setValue("amount", cp->getCalcAmount()); buffer->setValue("TemplID", cp->getTemplID().toInt()); buffer->setValue("percent", cp->getProzentPlus() ); } CalculationsSaverDB::CalculationsSaverDB( ) : CalculationsSaverBase(), mTableTimeCalc( "CalcTime" ), mTableFixCalc( "CalcFixed" ), mTableMatCalc( "CalcMaterials" ), mTableMatDetailCalc( "CalcMaterialDetails" ) { } CalculationsSaverDB::CalculationsSaverDB( TargetType tt ) : CalculationsSaverBase( tt ), mTableTimeCalc( "CalcTime" ), mTableFixCalc( "CalcFixed" ), mTableMatCalc( "CalcMaterials" ), mTableMatDetailCalc( "CalcMaterialDetails" ) { if ( tt == Document ) { mTableTimeCalc = "DocCalcTime"; mTableFixCalc = "DocCalcFixed"; mTableMatCalc = "DocCalcMaterials"; mTableMatDetailCalc = "DocCalcMaterialDetails"; } } bool CalculationsSaverDB::saveCalculations( CalcPartList parts, dbID parentID ) { bool res = true; CalcPartListIterator it( parts ); while( it.hasNext()) { CalcPart *cp = it.next(); if( cp->isDirty() ) { if( cp->getType() == KALKPART_TIME ) { res = saveTimeCalcPart( static_cast(cp), parentID ); Q_ASSERT( res ); } else if( cp->getType() == KALKPART_FIX ) { res = saveFixCalcPart( static_cast(cp), parentID ); Q_ASSERT( res ); } else if( cp->getType() == KALKPART_MATERIAL ) { res = saveMaterialCalcPart( static_cast(cp), parentID ); Q_ASSERT( res ); } else { // qDebug () << "ERROR: Unbekannter Kalkulations-Anteil-Typ!" << endl; } } } return res; } bool CalculationsSaverDB::saveTimeCalcPart( TimeCalcPart *cp, dbID parentId ) { bool result = true; if( !cp ) return result; int cpId = cp->getDbID().toInt(); QSqlTableModel model; model.setTable( mTableTimeCalc ); model.setFilter( "TCalcID="+QString::number(cpId) ); model.select(); // qDebug () << "Models last error: " << model.lastError() << model.rowCount(); if( cpId < 0 ) { // no entry in db yet => INSERT if( ! cp->isToDelete() ) { QSqlRecord buffer = model.record(); fillTimeCalcBuffer( &buffer, cp ); buffer.setValue( "TemplID", parentId.toInt() ); model.insertRecord(-1, buffer); dbID id = KraftDB::self()->getLastInsertID(); cp->setDbID(id); } else { // qDebug () << "delete flag is set -> skip saving." << endl; } } else { if( cp->isToDelete() ) { // delete this calcpart. if ( model.rowCount() > 0 ) { model.removeRow(0); model.submitAll(); } } else { // Update needed, record is already in the database if( model.rowCount() > 0 ) { QSqlRecord buffer = model.record(0); buffer.setValue( "modDate", KraftDB::self()->currentTimeStamp() ); fillTimeCalcBuffer( &buffer, cp ); model.setRecord(0, buffer); model.submitAll(); } else { qCritical() << "Unable to select TCalcID, corrupt data!" << endl; } } } return result; } void CalculationsSaverDB::fillTimeCalcBuffer( QSqlRecord *buffer, TimeCalcPart *cp ) { if( ! (buffer && cp )) return; buffer->setValue( "name", cp->getName() ); buffer->setValue( "minutes", cp->duration() ); buffer->setValue( "timeUnit", cp->timeUnitIndex()); buffer->setValue( "percent", cp->getProzentPlus() ); StdSatz std = cp->getStundensatz(); buffer->setValue( "stdHourSet", std.getId().toInt() ); buffer->setValue( "allowGlobal", cp->globalStdSetAllowed() ? 1 : 0 ); } /* =========================================================================== */ TemplateSaverDB::TemplateSaverDB( ) : TemplateSaverBase() { } TemplateSaverDB::~TemplateSaverDB( ) { } bool TemplateSaverDB::saveTemplate( FloskelTemplate *tmpl ) { bool res = true; // Transaktion ? QSqlTableModel model; model.setEditStrategy(QSqlTableModel::OnManualSubmit); model.setTable("Catalog"); QString templID = QString::number(tmpl->getTemplID()); model.setFilter("TemplID=" + templID); model.select(); QSqlRecord buffer; if( model.rowCount() > 0) { // qDebug () << "Updating template " << tmpl->getTemplID() << endl; // mach update buffer = model.record(0); fillTemplateBuffer( &buffer, tmpl, false ); buffer.setValue( "modifyDatum", KraftDB::self()->currentTimeStamp() ); model.setRecord(0, buffer); model.submitAll(); } else { // insert // qDebug () << "Creating new database entry" << endl; buffer = model.record(); fillTemplateBuffer( &buffer, tmpl, true ); model.insertRecord(-1, buffer); model.submitAll(); /* Jetzt die neue Template-ID selecten */ dbID id = KraftDB::self()->getLastInsertID(); // qDebug () << "New Database ID=" << id.toInt() << endl; if( id.isOk() ) { tmpl->setTemplID(id.toInt() ); templID = id.toString(); } else { // qDebug () << "ERROR: Kann AUTOINC nicht ermitteln" << endl; res = false; } } if( res ) { /* Nun die einzelnen Calcparts speichern */ CalcPartList parts = tmpl->getCalcPartsList(); CalculationsSaverDB calculationSaver; res = calculationSaver.saveCalculations( parts, tmpl->getTemplID() ); } return res; } void TemplateSaverDB::fillTemplateBuffer( QSqlRecord *buffer, FloskelTemplate *tmpl, bool isNew ) { buffer->setValue( "chapterID", tmpl->chapterId().toInt() ); buffer->setValue( "unitID", tmpl->unit().id()); buffer->setValue( "Floskel", tmpl->getText() ); buffer->setValue( "Gewinn", tmpl->getBenefit() ); buffer->setValue( "zeitbeitrag", tmpl->hasTimeslice() ); QDateTime dt = QDateTime::currentDateTime(); QString dtString = KraftDB::self()->currentTimeStamp(dt); if( isNew ) { buffer->setValue( "enterDatum", dtString); tmpl->setEnterDate( dt ); } buffer->setValue("modifyDatum", dtString ); tmpl->setModifyDate( dt ); int ctype = 2; // Calculation type Calculation if( tmpl->calcKind() == CatalogTemplate::ManualPrice ) { ctype = 1; } buffer->setValue( "Preisart", ctype ); buffer->setValue( "EPreis", tmpl->manualPrice().toDouble() ); } void TemplateSaverDB::saveTemplateChapter( FloskelTemplate* tmpl ) { if( tmpl ) { dbID id = tmpl->getTemplID(); dbID chapId = tmpl->chapterId(); QSqlQuery qUpdate; // qDebug () << "Updating Chapter to chapter id " << chapId.toInt() << " of id " << id.toString(); QString sql = "UPDATE Catalog SET chapterID=:chap WHERE TemplID=:id"; qUpdate.prepare( sql ); qUpdate.bindValue( ":chap", chapId.toInt() ); qUpdate.bindValue( ":id", id.toInt() ); qUpdate.exec(); // qDebug () << "setting template chapter sql: " << qUpdate.lastError().text(); } } /* END */ kraft-0.97/src/templatesaverdb.h000066400000000000000000000043611410616450300167060ustar00rootroot00000000000000/*************************************************************************** templatesaverdb - ------------------- begin : 2005-20-00 copyright : (C) 2005 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef _TEMPLATESAVERDB_H #define _TEMPLATESAVERDB_H #include "templatesaverbase.h" /** * */ class FloskelTemplate; class QSqlRecord; class QString; class TimeCalcPart; class FixCalcPart; class MaterialCalcPart; class StockMaterial; class TemplateSaverDB : public TemplateSaverBase { public: TemplateSaverDB(); virtual ~TemplateSaverDB(); virtual bool saveTemplate( FloskelTemplate* ); virtual void saveTemplateChapter( FloskelTemplate* ); private: void fillTemplateBuffer( QSqlRecord*, FloskelTemplate*, bool ); QString sqlWhereFromRecord( QSqlRecord * ) const; }; class CalculationsSaverDB:public CalculationsSaverBase { public: CalculationsSaverDB(); CalculationsSaverDB( TargetType tt ); virtual ~CalculationsSaverDB() { } bool saveCalculations( CalcPartList, dbID ); private: bool saveFixCalcPart( FixCalcPart *cp, dbID ); bool saveMaterialCalcPart( MaterialCalcPart *cp, dbID ); bool saveTimeCalcPart( TimeCalcPart*, dbID ); void fillFixCalcBuffer( QSqlRecord *buffer, FixCalcPart *cp ); void fillMatCalcBuffer( QSqlRecord *buffer, MaterialCalcPart *cp ); void fillTimeCalcBuffer( QSqlRecord*, TimeCalcPart* ); QString mTableTimeCalc; QString mTableFixCalc; QString mTableMatCalc; QString mTableMatDetailCalc; }; #endif /* END */ kraft-0.97/src/templkatalog.cpp000066400000000000000000000301251410616450300165400ustar00rootroot00000000000000/*************************************************************************** flostempllist.cpp - ------------------- begin : Son Feb 8 2004 copyright : (C) 2004 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include "floskeltemplate.h" #include "dbids.h" #include "templkatalog.h" #include "kraftdb.h" #include "unitmanager.h" #include "timecalcpart.h" #include "fixcalcpart.h" #include "materialcalcpart.h" #include "geld.h" #include "katalog.h" /** constructor of a katalog, which is only a list of Floskel templates. * A name must be given, which is displayed for the root element in the * */ TemplKatalog::TemplKatalog( const QString& name ) : Katalog( name ) { } TemplKatalog::~TemplKatalog() { } void TemplKatalog::reload( dbID id) { FloskelTemplate *templ=0; //Find the template we want to reload in the templatelist for(int i=0; i < m_flosList.count(); ++i) { templ = m_flosList[i]; if(templ->getTemplID() == id.toInt()) break; } if(templ) { QSqlQuery q("SELECT unitID, TemplID, chapterID, Preisart, EPreis, modifyDatum, enterDatum, Floskel, Gewinn, zeitbeitrag FROM Catalog WHERE TemplID=:TemplID"); q.bindValue(":TemplID", id.toInt()); q.exec(); if(q.next()) { //templ->setEinheitId(q.value(0).toInt()); // qDebug() << "Reloading template number " << q.value(1) << endl; templ->setChapterId(dbID( q.value(2).toInt()), false ); //templ->setCalculationType(q.value(3).toInt()); templ->setManualPrice(q.value(4).toDouble()); templ->setText( q.value(7).toString() ); templ->setBenefit( q.value(8).toDouble()); templ->setHasTimeslice( q.value(9).toBool() ); templ->clearCalcParts(); loadCalcParts( templ ); } } } int TemplKatalog::load() { Katalog::load(); int cnt = 0; QString chapIdList; bool firstOne = true; foreach( CatalogChapter chap, mChapters ) { if( !firstOne ) { chapIdList += ","; } else { firstOne = false; } chapIdList += chap.id().toString(); } // qDebug () << "The chapterIdList: " << chapIdList; QSqlQuery q("SELECT unitID, TemplID, chapterID, Preisart, EPreis, modifyDatum, enterDatum, " "Floskel, Gewinn, zeitbeitrag, lastUsed, useCounter FROM Catalog WHERE chapterID IN( " + chapIdList + ") " "ORDER BY chapterID, sortKey" ); q.exec(); m_flosList.clear(); while ( q.next() ) { cnt++; int einheit = q.value(0).toInt(); int templID = q.value(1).toInt(); // qDebug () << "Loading template number " << templID << endl; int chapID = q.value(2).toInt(); // int sortID = cur.value( "sortKey" ).toInt(); int calcKind = q.value(3).toInt(); double g = q.value(4).toDouble(); Geld preis(g); /* Only for debugging: */ if( templID == 272 ) { // qDebug () << "Geld ist " << preis.toString( *( &mLocale ) ) << " from g-value " << g << endl; } QDateTime modDt = q.value(5).toDateTime(); QDateTime enterDt = q.value(6).toDateTime(); // qDebug() << "Chapter ID is " << chapID << endl; FloskelTemplate *flos = new FloskelTemplate( templID, q.value(7).toString(), einheit, chapID, calcKind ); flos->setEnterDate( enterDt ); flos->setModifyDate( modDt ); // flos->setSortKey( sortID ); flos->setBenefit( q.value(8).toDouble()); flos->setManualPrice( preis ); bool tslice = q.value(9).toInt() > 0; flos->setHasTimeslice( tslice ); flos->setLastUsedDate( q.value(10).toDateTime() ); flos->setUseCounter( q.value(11).toInt() ); loadCalcParts( flos ); m_flosList.append(flos); } return cnt; } void TemplKatalog::recordUsage(int id) { QSqlTableModel model; model.setEditStrategy(QSqlTableModel::OnManualSubmit); model.setTable("Catalog"); model.setFilter(QString("TemplID=%1").arg(id)); model.select(); if( model.rowCount() > 0) { // qDebug () << "Updating template " << tmpl->getTemplID() << endl; bool ok; QSqlRecord buffer = model.record(0); int currCnt = buffer.value("useCounter").toInt(&ok); int newCnt = 0; if (ok) { newCnt = currCnt+1; } // mach update buffer.setValue( "useCounter", newCnt); buffer.setValue( "lastUsed", KraftDB::self()->currentTimeStamp() ); model.setRecord(0, buffer); model.submitAll(); } } int TemplKatalog::addNewTemplate( FloskelTemplate *tmpl ) { int re = -1; if ( tmpl ) { m_flosList.append( tmpl ); re = m_flosList.count(); } return re; } void TemplKatalog::deleteTemplate( int id ) { // Remove entry from the m_flosList FloskelTemplateListIterator it(m_flosList); FloskelTemplate *tmpl; int cnt = 0; while( it.hasNext() ) { tmpl = it.next(); if( tmpl->getTemplID() == id ) { break; } cnt++; } if( cnt < m_flosList.size()) { m_flosList.removeAt( cnt ); } QStringList tables; tables << "Catalog" << "CalcFixed" << "CalcMaterials" << "CalcTime"; for(const QString& table : tables ) { QSqlQuery q; q.prepare( "DELETE FROM " + table + " WHERE TemplID=:Id"); q.bindValue( ":Id", id ); q.exec(); // qDebug () << "SQL Delete Success: " << q.lastError().text(); } } int TemplKatalog::loadCalcParts( FloskelTemplate *flos ) { int cnt = 0; cnt = loadTimeCalcParts( flos ); cnt += loadFixCalcParts( flos ); cnt += loadMaterialCalcParts(flos); return cnt; } int TemplKatalog::loadTimeCalcParts( FloskelTemplate *flos ) { if( ! flos ) return(0); int cnt = 0; QSqlQuery q; q.prepare("SELECT TCalcID, TemplID, name, minutes, percent, stdHourSet, allowGlobal, timeUnit" " FROM CalcTime WHERE TemplID=:TemplID"); q.bindValue(":TemplID", QString::number( flos->getTemplID())); q.exec(); while( q.next() ) { cnt++; int tcalcid = q.value(0).toInt(); int templid = q.value(1).toInt(); const QString name = q.value(2).toString(); int minutes = q.value(3).toInt(); int prozent = q.value(4).toInt(); int hourSet = q.value(5).toInt(); bool globAllowed = q.value(6).toInt() > 0; int timeUnit = q.value(7).toInt(); TimeCalcPart::TimeUnit unit = TimeCalcPart::timeUnitFromInt(timeUnit); TimeCalcPart *zcp = new TimeCalcPart( name, minutes, unit, prozent ); zcp->setGlobalStdSetAllowed( globAllowed ); zcp->setStundensatz( StdSatzMan::self()->getStdSatz(hourSet) ); zcp->setDbID( dbID(tcalcid)); zcp->setTemplID( dbID(templid)); zcp->setDirty( false ); flos->addCalcPart( zcp ); } return cnt; } int TemplKatalog::loadMaterialCalcParts( FloskelTemplate *flos ) { if( ! flos ) return(0); int cnt = 0; QSqlQuery q; q.prepare("SELECT MCalcID, TemplID, materialID, percent, amount FROM CalcMaterials WHERE TemplID=:TemplID"); q.bindValue(":TemplID", QString::number( flos->getTemplID())); q.exec(); while( q.next() ) { cnt++; long mcalcID = q.value(0).toLongLong(); int templid = q.value(1).toInt(); long matID = q.value(2).toLongLong(); int procent = q.value(3).toInt(); double amount = q.value(4).toDouble(); MaterialCalcPart *mPart = new MaterialCalcPart( mcalcID, matID, procent, amount ); mPart->setDbID( dbID(mcalcID)); mPart->setTemplID( dbID(templid)); mPart->setDirty( false ); flos->addCalcPart( mPart ); } return cnt; } int TemplKatalog::loadFixCalcParts( FloskelTemplate *flos ) { if( ! flos ) return(0); int cnt = 0; QSqlQuery q; q.prepare("SELECT name, amount, percent, FCalcID, TemplID, price FROM CalcFixed WHERE TemplID=:TemplID"); q.bindValue(":TemplID", QString::number( flos->getTemplID())); q.exec(); while( q.next() ) { cnt++; QString name = q.value(0).toString(); double amount = q.value(1).toDouble(); int percent = q.value(2).toInt(); int tcalcid = q.value(3).toInt(); int templid = q.value(4).toInt(); double g = q.value(5).toDouble(); Geld price(g); // = (int) g; // FIXME: proper handling of money here. FixCalcPart *fcp = new FixCalcPart( name, price, percent ); fcp->setMenge( amount ); fcp->setDbID( dbID(tcalcid)); fcp->setTemplID( dbID(templid)); fcp->setDirty( false ); flos->addCalcPart( fcp ); } return cnt; } FloskelTemplateList TemplKatalog::getFlosTemplates( const CatalogChapter& chapter ) { FloskelTemplateList resultList; int chap = chapter.id().toInt(); if( m_flosList.count() == 0 ) { // qDebug () << "Empty katalog list - loading!" << endl; load(); } FloskelTemplateListIterator it(m_flosList); FloskelTemplate *tmpl; while( it.hasNext() ) { tmpl = it.next(); int haveChap = tmpl->chapterId().toInt(); // qDebug() << "Searching for chapter " << chapter << " with ID " << chap << " and have " << haveChap << endl; if( haveChap == chap ) { resultList.append( tmpl ); } } return resultList; } int TemplKatalog::load( const QString& /* chapter */ ) { return 0; } void TemplKatalog::writeXMLFile() { QString filename = QFileDialog::getSaveFileName(0, QString(), QDir::homePath(), QString()); // "*.xml", 0, i18n("Export XML Katalog")); if(filename.isEmpty()) return; QDomDocument doc = toXML(); QFile file( filename ); if( file.open( QIODevice::WriteOnly ) ) { QTextStream ts( &file ); ts << doc.toString(); file.close(); } } QDomDocument TemplKatalog::toXML() { QDomDocument doc("catalog"); QDomElement root = doc.createElement("catalog"); doc.appendChild(root); QDomElement elem = doc.createElement("catalogname"); QDomText text = doc.createTextNode(m_name); elem.appendChild(text); root.appendChild(elem); QStringList allSets = StdSatzMan::self()->allStdSaetze(); for ( QStringList::Iterator it = allSets.begin(); it != allSets.end(); ++it ) { QDomElement set = doc.createElement("hourset"); QDomElement elem = doc.createElement("name"); QDomText tname = doc.createTextNode(*it); elem.appendChild(tname); set.appendChild(elem); QDomElement rateelem = doc.createElement("rate"); StdSatz satz = StdSatzMan::self()->getStdSatz(*it); Geld g = satz.getPreis(); QDomText rname = doc.createTextNode(g.toString()); rateelem.appendChild(rname); set.appendChild(rateelem); root.appendChild(set); } QList chaps = getKatalogChapters(); foreach( CatalogChapter theChapter, chaps ) { QString chapter = theChapter.name(); QDomElement chapElem = doc.createElement("chapter"); QDomElement chapName = doc.createElement("chaptername"); text = doc.createTextNode(chapter); chapName.appendChild(text); chapElem.appendChild(chapName); root.appendChild(chapElem); FloskelTemplateList templs = getFlosTemplates(theChapter); FloskelTemplateListIterator it(templs); // FIXME: XML export! } return doc; } int TemplKatalog::getEntriesPerChapter( const CatalogChapter& chapter) { int cnt = 0; QString q( QString("SELECT count(*) FROM katalog WHERE chapterID=%1" ).arg( chapter.id().toInt() ) ); QSqlQuery query( q ); while ( query.next() ) { cnt = query.value(0).toInt(); } return cnt; } kraft-0.97/src/templkatalog.h000066400000000000000000000041711410616450300162070ustar00rootroot00000000000000/*************************************************************************** katalog.h - ------------------- begin : Son Feb 8 2004 copyright : (C) 2004 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef TEMPLKATALOG_H #define TEMPLKATALOG_H #include #include "floskeltemplate.h" #include "katalog.h" #include "dbids.h" /** *@author Klaas Freitag */ class MaterialCalcPart; class QDomDocument; class TemplKatalog : public Katalog { public: TemplKatalog(const QString& name); ~TemplKatalog(); int load(const QString&); int load() override; void reload( dbID ) override; /** No descriptions */ FloskelTemplateList getFlosTemplates( const CatalogChapter& chapter ); KatalogType type() override { return TemplateCatalog; } QDomDocument toXML() override; /** get the amount of entries in a chapter or the entire catalog */ int getEntriesPerChapter( const CatalogChapter& ) override; int addNewTemplate( FloskelTemplate *tmpl ); void recordUsage(int id) override; public slots: void writeXMLFile() override; void deleteTemplate( int ); private: int loadCalcParts( FloskelTemplate* ); int loadTimeCalcParts( FloskelTemplate* ); int loadFixCalcParts( FloskelTemplate* ); int loadMaterialCalcParts( FloskelTemplate * ); FloskelTemplateList m_flosList; }; #endif kraft-0.97/src/templkataloglistview.cpp000066400000000000000000000204511410616450300203300ustar00rootroot00000000000000 /*************************************************************************** templkataloglistview - template katalog listview. ------------------- begin : 2005-07-09 copyright : (C) 2005 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include #include #include #include #include #include #include "templkataloglistview.h" #include "portal.h" #include "kraftglobals.h" #include "katalog.h" #include "katalogman.h" #include "kataloglistview.h" #include "materialcalcpart.h" #include "stockmaterial.h" #include "templkatalog.h" #include "timecalcpart.h" #include "docposition.h" #include "defaultprovider.h" #include "kraftsettings.h" TemplKatalogListView::TemplKatalogListView(QWidget *w) : KatalogListView(w), mShowCalcParts( true ) { QStringList labels; labels << i18n("Template"); labels << i18n("Price"); labels << i18n("Calc. Type"); setHeaderLabels(labels); QByteArray headerState = QByteArray::fromBase64( KraftSettings::self()->templateCatViewHeader().toAscii() ); header()->restoreState(headerState); contextMenu()->setTitle( i18n("Template Catalog")); } /* * This class adds a complete catalog and fills the view. It gets the * catalog from KatalogMan, iterates over the catalog chapters and * fills in the templates. */ void TemplKatalogListView::addCatalogDisplay( const QString& katName ) { KatalogListView::addCatalogDisplay(katName); TemplKatalog* catalog = static_cast(KatalogMan::self()->getKatalog(katName)); if ( !catalog ) { qCritical() << "Could not load catalog " << katName << endl; return; } setupChapters(); const QList chapters = catalog->getKatalogChapters(); foreach( CatalogChapter chap, chapters ) { if( mChapterDict.contains( chap.id().toInt() ) ) { QTreeWidgetItem *katItem = mChapterDict[chap.id().toInt()]; FloskelTemplateList katList = catalog->getFlosTemplates(chap); FloskelTemplateListIterator flosIt( katList ); while( flosIt.hasNext() ) { FloskelTemplate *tmpl = flosIt.next(); /* create a ew item as the child of katalog entry */ addFlosTemplate( katItem, tmpl ); if ( mShowCalcParts ) addCalcParts( tmpl ); } } } } /* * add a single template to the view with setting icon etc. */ QTreeWidgetItem* TemplKatalogListView::addFlosTemplate( QTreeWidgetItem *parentItem, FloskelTemplate *tmpl ) { if( ! parentItem ) parentItem = m_root; QTreeWidgetItem *listItem = new QTreeWidgetItem( parentItem ); slFreshupItem( listItem, tmpl); tmpl->setListViewItem( listItem ); if( tmpl->calcKind() == CatalogTemplate::ManualPrice ) { listItem->setIcon(0, QIcon::fromTheme( "kraftdice" ) ); } else { listItem->setIcon(0, QIcon::fromTheme("accessories-calculator")); } if ( mCheckboxes ) { listItem->setCheckState(0, Qt::Unchecked); } // store the connection between the listviewitem and the template in a dict. m_dataDict.insert( listItem, tmpl ); return listItem; } void TemplKatalogListView::slFreshupItem( QTreeWidgetItem *item, FloskelTemplate *tmpl, bool remChildren ) { if( !(item && tmpl) ) return; Geld g = tmpl->unitPrice(); const QString ck = tmpl->calcKindString(); const QString t = Portal::textWrap(tmpl->getText(), 72, 4); item->setText( 0, t ); if( t.endsWith(QLatin1Literal("..."))) { item->setToolTip(0, Portal::textWrap(tmpl->getText(), 72, 22)); } QString h; h = QString( "%1 / %2" ).arg( g.toString() ) .arg( tmpl->unit().einheitSingular() ); item->setText( 1, h ); item->setText( 2, ck ); // item->setText( 4, QString::number(tmpl->getTemplID())); if( remChildren ) { /* remove all children and insert them again afterwards. * That updates the view */ for( int i = 0; i < item->childCount(); i++ ) { QTreeWidgetItem *it = item->child(i); if( it ) { item->removeChild( it ); delete it; } } addCalcParts(tmpl); // Insert to update the view again. } } void TemplKatalogListView::addCalcParts( FloskelTemplate *tmpl ) { QTreeWidgetItem *item = tmpl->getListViewItem(); if( ! item ) return; CalcPartList parts = tmpl->getCalcPartsList(); CalcPartListIterator it(parts); while( it.hasNext() ) { CalcPart *cp = it.next(); QString title = cp->getName(); QString type = cp->getType(); // qDebug () << "Type is " << type << endl; if( type == KALKPART_TIME ) { TimeCalcPart *zcp = static_cast(cp); StdSatz stdsatz = zcp->getStundensatz(); title = QString( "%1, %2 %3 %4" ) .arg( cp->getName() ) .arg( QString::number(zcp->duration())) .arg( TimeCalcPart::timeUnitString(zcp->timeUnit())) .arg( stdsatz.getName() ); } QStringList list; list << title; list << cp->basisKosten().toString(); list << cp->getType(); QTreeWidgetItem *cpItem = new QTreeWidgetItem( item, list ); cpItem->setDisabled(true); } } void TemplKatalogListView::setShowCalcParts( bool on ) { mShowCalcParts = on; } bool TemplKatalogListView::showCalcParts() { return mShowCalcParts; } TemplKatalogListView::~TemplKatalogListView() { } DocPosition TemplKatalogListView::itemToDocPosition( QTreeWidgetItem *it ) { DocPosition pos; if ( ! it ) { it = currentItem(); } if ( ! it ) return pos; FloskelTemplate *flos = static_cast( m_dataDict[ it ] ); if ( flos ) { pos.setText( flos->getText() ); pos.setUnit( flos->unit() ); pos.setUnitPrice( flos->unitPrice() ); } else { // qDebug () << "Can not find a template for the item" << endl; } return pos; } CalcPartList TemplKatalogListView::itemsCalcParts( QTreeWidgetItem* it ) { CalcPartList cpList; if ( ! it ) { it = currentItem(); } if ( ! it ) return cpList; FloskelTemplate *flos = static_cast( m_dataDict[ it ] ); if ( flos ) { // qDebug () << "We have calc parts: " << flos->getCalcPartsList().count()<< endl; cpList = flos->getCalcPartsList(); } return cpList; } // Updates the sequence of items below a parent item stored in the inherited // variable mSortChapterItem void TemplKatalogListView::slotUpdateSequence() { if( ! mSortChapterItem ) { return; } dbID parentID; CatalogChapter *parentChap = static_cast(itemData( mSortChapterItem ) ); if( parentChap ) { parentID = parentChap->id(); } else { parentID = 0; // root item } int childCount = mSortChapterItem->childCount(); if( ! childCount ) return; emit sequenceUpdateMaximum( childCount-1 ); QSqlQuery query; query.prepare("UPDATE Catalog SET sortKey=? WHERE TemplID=?"); int sequenceCnt = 1; // Start at 1 for( int i = 0; i < childCount; i++ ) { QTreeWidgetItem *item = mSortChapterItem->child( i ); emit sequenceUpdateProgress( i ); // set the sortKey to the sequence counter i if( ! (isChapter( item ) /* || isRoot( item ) */ ) ) { FloskelTemplate *flos = static_cast( itemData(item) ); // qDebug () << "Updating item " << flos->getTemplID() << " to sort key " << sequenceCnt; if( flos ) { query.bindValue( 0, sequenceCnt++ ); query.bindValue( 1, flos->getTemplID() ); query.exec(); } } } KatalogListView::slotUpdateSequence(); } void TemplKatalogListView::saveState() { const QByteArray state = this->header()->saveState(); KraftSettings::self()->setTemplateCatViewHeader(state.toBase64()); KraftSettings::self()->save(); } kraft-0.97/src/templkataloglistview.h000066400000000000000000000036661410616450300200060ustar00rootroot00000000000000/*************************************************************************** templkataloglistview - template katalog listview. ------------------- begin : 2005-07-09 copyright : (C) 2005 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef TEMPLKATALOGLISTVIEW_H #define TEMPLKATALOGLISTVIEW_H #include #include "floskeltemplate.h" /** @author Klaas Freitag */ class DocPosition; class TemplKatalogListView : public KatalogListView { Q_OBJECT public: TemplKatalogListView(QWidget*); ~TemplKatalogListView(); FloskelTemplate *currentTemplate(); /* create a listview entry for a floskel template */ QTreeWidgetItem *addFlosTemplate( QTreeWidgetItem*, FloskelTemplate* ); void addCatalogDisplay( const QString&); void setShowCalcParts( bool ); bool showCalcParts(); DocPosition itemToDocPosition( QTreeWidgetItem* it = 0 ); CalcPartList itemsCalcParts( QTreeWidgetItem* it = 0 ); public slots: void slFreshupItem( QTreeWidgetItem*, FloskelTemplate*, bool remChildren = false ); void saveState(); protected slots: void slotUpdateSequence(); private: bool mShowCalcParts; void addCalcParts( FloskelTemplate* ); }; #endif kraft-0.97/src/templkatalogview.cpp000066400000000000000000000205251410616450300174360ustar00rootroot00000000000000/*************************************************************************** templkatalogview.cpp ------------------- begin : 2005-07-09 copyright : (C) 2005 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ // include files for QT #include #include #include #include #include // application specific includes #include "katalogview.h" #include "templkatalogview.h" #include "floskeltemplate.h" #include "kataloglistview.h" #include "flostempldialog.h" #include "templkatalog.h" #include "templkataloglistview.h" #include "katalogman.h" #include "documentman.h" #include "kraftsettings.h" #define ID_STATUS_MSG 1 TemplKatalogView::TemplKatalogView(QWidget* parent, const char* name) : KatalogView(parent, name), m_flosDialog(nullptr), m_listview(nullptr) { } TemplKatalogView::~TemplKatalogView() { slotSaveState(); delete m_flosDialog; } Katalog* TemplKatalogView::getKatalog( const QString& name ) { Katalog *k = KatalogMan::self()->getKatalog( name ); if( ! k ) { k = new TemplKatalog( name ); KatalogMan::self()->registerKatalog( k ); } return k; } ///////////////////////////////////////////////////////////////////// // SLOT IMPLEMENTATION ///////////////////////////////////////////////////////////////////// void TemplKatalogView::slEditTemplate() { TemplKatalogListView* listview = static_cast(getListView()); if( listview ) { QTreeWidgetItem *item = listview->currentItem(); if( listview->isChapter(item) ) { // check if the chapter is empty. If so, switch to slNewTempalte() // if there others, open the chapter. if( !listview->isRoot( item ) && item->childCount() == 0 ) { slNewTemplate(); } else { // do nothing. } } else { // the clicked item is not a chapter FloskelTemplate *currTempl = static_cast (listview->currentItemData()); if( currTempl ) { QTreeWidgetItem *item = (QTreeWidgetItem*) listview->currentItem(); openDialog( item, currTempl, false ); } } } } void TemplKatalogView::slNewTemplate() { KatalogListView *listView = getListView(); if( !listView ) return; // create new template object FloskelTemplate *flosTempl = new FloskelTemplate(); flosTempl->setText( i18n( "" ) ); // find the corresponding parent (==chapter) item QTreeWidgetItem *parentItem = static_cast(listView->currentItem()); if( parentItem ) { // if it is not a chapter nor root, take the parent if( ! (listView->isRoot(parentItem) || listView->isChapter(parentItem)) ) { parentItem = (QTreeWidgetItem*) parentItem->parent(); } } if( parentItem ) { // try to find out which catalog is open/current CatalogChapter *chap = static_cast( listView->itemData( parentItem ) ); if( chap ) { flosTempl->setChapterId( chap->id().toInt(), true ); } } TemplKatalogListView *templListView = static_cast(listView); QTreeWidgetItem *item = templListView->addFlosTemplate(parentItem, flosTempl); listView->scrollToItem( item ); listView->setCurrentItem( item ); openDialog( item, flosTempl, true ); } void TemplKatalogView::slDeleteTemplate() { // qDebug () << "delete template hit"; TemplKatalogListView* listview = static_cast(getListView()); if( listview ) { FloskelTemplate *currTempl = static_cast (listview->currentItemData()); if( currTempl ) { int id = currTempl->getTemplID(); QMessageBox msgBox; msgBox.setText(i18n( "Do you really want to delete the template from the catalog?" )); msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); msgBox.setDefaultButton(QMessageBox::Ok); int result = msgBox.exec(); if( result == QMessageBox::Ok) { // qDebug () << "Delete item with id " << id; TemplKatalog *k = static_cast( getKatalog( m_katalogName ) ); if( k ) { k->deleteTemplate( id ); listview->removeTemplateItem( listview->currentItem()); } } } } } bool TemplKatalogView::currentItemToDocPosition( DocPosition& pos ) { TemplKatalogListView* listview = static_cast(getListView()); bool res = false; if( listview ) { FloskelTemplate *currTempl = static_cast (listview->currentItemData()); if( currTempl ) { // create a new position and offer it to the document manager pos.setText( currTempl->getText() ); pos.setUnit( currTempl->unit() ); pos.setUnitPrice( currTempl->unitPrice() ); pos.setAmount( 1.0 ); res = true; } } return res; } CalcPartList TemplKatalogView::currentItemsCalcParts() { TemplKatalogListView* listview = static_cast(getListView()); CalcPartList cpList; if( listview ) { FloskelTemplate *currTempl = static_cast (listview->currentItemData()); if ( currTempl ) { cpList = currTempl->getCalcPartsList(); } } return cpList; } void TemplKatalogView::openDialog( QTreeWidgetItem *listitem, FloskelTemplate *tmpl, bool isNew ) { if( ! m_flosDialog ) { m_flosDialog = new FlosTemplDialog(this, false); connect( m_flosDialog, SIGNAL(editAccepted( FloskelTemplate* )), this, SLOT( slEditOk(FloskelTemplate*))); connect( m_flosDialog, SIGNAL(editRejected( )), this, SLOT( slEditRejected())); } m_flosDialog->setTemplate( tmpl, m_katalogName, isNew ); m_editListViewItem = listitem; m_flosDialog->refreshPrices(); m_flosDialog->show(); } void TemplKatalogView::slEditOk(FloskelTemplate* templ) { // the dialog saves the template in its accept-slot. KatalogListView *listview = getListView(); if( !listview ) return; TemplKatalogListView *templListView = static_cast(listview); if(m_flosDialog ){ if ( m_flosDialog->templateIsNew() ) { TemplKatalog *k = static_cast( getKatalog( m_katalogName ) ); if ( k ) k->addNewTemplate( templ ); } } if( templListView && m_editListViewItem ) { // qDebug () << "Edit was ok, refreshing item in list " << m_editListViewItem << endl; templListView->setCurrentItem( m_editListViewItem ); templListView->slFreshupItem( m_editListViewItem, templ, true ); templListView->scrollToItem( m_editListViewItem ); } m_editListViewItem = nullptr; } void TemplKatalogView::slEditRejected() { // qDebug () << "Rejecting Edit!"; if ( m_editListViewItem ) { delete m_editListViewItem; m_editListViewItem = nullptr; } } void TemplKatalogView::createCentralWidget(QBoxLayout*box, QWidget *w) { // qDebug () << "Creating new Listview" << endl; m_listview = new TemplKatalogListView( w ); box->addWidget(m_listview); KatalogView::createCentralWidget( box, w ); } void TemplKatalogView::saveWindowState( const QByteArray& arr ) { KraftSettings::self()->setTemplateCatViewState(arr); } QByteArray TemplKatalogView::windowState() { const QByteArray re = QByteArray::fromBase64( KraftSettings::self()->templateCatViewState().toAscii() ); return re; } void TemplKatalogView::saveWindowGeo( const QByteArray& arr ) { KraftSettings::self()->setTemplateCatViewGeo( QString::fromAscii(arr) ); } QByteArray TemplKatalogView::windowGeo() { const QByteArray re = QByteArray::fromBase64( KraftSettings::self()->templateCatViewGeo().toAscii() ); return re; } kraft-0.97/src/templkatalogview.h000066400000000000000000000052141410616450300171010ustar00rootroot00000000000000/*************************************************************************** templkatalogview.h ------------------- begin : 2005-07-09 copyright : (C) 2005 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef TEMPLKATALOGVIEW_H #define TEMPLKATALOGVIEW_H // include files for Qt #include #include "katalogview.h" #include "templkataloglistview.h" class TemplKatalog; class KatalogListView; class FloskelTemplate; class FlosTemplDialog; class QBoxLayout; /** * The base class for Kraft katalog view. * @author Klaas Freitag * @version $Id$ */ class TemplKatalogView: public KatalogView { Q_OBJECT public: /** construtor of KraftApp, calls all init functions to create the application. */ TemplKatalogView(QWidget* parent=0, const char* name=0); TemplKatalogView(const QString& katToShow, QWidget* parent=0, const char* name=0); ~TemplKatalogView(); // virtual KatalogListView *createListView(QWidget*); /** opens a file specified by commandline option */ void createCentralWidget(QBoxLayout*, QWidget*); KatalogListView* getListView(){return m_listview;} protected: Katalog* getKatalog( const QString& ); bool currentItemToDocPosition( DocPosition& ); CalcPartList currentItemsCalcParts(); void saveWindowState( const QByteArray& arr ); QByteArray windowState(); void saveWindowGeo( const QByteArray& arr ); QByteArray windowGeo(); public slots: /* Editing of templates -> open edit dialog */ void slEditTemplate(); void slNewTemplate(); void slDeleteTemplate(); /* selected Ok in the template editor */ void slEditOk(FloskelTemplate*); void slEditRejected(); private: // opens the edit dialog. void openDialog( QTreeWidgetItem*, FloskelTemplate *, bool ); // editing dialog for templates FlosTemplDialog *m_flosDialog; TemplKatalogListView *m_listview; }; #endif // TEMPLKATALOGVIEW_H kraft-0.97/src/templtopositiondialogbase.cpp000066400000000000000000000046011410616450300213400ustar00rootroot00000000000000/*************************************************************************** templtopositiondialogbase.cpp - base dialog template to doc ------------------- begin : Mar 2007 copyright : (C) 2007 Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include #include #include #include #include #include #include #include "templtopositiondialogbase.h" #include "docposition.h" TemplToPositionDialogBase::TemplToPositionDialogBase( QWidget *w ) : QDialog( w ) { setObjectName( "TEMPL_DIALOG" ); setWindowTitle( i18n("Create Item from Template" ) ); setModal( true ); } TemplToPositionDialogBase::~TemplToPositionDialogBase() { } void TemplToPositionDialogBase::setPositionList( DocPositionList list, int intendedPos ) { if ( ! getPositionCombo() ) { qCritical() << "Can not get a ptr to the position combo" << endl; return; } QStringList strList; strList << i18n( "the Header of the Document as first item" ); DocPositionListIterator it( list ); while( it.hasNext() ) { DocPosition *dp = static_cast( it.next() ); QString h = QString( "%1. %2" ).arg( list.posNumber( dp ) ).arg( dp->text() ); if ( h.length() > 50 ) { h = h.left( 50 ); h += i18n( "..." ); } strList.append( h ); } getPositionCombo()->insertItems( -1, strList ); getPositionCombo()->setCurrentIndex( intendedPos ); } int TemplToPositionDialogBase::insertAfterPosition() { int itemPos = getPositionCombo()->currentIndex(); // qDebug () << "Current item selected: " << itemPos << endl; return itemPos; } kraft-0.97/src/templtopositiondialogbase.h000066400000000000000000000036171410616450300210130ustar00rootroot00000000000000/*************************************************************************** templtopositiondialogbase.h - base dialog template to doc ------------------- begin : Mar 2007 copyright : (C) 2007 Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef TEMPLTOPOSITIONDIALOGBASE #define TEMPLTOPOSITIONDIALOGBASE #include #include class QWidget; class DocPosition; class DocPositionList; class QComboBox; class TemplToPositionDialogBase: public QDialog { Q_OBJECT public: TemplToPositionDialogBase( QWidget* ); ~TemplToPositionDialogBase( ); virtual void setDocPosition( DocPosition*, bool, bool ) = 0; virtual void setCatalogChapters( const QList&, const QString& ) = 0; virtual QString chapter() const = 0; void setPositionList( DocPositionList, int ); int insertAfterPosition(); virtual DocPosition docPosition() = 0; protected: /** * Needs to be reimplemented to return a pointer to a * combobox which can be filled with the current list * of positions to let the user select where the new * pos should go to. */ virtual QComboBox* getPositionCombo() = 0; }; #endif kraft-0.97/src/texteditbase.ui000066400000000000000000000154211410616450300163760ustar00rootroot00000000000000 TextEditBase 0 0 481 286 0 0 11 75 true Edit Document Text Template Qt::PlainText false QFrame::HLine QFrame::Sunken &Name: false mEditName Qt::AlignTop false 0 0 displayed as false 0 0 75 true textLabel2 false 0 0 in doc type false 0 0 75 true textLabel3 false Qt::Horizontal QSizePolicy::Expanding 65 16 &Text: Qt::AlignTop false mEditText true Qt::Horizontal QSizePolicy::Expanding 212 16 mEditName mEditText kraft-0.97/src/texteditdialog.cpp000066400000000000000000000067011410616450300170710ustar00rootroot00000000000000/*************************************************************************** texteditdialog.cpp - Edit document text templates ------------------- begin : Apr 2007 copyright : (C) 2007 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include "templtopositiondialogbase.h" #include "texteditdialog.h" #include "doctext.h" #include "defaultprovider.h" TextEditDialog::TextEditDialog( QWidget *parent, KraftDoc::Part docPart ) : QDialog( parent ) { setObjectName( "TEMPL_DIALOG" ); setModal( true ); setWindowTitle( i18n("Edit Text Templates" )); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); QWidget *mainWidget = new QWidget(this); QVBoxLayout *mainLayout = new QVBoxLayout; setLayout(mainLayout); mainLayout->addWidget(mainWidget); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); //PORTING: Verify that widget was added to mainLayout: //PORTING: Verify that widget was added to mainLayout: setMainWidget( mainWidget ); // Add mainLayout->addWidget(mainWidget); if necessary // Add mainLayout->addWidget(mainWidget); if necessary mBaseWidget = new Ui::TextEditBase; mBaseWidget->setupUi( mainWidget ); mBaseWidget->mDocTypeLabel->setText( DocText::textTypeToString( docPart ) ); QString h = i18n( "Edit %1 Template", DocText::textTypeToString( docPart ) ); mBaseWidget->dmHeaderText->setText( h ); mainLayout->addWidget(buttonBox); } TextEditDialog::~TextEditDialog() { } void TextEditDialog::setDocText( DocText dt ) { QString name = i18n( "Template" ); if ( ! dt.name().isEmpty() ) { name = dt.name(); } mBaseWidget->mEditName->setText( name ); // mBaseWidget->mEditDescription->setText( dt.description() ); mBaseWidget->mEditText->setText( dt.text() ); mBaseWidget->mDocPartLabel->setText( dt.textTypeString() ); mBaseWidget->mDocTypeLabel->setText( dt.docType() ); mOriginalText = dt; } DocText TextEditDialog::docText() { DocText dt; dt = mOriginalText; dt.setName( mBaseWidget->mEditName->text() ); dt.setDescription( QString() ); // mBaseWidget->mEditDescription->text() ); dt.setText( mBaseWidget->mEditText->toPlainText() ); // dt.setDocType( mBaseWidget->mCbDocType->currentText() ); // dt.setTextType( DocText::stringToTextType( mBaseWidget->mCbTextType->currentText() ) ); return dt; } kraft-0.97/src/texteditdialog.h000066400000000000000000000026311410616450300165340ustar00rootroot00000000000000/*************************************************************************** texteditdialog.h - Edit document text templates ------------------- begin : Apr 2007 copyright : (C) 2007 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef TEXTEDITDIALOG_H #define TEXTEDITDIALOG_H #include #include "ui_texteditbase.h" #include "doctext.h" #include "kraftdoc.h" class QWidget; class QComboBox; class DocText; class TextEditBase; class TextEditDialog: public QDialog { Q_OBJECT public: TextEditDialog( QWidget*, KraftDoc::Part ); ~TextEditDialog( ); virtual void setDocText( DocText ); DocText docText(); private: Ui::TextEditBase *mBaseWidget; DocText mOriginalText; }; #endif kraft-0.97/src/textselection.cpp000066400000000000000000000172111410616450300167470ustar00rootroot00000000000000/*************************************************************************** textselection - widget to select header- and footer text data for the doc ------------------- begin : 2007-06-01 copyright : (C) 2007 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include "textselection.h" #include "filterheader.h" #include "defaultprovider.h" #include "kraftdoc.h" #include "doctype.h" #include "doctext.h" #include #include #include #include #include #include #include #include #include #include #include #include TextSelection::TextSelection( QWidget *parent, KraftDoc::Part part ) :QWidget( parent ), mPart( part ) { mGroupBox = new QGroupBox(i18n("Template Collection")); QVBoxLayout *layout = new QVBoxLayout; setLayout(layout); layout->addWidget( mGroupBox ); /* a view for the entry text repository */ QVBoxLayout *vbox = new QVBoxLayout; vbox->setMargin(0); mTextNameView = new QListView; vbox->addWidget(mTextNameView); mTextNameView->setSelectionMode( QAbstractItemView::SingleSelection ); mTextNameView->setMaximumHeight(120 ); mTextNameView->setEditTriggers( QAbstractItemView::NoEditTriggers ); connect( mTextNameView, SIGNAL(doubleClicked(QModelIndex)), this, SIGNAL(editCurrentTemplate())); mTextDisplay = new QTextEdit; mTextDisplay->setFrameStyle( QFrame::StyledPanel | QFrame::Sunken ); mTextDisplay->setLineWidth( 1 ); mTextDisplay->setReadOnly(true); QPalette p = mTextDisplay->palette(); p.setColor( QPalette::Active, QPalette::Base, p.color(QPalette::Window)); p.setColor( QPalette::Inactive, QPalette::Base, p.color(QPalette::Window)); mTextDisplay->setPalette(p); vbox->addWidget( mTextDisplay, 3 ); mHelpDisplay = new QLabel; mHelpDisplay->setStyleSheet("background-color: #ffcbcb;"); mHelpDisplay->setAutoFillBackground(true); mHelpDisplay->setWordWrap( true ); QFontMetrics fm( mHelpDisplay->font() ); int minHeight = 1.5 * fm.height(); mHelpDisplay->setMinimumHeight( minHeight ); mHelpDisplay->setAlignment( Qt::AlignCenter | Qt::AlignVCenter ); mHelpDisplay->hide(); vbox->addWidget( mHelpDisplay ); mGroupBox->setLayout( vbox ); mTemplNamesModel = new QStringListModel; mTextNameView->setModel( mTemplNamesModel ); connect( mTextNameView->selectionModel(), SIGNAL( currentChanged( const QModelIndex&, const QModelIndex& ) ), this, SLOT( slotTemplateNameSelected( const QModelIndex&, const QModelIndex& ) ) ); #if 0 connect( mTextsView, SIGNAL( currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*) ), this, SLOT( slotSelectionChanged( QTreeWidgetItem* ) ) ); connect( mTextsView, SIGNAL(doubleClicked(QModelIndex) ), this, SLOT( slotSelectionChanged( QTreeWidgetItem* ) ) ); #endif // Context Menu mMenu = new QMenu( this ); mMenu->setTitle( i18n("Template Actions") ); #if 0 mTextsView->setContextMenuPolicy(Qt::CustomContextMenu); connect( mTextsView, SIGNAL(customContextMenuRequested(QPoint) ), this, SLOT( slotRMB( QPoint ) ) ); #endif initActions(); } /* selected the name of a template in the listview of template names */ void TextSelection::slotTemplateNameSelected( const QModelIndex& current, const QModelIndex& ) { if( current.isValid() ) { mCurrTemplateName = mTemplNamesModel->data( current, Qt::DisplayRole ).toString(); // qDebug () << "New selected template name: " << mCurrTemplateName; showHelp(); DocText dt = currentDocText(); showDocText( dt ); } else { mCurrTemplateName.clear(); } emit validTemplateSelected( ); } void TextSelection::showDocText( DocText dt ) { if( dt.type() != KraftDoc::Unknown && dt.isStandardText() ) { showHelp(i18n("This is the standard text used in new documents.")); } mTextDisplay->setText( dt.text() ); } void TextSelection::slotSelectDocType( const QString& doctype ) { QString partStr = KraftDoc::partToString( mPart ); QString t = QString( i18n( "%1 Templates for %2", partStr, doctype) ); mGroupBox->setTitle( t ); mDocType = doctype; DocTextList dtList = DefaultProvider::self()->documentTexts( doctype, mPart ); QStringList templNames; if( dtList.count() == 0 ) { showHelp( i18n("There is no %1 template text available for document type %2.
            " "Click the add-button below to create one.", partStr, doctype ) ); } else { foreach( DocText dt, dtList ) { templNames << dt.name(); } showHelp(); } mTemplNamesModel->setStringList( templNames ); mTextDisplay->clear(); } void TextSelection::addNewDocText( const DocText& dt ) { slotSelectDocType( mDocType ); // update the list of available texts QModelIndexList newItems = mTemplNamesModel->match( mTemplNamesModel->index(0), Qt::DisplayRole, dt.name() ); if( newItems.size() > 0 ) { QModelIndex selected = newItems[0]; mTextNameView->selectionModel()->setCurrentIndex( selected, QItemSelectionModel::Select); } else { // qDebug () << "Unable to find the new item named " << dt.name(); } emit validTemplateSelected(); } /* requires the QListViewItem set as a member in the doctext */ void TextSelection::updateDocText( const DocText& ) { QModelIndex selected = mTextNameView->selectionModel()->currentIndex(); if( selected.isValid() ) { slotSelectDocType( mDocType ); mTextNameView->selectionModel()->setCurrentIndex( selected, QItemSelectionModel::Select ); } } bool TextSelection::validSelection() const { return mTextNameView->selectionModel()->currentIndex().isValid(); } void TextSelection::deleteCurrentText() { slotSelectDocType( mDocType ); } TextSelection::~TextSelection() { } void TextSelection::initActions() { mAcMoveToDoc = new QAction(QIcon::fromTheme( "go-previous" ), i18n("&Use in Document"), this); connect(mAcMoveToDoc, SIGNAL(triggered()), this, SIGNAL(actionCurrentTextToDoc())); mMenu->addAction( mAcMoveToDoc ); } /* if the help string is empty, the help widget disappears. */ void TextSelection::showHelp( const QString& help ) { mHelpDisplay->setText( help ); if( help.isEmpty() ) { mHelpDisplay->hide(); } else { mHelpDisplay->show(); #if 0 // qDebug () << "Displaying help text: " << help; QPropertyAnimation *ani = new QPropertyAnimation( mHelpDisplay, "geometry" ); QRect r2 = r1; r2.setHeight( 200 ); ani->setDuration( 2000 ); ani->setStartValue( r1 ); ani->setEndValue( r2 ); ani->start(); #endif } } DocText TextSelection::currentDocText() const { DocTextList dtList = DefaultProvider::self()->documentTexts( mDocType, mPart ); foreach( DocText dt, dtList ) { if( dt.name() == mCurrTemplateName ) { return dt; } } DocText dt; return dt; } QString TextSelection::currentText() const { return currentDocText().text(); } void TextSelection::slotRMB(QPoint ) { // mMenu->popup( mTextsView->mapToGlobal(point) ); } kraft-0.97/src/textselection.h000066400000000000000000000055001410616450300164120ustar00rootroot00000000000000/*************************************************************************** textselection - widget to select header- and footer text data for the doc ------------------- begin : 2007-06-01 copyright : (C) 2007 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef FOOTERSELECTION_H #define FOOTERSELECTION_H #include #include #include #include "kraftdoc.h" class QTreeWidget; class QTreeWidgetItem; class QString; class QPoint; class QMenu; class DocText; class QAction; class QListView; class QStringListModel; class QTextEdit; class QLabel; class QModelIndex; class QGroupBox; class TextSelection : public QWidget { Q_OBJECT public: TextSelection( QWidget*, KraftDoc::Part ); ~TextSelection(); QString currentText() const; DocText currentDocText() const; bool validSelection() const; signals: void actionCurrentTextToDoc(); void currentTextChanged( const QString& ); void validTemplateSelected(); void editCurrentTemplate(); public slots: void addNewDocText( const DocText& ); void deleteCurrentText(); void updateDocText( const DocText& ); void slotSelectDocType( const QString& ); void slotRMB( QPoint ); protected: void initActions(); void buildTextList( KraftDoc::Part ); void showDocText( DocText ); protected slots: // void slotSelectionChanged( QTreeWidgetItem* ); void slotTemplateNameSelected( const QModelIndex&, const QModelIndex& ); void showHelp( const QString& help = QString() ); private: QListView *mTextNameView; QStringListModel *mTemplNamesModel; QTextEdit *mTextDisplay; QLabel *mHelpDisplay; KraftDoc::Part mPart; QString mDocType; QString mCurrTemplateName; QMap mTextMap; QMap mDocTypeItemMap; QMap mStandardItemMap; QMenu *mMenu; QGroupBox *mGroupBox; QAction *mAcMoveToDoc; }; #endif kraft-0.97/src/texttemplate.cpp000066400000000000000000000072171410616450300166020ustar00rootroot00000000000000/*************************************************************************** texttemplate.cpp - fill a template with text tags ------------------- begin : Sep 2007 copyright : (C) 2007 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include "texttemplate.h" #include "ctemplate/template.h" #include "klocalizedstring.h" #include #include TextTemplate::TextTemplate() :TextTemplateInterface(), mStandardDict( nullptr ) { } TextTemplate::~TextTemplate() { if (mStandardDict) delete mStandardDict; } bool TextTemplate::createSubDictionary( const QString& parent, const QString& name ) { Dictionary ttd; bool re = false; if ( mDictionaries.contains( parent ) ) { ttd.mDict = mDictionaries[parent]->AddSectionDictionary( name.toAscii().data() ); ttd.mParent = parent; ttd.mName = name; mDictionaries[name] = ttd.mDict; re = true; } return re; } void TextTemplate::createDictionary( const QString& dictName ) { if ( mStandardDict ) { mDictionaries[dictName] = mStandardDict->AddSectionDictionary( dictName.toAscii().data() ); mStandardDict->ShowSection( dictName.toAscii().data() ); } } void TextTemplate::setValue( const QString& dictName, const QString& key, const QString& val ) { TemplateDictionary *dict = nullptr; if ( mDictionaries.contains( dictName ) ) { dict = mDictionaries[dictName]; } else { if( mStandardDict ) { dict = mStandardDict->AddSectionDictionary( dictName.toAscii().data() ); mDictionaries[dictName] = dict; mStandardDict->ShowSection( dictName.toAscii().data() ); } } if ( dict ) dict->SetValue( key.toAscii().data(), val.toStdString() ); // std::string( val.toUtf8() ) ); } void TextTemplate::setValue( const QString& key, const QString& val ) { if ( mStandardDict ) { mStandardDict->SetValue( key.toAscii().data(), val.toStdString() ); } } void TextTemplate::setValue( Dictionary ttd, const QString& key, const QString& val ) { if ( ttd.mDict ) { ( ttd.mDict )->SetValue( key.toAscii().data(), val.toStdString() ); } } bool TextTemplate::initialize() { Template *tmpl = Template::GetTemplate(fileName().toStdString(), ctemplate::DO_NOT_STRIP ); if ( !tmpl || tmpl->state() != ctemplate::TS_READY ) { setError( i18n( "Failed to open template source" ) ); return false; } tmpl->ReloadAllIfChanged(); if (mStandardDict) delete mStandardDict; mStandardDict = new TemplateDictionary( "TopLevel" ); return true; } QString TextTemplate::expand() { std::string output; if ( mStandardDict) { bool errorFree = ExpandTemplate( fileName().toStdString(), ctemplate::DO_NOT_STRIP ,mStandardDict, &output ); QString qout = QString::fromStdString(output); qout.remove(QChar(0)); if ( errorFree ) { return qout; } } return QStringLiteral("Unable to expand template"); } kraft-0.97/src/texttemplate.h000066400000000000000000000051341410616450300162430ustar00rootroot00000000000000/*************************************************************************** texttemplate.h - fill a template with text tags ------------------- begin : Sep 2007 copyright : (C) 2007 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef TEXTTEMPLATE_H #define TEXTTEMPLATE_H #include #include #include "texttemplateinterface.h" #include #include using ctemplate::Template; using ctemplate::TemplateDictionary; class TextTemplate : public TextTemplateInterface { private: struct Dictionary { QString mParent; QString mName; TemplateDictionary *mDict; }; public: TextTemplate(); ~TextTemplate() override; /** * set a value in the default dictionary */ void setValue( const QString&, const QString& ) override; /** * set a value in the named dictionary * Parameters: * the parameter group name * the key name * the value */ void setValue( const QString&, const QString&, const QString& ); void setValue( Dictionary, const QString& , const QString& ); void createDictionary( const QString& ); /** * creates a sub dictionary to a given dictionary. * Parameter 1 is the parent dict, Param 2 the sub dictionary name. */ bool createSubDictionary( const QString& , const QString& ); /** * creates a dictionary with the name given in parameter two nested * in the parent dictionary given in the first parameter. * * The dictionary struck is given back to use with setValue. */ // Dictionary createSubDictionary( Dictionary, const QString& ); /** * get the expanded output */ QString expand() override; protected: virtual bool initialize() override; private: TemplateDictionary *mStandardDict; QMap mDictionaries; }; #endif kraft-0.97/src/texttemplateinterface.cpp000066400000000000000000000026001410616450300204520ustar00rootroot00000000000000#include "texttemplateinterface.h" #include "defaultprovider.h" #include #include #include namespace { QString findTemplateFile(const QString &filename) { QString tmplFile; if( ! filename.isEmpty() ) { const QString templFileName = QStringLiteral("kraft/")+filename; tmplFile = DefaultProvider::self()->locateFile(templFileName); } return tmplFile; } } // end of anonym namespace TextTemplateInterface::TextTemplateInterface() { } TextTemplateInterface::~TextTemplateInterface() { } QString TextTemplateInterface::fileName() const { return _fileName; } bool TextTemplateInterface::setTemplateFileName( const QString& name ) { _errorString.clear(); _fileName = name; QFileInfo info( _fileName ); if ( info.isAbsolute() ) { // assume it is a absolute path } else { _fileName = findTemplateFile(_fileName); if ( _fileName.isEmpty() ) { _errorString = i18n( "No file name given for template" ); return false; } info.setFile( _fileName ); } if ( ! ( info.isFile() && info.isReadable() ) ) { _errorString = i18n( "Could not find template file %1", info.absoluteFilePath() ); return false; } qDebug () << "Loading template source file: " << _fileName << endl; return initialize(); } bool TextTemplateInterface::isOk() { return _errorString.isEmpty(); } kraft-0.97/src/texttemplateinterface.h000066400000000000000000000027661410616450300201340ustar00rootroot00000000000000#ifndef TextTemplateInterface_H #define TextTemplateInterface_H #include class TextTemplateInterface { public: TextTemplateInterface(); virtual ~TextTemplateInterface(); /** * take the template absolute filename of the template source and * load it immediately. * returns true if successful. Otherwise check errorString() for * error messages */ bool setTemplateFileName(const QString& file); QString fileName() const; /** * @brief isOk - returns true if the TextTemplate is ok and can be used. * @return true if the text template can be used. */ virtual bool isOk(); /** * return a describing string if something went wrong when opening * the template. */ QString errorString() const { return _errorString; } /** * set a value in the default dictionary */ virtual void setValue( const QString&, const QString& ) = 0; /** * get the expanded output */ virtual QString expand() = 0; protected: /** * @brief initialize - use for basic initialization * @return true if successful * * This method can assume that the filename member var points to * a valid file. */ virtual bool initialize() = 0; /** * @brief overwrites the error message * @param the error string */ void setError(const QString& msg) { _errorString = msg; } private: QString _fileName; QString _errorString; }; #endif // TextTemplateInterface_H kraft-0.97/src/timecalcdialog.cpp000066400000000000000000000057201410616450300170200ustar00rootroot00000000000000/*************************************************************************** Timecalcdialog - ------------------- begin : 2004-23-09 copyright : (C) 2004 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ // include files for Qt #include #include #include #include #include "timecalcdialog.h" #include "timecalcpart.h" #include "stdsatzman.h" TimeCalcDialog::TimeCalcDialog(QWidget *parent) : CalcDialogBase( parent ), _timeWidget(new Ui::calcdetailTime), _part(0) { _timeWidget->setupUi(_centralWidget); _timeWidget->m_hourSets->insertItems(-1, StdSatzMan::self()->allStdSaetze()); _timeWidget->_cbTimeUnit->addItems(TimeCalcPart::timeUnitStrings()); } void TimeCalcDialog::setTimeCalcPart(TimeCalcPart *cp) { _part = cp; if( ! cp ) return; _timeWidget->m_nameEdit->setText( cp->getName()); _timeWidget->m_dauer->setValue( cp->duration()); _timeWidget->_cbTimeUnit->setCurrentText( TimeCalcPart::timeUnitString(cp->timeUnit())); _timeWidget->m_stdGlobal->setChecked(cp->globalStdSetAllowed()); StdSatz std = cp->getStundensatz(); _timeWidget->m_hourSets->setCurrentIndex(_timeWidget->m_hourSets->findText( std.getName() )); } void TimeCalcDialog::accept() { if( _part ) { _part->setGlobalStdSetAllowed(_timeWidget->m_stdGlobal->isChecked()); _part->setDuration(_timeWidget->m_dauer->value(), _timeWidget->_cbTimeUnit->currentText()); _part->setName(_timeWidget->m_nameEdit->text()); QString selHourSet = _timeWidget->m_hourSets->currentText(); StdSatz stdsatz = StdSatzMan::self()->getStdSatz(selHourSet); _part->setStundensatz(stdsatz); } if( _part && _part->isDirty() ) { emit timeCalcPartChanged(_part); } CalcDialogBase::accept(); } QString TimeCalcDialog::getName() { return _timeWidget->m_nameEdit->text(); } int TimeCalcDialog::getDauer() { return _timeWidget->m_dauer->value(); } bool TimeCalcDialog::allowGlobal() { return _timeWidget->m_stdGlobal->isChecked(); } QString TimeCalcDialog::getStundensatzName() { return _timeWidget->m_hourSets->currentText(); } QString TimeCalcDialog::unitStr() const { return _timeWidget->_cbTimeUnit->currentText(); } /* END */ kraft-0.97/src/timecalcdialog.h000066400000000000000000000031321410616450300164600ustar00rootroot00000000000000/*************************************************************************** Timecalcdialog - ------------------- begin : 2004-23-09 copyright : (C) 2004 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef _TimeCALCDIALOG_H #define _TimeCALCDIALOG_H // include files #include "calcdialogbase.h" #include "ui_timepart.h" /** * */ class TimeCalcPart; class TimeCalcDialog : public CalcDialogBase { Q_OBJECT public: TimeCalcDialog(QWidget *parent=0); void setTimeCalcPart(TimeCalcPart *cp); QString getName(); QString getStundensatzName(); int getDauer(); bool allowGlobal(); QString unitStr() const; signals: void timeCalcPartChanged(TimeCalcPart*); protected slots: void accept(); private: Ui_calcdetailTime *_timeWidget; TimeCalcPart *_part; }; #endif /* END */ kraft-0.97/src/timecalcpart.cpp000066400000000000000000000101441410616450300165230ustar00rootroot00000000000000/*************************************************************************** timecalcpart.cpp - ------------------- begin : Don Jan 1 2004 copyright : (C) 2004 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include #include "klocalizedstring.h" #include "timecalcpart.h" TimeCalcPart::TimeCalcPart() :CalcPart(), _duration( 0 ), m_allowGlobalStundensatz(false), _timeUnit(Minutes) { } TimeCalcPart::TimeCalcPart(const QString& name, int minutes, TimeUnit unit, int prozent) :CalcPart( name, prozent ), _duration( minutes ), m_allowGlobalStundensatz(true), _timeUnit( unit ) { } TimeCalcPart::~TimeCalcPart() { } StdSatz& TimeCalcPart::getStundensatz() { return m_stundensatz; } /** Write property of Geld m_stundensatz. */ void TimeCalcPart::setStundensatz( const StdSatz& _newVal) { // qDebug() << "stundensatz gesetzt: " << _newVal.toString() << endl; m_stundensatz = _newVal; setDirty(true); } void TimeCalcPart::setGlobalStdSetAllowed( bool s ) { if( m_allowGlobalStundensatz != s ) { m_allowGlobalStundensatz = s; setDirty(true); } } void TimeCalcPart::setDuration( int duration, const QString& unitStr ) { if( _duration != duration || timeUnitString(_timeUnit) != unitStr ) { _duration = duration; _timeUnit = timeUnitFromString(unitStr); setDirty(true); } } QString TimeCalcPart::timeUnitString( const TimeUnit& unit ) { if( unit == Minutes ) { return i18n("Minutes"); } else if( unit == Hours) { return i18n("Hours"); } return i18n("Seconds"); } QStringList TimeCalcPart::timeUnitStrings() { // When adding something here make sure to adjust other places in the file return QStringList() << timeUnitString(Minutes) << timeUnitString(Seconds) << timeUnitString(Hours); } TimeCalcPart::TimeUnit TimeCalcPart::timeUnitFromString( const QString& unit) { const QStringList li = timeUnitStrings(); int pos = li.indexOf(unit); return timeUnitFromInt(pos); } TimeCalcPart::TimeUnit TimeCalcPart::timeUnitFromInt( int index ) { // the static_cast here need to be updated if new enums are added if( index > -1 && index <= static_cast(Hours)) { switch (index) { case static_cast(Minutes): case static_cast(Seconds): case static_cast(Hours): return static_cast(index); default: // this is actually an error case, forgot to add a pot. new enum... return Minutes; } } return Minutes; } int TimeCalcPart::timeUnitIndex() const { // Make sure to adopt this if a new unit is added! if( _timeUnit == Hours ) return 2; else if( _timeUnit == Seconds ) return 1; else return 0; } qint32 TimeCalcPart::durationToSeconds() const { if( _timeUnit == Minutes ) { return _duration * 60; } else if( _timeUnit == Hours ) { return 60*60*_duration; } // seconds is default return _duration; } Geld TimeCalcPart::basisKosten() { StdSatz stdSatz = getStundensatz(); const Geld g( (stdSatz.getPreis().toLong() * durationToSeconds()) / 360000.0); return g; } QString TimeCalcPart::getType() const { return KALKPART_TIME; } kraft-0.97/src/timecalcpart.h000066400000000000000000000044551410616450300162000ustar00rootroot00000000000000/*************************************************************************** Timecalcpart.h - ------------------- begin : Don Jan 1 2004 copyright : (C) 2004 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef TIMECALCPART_H #define TIMECALCPART_H #include #include "stdsatzman.h" /** *@author Klaas Freitag */ class TimeCalcPart : public CalcPart { public: enum TimeUnit {Minutes, Seconds, Hours}; TimeCalcPart( const QString& name, int minutes, TimeUnit unit, int prozent = 0); TimeCalcPart(); ~TimeCalcPart(); bool globalStdSetAllowed() { return m_allowGlobalStundensatz; } void setGlobalStdSetAllowed( bool s ); void setDuration(int duration, const QString &unitStr ); static QStringList timeUnitStrings(); static QString timeUnitString( const TimeUnit& unit ); static TimeCalcPart::TimeUnit timeUnitFromString( const QString& unit); static TimeCalcPart::TimeUnit timeUnitFromInt(int index); qint32 durationToSeconds() const; qint32 duration() const { return _duration; } TimeUnit timeUnit() const { return _timeUnit; } int timeUnitIndex() const; virtual Geld basisKosten(); /** Write property of Geld m_stundensatz. */ virtual void setStundensatz( const StdSatz& _newVal); /** Read property of Geld m_stundensatz. */ virtual StdSatz& getStundensatz(); virtual QString getType() const; private: qint32 _duration; /** */ StdSatz m_stundensatz; bool m_allowGlobalStundensatz; TimeUnit _timeUnit; }; #endif kraft-0.97/src/timepart.ui000066400000000000000000000076321410616450300155430ustar00rootroot00000000000000 calcdetailTime 0 0 432 275 Calculation Item Time <h1>Calculation Part 'Time'</h1> false Calculate time efforts here for one unit of the template. <br/>Note that the costs may depend on a global hourly rate. Qt::RichText Qt::AlignVCenter false &Label: false m_nameEdit Work &Time Effort: false m_dauer 5 0 10000 &Hourly Rate: false m_hourSets 0 0 Apply the &global hourly rate m_nameEdit m_dauer m_hourSets m_stdGlobal kraft-0.97/src/unitmanager.cpp000066400000000000000000000054731410616450300163760ustar00rootroot00000000000000/*************************************************************************** unitmanager - ------------------- begin : 2004-05-05 copyright : (C) 2004 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include // include files for KDE #include #include #include "unitmanager.h" #include "einheit.h" Q_GLOBAL_STATIC(UnitManager, mSelf) UnitManager* UnitManager::self() { return mSelf; } UnitManager::UnitManager( ) { } void UnitManager::load() { QSqlQuery q( "SELECT unitID, unitShort, unitLong, unitPluShort, unitPluLong FROM units"); while( q.next()) { int unitID = q.value(0).toInt(); Einheit e( unitID, q.value(1).toString(), q.value(2).toString(), q.value(3).toString(), q.value(4).toString() ); mUnits.append(e); } } int UnitManager::nextFreeId() { int id = 0; if( mUnits.size() == 0 ) { load(); } foreach( Einheit u, mUnits ) { if( u.id() > id ) { id = u.id(); } } return id+1; } QStringList UnitManager::allUnits() { QStringList list; if(mUnits.size() == 0 ) load(); foreach( Einheit e, mUnits ) { QString uSing = e.einheitSingular(); if( !uSing.isEmpty()) list << uSing; } return list; } Einheit UnitManager::getPauschUnit() { int id = getUnitIDSingular(QStringLiteral("pausch.")); if (id > -1) return getUnit(id); return Einheit(); } Einheit UnitManager::getUnit( int id ) { if( mUnits.size() == 0 ) load(); // qDebug() << "Searching unit ID " << id << endl; foreach( Einheit e, mUnits ) { if( e.id() == id ) return e; } return Einheit(); } int UnitManager::getUnitIDSingular( const QString& einheitStr ) { if( mUnits.size() == 0 ) load(); foreach( Einheit tmp, mUnits ) { if( tmp.einheitSingular() == einheitStr || tmp.einheitPlural() == einheitStr ) { // qDebug() << "Thats it, returning " << tmp.id() << endl; return tmp.id(); } } return -1; } UnitManager::~UnitManager( ) { } /* END */ kraft-0.97/src/unitmanager.h000066400000000000000000000031561410616450300160370ustar00rootroot00000000000000/*************************************************************************** unitmanager - ------------------- begin : 2004-05-05 copyright : (C) 2004 by Klaas Freitag email : freitag@kde.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef _UNITMANAGER_H #define _UNITMANAGER_H #include "einheit.h" /** * */ // FIXME: How to identify the unit for piece? #define PIECE_UNIT_ID 6 class UnitManager { public: UnitManager(); virtual ~UnitManager(); static UnitManager* self(); Einheit getUnit( int id ); Einheit getPauschUnit(); QStringList allUnits(); int getUnitIDSingular( const QString& einheit ); // Workaround: since the unit table does not have an auto update id coloum, // this function calculates the next free unit id to save a new one. int nextFreeId(); private: Einheit::List mUnits; void load(); }; #endif /* END */ kraft-0.97/src/unitseditbase.ui000066400000000000000000000044411410616450300165540ustar00rootroot00000000000000 UnitsEditBase 0 0 348 151 <h1>Add a unit</h1> false Unit short mUnitShort Unit long mUnitLong Unit plural short mUnitPluShort Unit plural long mUnitPluLong kraft-0.97/src/upgradedb.ui000066400000000000000000000052711410616450300156500ustar00rootroot00000000000000 upgradeDbForm 0 0 420 186 This step checks if the database schema version is sufficient for this version of Kraft. In case it is not, the schema is updated automatically. true 0 0 / 129 30 0 X Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 0 0 Status: Upgrade not yet started true Qt::Vertical 20 101 kraft-0.97/src/version.h000066400000000000000000000001501410616450300152010ustar00rootroot00000000000000#define KRAFT_VERSION "0.97" #define KRAFT_CODENAME "Gunny" #define KRAFT_REQUIRED_SCHEMA_VERSION 22 kraft-0.97/src/wageseditbase.ui000066400000000000000000000025551410616450300165240ustar00rootroot00000000000000 WagesEditBase 0 0 348 123 <h1>Add a Wage group</h1> false Group name Wage kraft-0.97/styles/000077500000000000000000000000001410616450300141035ustar00rootroot00000000000000kraft-0.97/styles/CMakeLists.txt000066400000000000000000000004421410616450300166430ustar00rootroot00000000000000########### install files ############### add_subdirectory(pics) install(FILES templcatalog.style DESTINATION ${DATA_INSTALL_DIR}/kraft/styles) install(FILES docdigestview.css docoverview.css docoverview_ro.css catalogview.css systemview.css DESTINATION ${DATA_INSTALL_DIR}/kraft/styles) kraft-0.97/styles/catalogview.css000066400000000000000000000005751410616450300171310ustar00rootroot00000000000000 * { font-family: sans-serif; margin-left: 10px; margin-top: 10px; } h2 { padding-top: 20px; margin-left: 20px; margin-bottom: 10px; } tr { } td.bigfont { font-size: x-large; color: #323432; font-weight: bold; background: #dffdd0; padding-right: 20px; } td.sub { margin-bottom: 20px; padding-left: 20px; padding-bottom: 30px; font-size: small; } kraft-0.97/styles/docdigestview.css000066400000000000000000000000351410616450300174530ustar00rootroot00000000000000 h2 { color: #64ad24; } kraft-0.97/styles/docoverview.css000066400000000000000000000016171410616450300171560ustar00rootroot00000000000000 body { background-image:url(pics/docoverviewbg.png); font-family: 'Source Sans Pro', sans-serif; } td { vertical-align:top; } div { padding: 1px; color: #898989; } a:link { text-decoration: none; color: #00008b; } a:visited { text-decoration: none; color: #00008b; } a:hover { color: #00008b; text-decoration: none; } a:active { color: #00008b; text-decoration: underline; } .headerlink { background: #8ddbe9; } .headerlink_selected { background: #9af0ff; font-weight: bold; } .bodylink { background: #8de99a; } .bodylink_selected { background: #9affaa; font-weight: bold; } .footerlink { background: #dbe98d; } .footerlink_selected { background: #f0ff9a; font-weight: bold; } .negative { color: #800040; } .head_selected { } .body_selected { } .foot_selected { } td.baseline { } td.itemnums { } td.itemtexts { } td.prices { } kraft-0.97/styles/docoverview_ro.css000066400000000000000000000007101410616450300176470ustar00rootroot00000000000000body { margin:20px; background-image:url(pics/docoverviewbg.png); color: #4e4e4e; font-size:x-small; font-family: 'Source Sans Pro', sans-serif; } td { vertical-align:top; } div { border-style:none; border-width:0px; border-color:#00008B; margin: 1px 0px 1px 0px; padding: 1px; } .negative { color: #800040; } .positive { } div.header { margin-top: 120; margin-bottom: 20px; vertical-align:bottom; } kraft-0.97/styles/pics/000077500000000000000000000000001410616450300150415ustar00rootroot00000000000000kraft-0.97/styles/pics/Blank_Calendar_page_icon.svg000066400000000000000000000203621410616450300224110ustar00rootroot00000000000000 image/svg+xml blank calendar 20071204 Jackaranga GFDL kraft-0.97/styles/pics/CMakeLists.txt000066400000000000000000000002651410616450300176040ustar00rootroot00000000000000########### install files ############### install(FILES docoverviewbg.png kraftapp_logo_trans.png kraft_customer.png postit.png DESTINATION ${DATA_INSTALL_DIR}/kraft/styles/pics) kraft-0.97/styles/pics/Calendar_page.png000066400000000000000000000050701410616450300202560ustar00rootroot00000000000000PNG  IHDR|ZbKGD pHYs B(xtIME 3qx IDATxkl[c؉\΅6IeҎ)C ! e~ V VƘШ[XۥTHEcem^HMԎ9ѬMb[ct~|w,M}>'н^2ޯ{>w)q9ssX@?80f5'EFɿm۶M̈́jEW EǞmڔ)‡+ l;g,VJ㨦Wl3!ɴ p'W4A;Xu}sBx͙n+… DpA "\D .pA 2[Ͱ%(PoVQ_8zh^YO)tgR6<'Ex8$EL(r78 ǽ(M%F4-mrHj"R d~l&?e /&3KƵ':%&5gR40( 2jLDrUOP53<7iSx(,Li/w9iu8ŋTZ!\`' FAYYv7XCWaF"_pӟBYj=TXFFdA뫪R]2߃+[MUwRUC3??%gw]M|ԁ-_\U`^g8 G5[R]>-BZ-C!Px=J K^Ӷ62pL v#_{n2%v)#/N*M&ye^Y <FAx?5 N]g:OB$<[T8~ !qX-^LYS'y[ɟB7@i,_ҝxjȉp_Ql$Qn膝^>V+'U{ApdhIa!IUE4IqN8wGcσޢcx$.%J:a^C=~ VAD́FNn 8Z\ïǂqx<|2rF~Z/-ȗ(*yjnvXjɇMu#'jBR Ň`nTxvS}iGRo"wnM.K[%9 ,;7bܿsFbOµG,&@.;\.nn ϭ).6AmB`pҥeJu[&Szn!k6ٖ=\fH˶L 6| [^\D .pA "\D ..pA "\D .pA "\K D .pA "\D .pApA "\D .pA "\DD .pA "\D …/i&i t]LD"& xDK6rh45b===  ~#fhh)w9/)n]]]o[[[$ٷo/6|KVp8LWWW;7xw&;y:;;_+'m:'2$v BG MMMx2v5~ ,\'N$u!+Bz]]݁D뽱XLlݺfq xGejܹ>oD"tMV^.{HSSSsGGdz_3 Z穪z+$sDm]P͟@ [qEQ$Ϋھ}[^򫡺S@@VAJ$~ ,L Omm/Ri bϞ=e˖xrOv=([74M߻wotÆ 'jjj`&NƬTTTSYYX}} u֭3M8G՚Ϸ2i᪨vr:EEE%eee6iq8VEQ)D  A0{ٷsSĤDIENDB`kraft-0.97/styles/pics/docoverviewbg.png000066400000000000000000000003171410616450300204150ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIME  !%*dtEXtCommentCreated with The GIMPd%n3IDAT8c !?+8A&dEh^^^3d4 ̀MBTIENDB`kraft-0.97/styles/pics/kraft_customer.png000066400000000000000000000114401410616450300205770ustar00rootroot00000000000000PNG  IHDR00WtEXtSoftwareAdobe ImageReadyqe<IDATxZi]Y~wx;;N8iRph46 TЖPAS "AT@-E~T]( ޔiK8x6sG 1f sMpw<ව ꋏc~]` ю8QSuyh=R[=3`;6LY< W3 }<<_4d, ~?e^!]hi;2Fɑ4Lv729/֔,{a<'{;l+dz}:sdǂ0XVV+ô_mdO XӴˤ]-)3U`Q?+eOM^4%@7ip9<ݹGN{_ ĽKjٖ-"K;ô]:!ycFXyGEᑇnŁr o=VTݿ^z= ɛb$ftc?:d7l?sMC?KLDy׆\HBE(v:]ty;?{|6[q YSs]سK.Y• j6i92ꦇ=b lu+*dLF-TDwA:9TVѢ#ׁ{M bdD8_ysWuA\^lm9T6T6^=҉/:4OV{Ư i Aԏ&2@u XFͶD9 sþad6q~zuJIE# dFرs|R Kg:_AU& l07%Kz}|\]vhP0 oFʏ"td_;KK l7>V*-,5 窛ݡ8Jwt ˎ򐉁9Cm:)^zfBρ/kvM`6,ǁC5<ze(+ aY$2ܨ t]3:EL2K}epw+z>1NAo9t V]?ҷ/4;v abba26p@ NEq8y-7XD1Z (3AR ڍv>|^$F$6L]3{ʥ܁ geF th"y!f*X}w '=YXvqۑ]8zx'cn`!ɖn (-~$>U7K*hWMJmt5 e rb)9X 'jBR*#v,^qxiEJؐfK8VEx,J l.r Ik$mIB֙UmCϹkdߊ$8 y4  :B ZBu ( NT7XW]U 0`|8b0 U3f4 J[v|C{K)0(ӑ׽M1q1o,/1384sIKx)Y&>9<p| ftזt |Hō(1.,n/ [~tjT^^ax8԰uOWuF'عmxj~gq2;i:mvhA&>c<с\.3e0dK5 WdND,k}F?YF[N%CFet#)5|?jy5U 3K8 4:8h1CN_Bg=U<9d{x<5F,t{L6sbe"u`3ņsnFWc"vSp9#) n–ϠRbtg|Wkx1ߦ,a{"e%ք\L=V*f9Q9hӡ@#e gbqE|6-ߏ*f)|I+'!f$Rwގѹ#_{fm]3BkVkr54dRgFulx!x<[^_|_Bg #Mv-d9 RpJ4 =q\=C]{|-1G cR0Xkx2W~X Y\xMЉ<(a,[}oib03zTC”c2"wrɏ@E+D(g#2?s;8kP퀑v8#C>!Cٗ x A~/$9fh}YKae*2c *i! ~< \3~K}yTIĖ7?73ͫ/)l qLOa3į+QŽq%(Ρ,Xܨh$Xo'oY:y`M7+w,Nݍ0nB7Iﻟ`xI5'$^{9ŻIB/TqH&MsIL|s0 34ʵDqWQaYCXrz_1>gkfQL$0_G'7XFpMXWq.t}{OlYC1ìW;ǵlN89JaR* &GʸkrÅTrTƔSrHt,,.+Qcj$*a/=S.h=p>8)n,CF ohቸĕ_۞$ ö09>ZRer$\2+MsjVP6Ռ"QGQB |ȽX;x/^\_?}_C @/lp\/:fH9Rh4=>Dof⻭\QJ>5YYp]qF1iI Ѧ9 LyeҒ I$~Ze<<6!$NHH7T$F"L% <`&'$S7r-5ic;K"QP+xK[mjI$,xRݓj2x D}A&b]b qh<hG A3DyME?z],bHL !wv$-(Lx=Q-aj֦:{Gл6>RCD9q3fRž4yD Wg{&۝q$5~7WE^V7HG_Qճ-beOo,t,JZKk-it=xtSuA4g c {O_Z+FD;ݒ+YMo]][mf;.SE|@E[2HhEH"wVd$ GIK1իu]{ĺT&x#Qa-U/peYyB^ȴ;pzSW>0D`;@0cYdSR#&ힰMJ->SoE| 0ۉ%mϏ~/G|2S7L-+EO32ENm2$-HcV%V4A8J]Cz?s?|pZ3:arxI/$p#K P·3 F8`&-!eDUE%tk><}/3o` V7nIENDB`kraft-0.97/styles/pics/kraftapp_logo_trans.png000066400000000000000000000054341410616450300216140ustar00rootroot00000000000000PNG  IHDR@@iqbKGD pHYs  tIME  1IiTXtCommentCreated with GIMPd.e IDATxk]UϹ{Ə9)*RA"HP_A%ቃ82䬛GGǂ%yI5:iwzKHaI Lp# " hD S۾HGWL- i72;XΫ&rZyw1ҷ~I -W 'k:)>E0ZEIn$}-u/˱;V#?زWI ,`F4M%37\3ܱxp)ͷs;KˍCĹX40'"|Ͻ)dد'\B"`>;%(F˽sM\ ;( ǀHg2Žg!=V{C3n 9$G`tߟ|"^eHTs?w^$qDʅ@:^3/ ~Ս`&À$!9җW~׬StPw$ p}(WP =q?yI4[wY!Fd^y$,|l8̽!sn8l/H~8y$GADNG281թooGO7sߐ xGK'l#JTW'j58%> {Z_=kΕfuC)~$ˤ {IO?^Fb y*+85! 'ϐwf_IjFq MtS\ C3&[5Yb^ d ң% Ucպ O PgNKF(G?h5,oXK궾3K,TS88^TYYoH1c$97 GK_sAvwY]AI!Pt87z.[rIy wHU=y'#Rœp7Adie79ʦ4*1kh&8MX%K'}c8;/J%'=瑒Tk+Ϊ$K^I+b)etY<@F[V!,| =SrPIl39w  Iu{0#pxȫHYr1w>Ǯ|{d=6-`C@D੺W}[$oN,' ,+Kѵ:3Pǹ?ew\=,䃼6{p{oH`/IV׉]SI>*՜ՙ%'@H) j?qb0" * \X{5 @:$id@9?W ww37FY(۫'EZf֓NlVqR*{)/+ƭ|֔$ 9H.uO㌯pзw<_m"#ymB( ^\M|U- u8 }{Ȝaٯfa01rw!UI]~-5#|υxS_5_APw;|G"3R\%jWaD (@#>?>g6}m ڧ)B?;ECH:|χ=O?߱U㧺3J1S}GN C5CMO=8oxՋKun ׵z)I?ҳ:_/~U8,\Sy=?[kLx9lk(E|ޕHZwk?ߴ he#^BÓ{5͟LoQ{ͮ`L @ &O'SZ1,@'@9w$]CP3kVIҟp#[ZťҬ @^<=_\`@^&wR^^ji+ɺKsѫ36^|0=^wx$p'=~w._?2H*$u]9 =ɋa(~-]恂~BR4?+ Tw=t}:A4ta0"P^UW]#xў\Ao xB.6Is=SZבjV iΆ9gpT碽 (X "NX+"Rc% cWzqo'h+( K,Nqvi+Hϵjd/ڡD􂼃T&TﵝӾ^5PR&H(P|o_>$ /Q;oG{_a pͨ:_u fW6 ϲuQB ɡba3.I0Nj-lfhtew/0I|FQ=L-4Lva`( /Ң`KӋgrLXkχt w`zLgX`!;q٥3+Ʈ#^ _Lwsm@`" a0]pWtߓA6ZP~<=<СS+UֱJ=htlfAˌN۔i HTg{mDU7;B ȩ{'ݣbA{;T{;}y+P?OF]TI=oO9{_<5OFY7[dU{]|vD|1O *W25PDZCPGNN Щh37Pk`޶;6DGE#J/eSO] yWP}X!mῈ6y^ٰ^Ǒ Prǹj}aVfP'᝵:9gN CvLi `޵V )N [sIPG5N`?.CEŤPMMps?@ĄfA$8m _8vVQp;{lIj.g( H9q*:l6qd "$>h˧xKO Hexܲ`s4rI %c|X TuI@U;PdLN_'2gTImOnL9q=Xt@jFԀ;G ;w+&`ߝGC'Fk5}e?/0[oB}&v #M?5ſ_!M`j$(F7? ~ǁ}- ŏٺ6"#}u*=Z-kc ׿3/n}s F EBK^>)SĹW9C^UୡFDW9 ڟء eZ]8VE n-81{Z~D;5q ׸}};eq|UB51,mQpzs Yw.ͯǏH}5/ \e63dؙ*Xf%7Z.m+`46FcvဍO@`:l`a3A'hOfU:xY~؇)8f9rDUjLɒXCL CtT,! hŎz08hJwc^״1D " I!W%%s*1d6H$1@:8 `  .P.Dlggg_(1ϴwJ0͟' fq,8Ƽ~]j6dlbϘ8vt(Pt%^5w-N_ɾ\و٦7Ta#KWJpCYû_bQ+Dd' k{-wOjKqsF5Ru.k]8> Lf?(ZD bq L ʂOF<#17m U` ^Ȃ4\l, SjEw`M̊R~F/U&P}` z_\'NΉ]v9"IDoX+m`T4tڠ`^0ӱ ~cI!dOR76T=9#& oɢ(a%bxSGo](RE!ٺ))cԺ^)l׍y4,!Y E7=a]#c?i|9?{k䍯c~305aJ,#O{!\FNe(* `ݠٟ%$|`h2Tf :A!Y15lkQ Pt>dz Ĵ " I/i Df3(:E9FJ!^L:KrΉ9ϘsdNc)-P]_zJ8pu@kʈ% dkI<- nGHNM :i'S ]hDlRI*jh E)ᇟտ}u-pz ]ϗ?IO<~i|Sg?x^mD~*~ccPT;Tx+AH HyGA~ J"g83蛢20.{î/bZpuCNjhumAftئ/}F>'쭷S"wcγqߍE D;j5(a*X N5Vsx dE7r^PPChL2rm_/IrmZ Z/Nk8fa¤J"b9%jd`cزY76`<>e~ W )>xɷ㯃V)/_w[-PbH1 Ħ(^Llj+OԷFf":As )k$?' O ޟM?@\n},_k%ܟϵ;WL *Ftޮm2''_J%Wv_2}pܪ 6^L!t+~TLkxDI)HZ Z8'pNZPpH"ې odP,N];qVmX-h7sǛN ٺ}ǁ _nZ2u+@&^&>z4 PLGrY( \);SP:m`gA}e(+\,RAPP,&h(rM,bmHg ?#1Wwή~Ɗ'~!pfZ9w}e ]vT"3b2"3Uw#G,%\bl;ZGд/𕕜.[#XDap8%Ic*DCƯyp܊7='!7S-} %8y3~om k`u?3G??^r(jf1_fe |xfǶ1fss ؕJ0ilVDsD8c l}+[,&:/E:cblw _cGmۖ$]t?H_+;C3_dhw:Ƚ Ve[f」-5I]ݺiM['n(KS!piP^^)r`xbrL!:[ HDJ zf{qH L cR(7BN7xS(XȋO4cEv]- ^ç^?xo~TWm!;0v m\ <V4VPԋT|['N l.ٲm;Cdk 0κ: $ԓ+lC)  02|„Tݯ7J[q2S^b;G1H㟮-iM$>5k:EGju$/!EVTǃWsm\H0$ ͸`w6̔< ;#J 6xQLҴ"p\vKuG2L;/R<趺:ެs4R$Yu`4 ~Lrj 'v /0:~$eIq1={W/θ۷xP0ϧt/#Dg|0^k/#| ^]}g'ܜ0( r Nb63?-M.m5B o6ObiO)c#<xtHswASZ"" "n O?“nNضf2FYM[1y~` z7f# Éeϩ 6n@v IDATpx1ߠ4]H;2V|<jna]V>[Snۼ1Jj%^`q_K>zD;`G`Ǫ`(qQ+DiJ+fNіs` @ZPGblaZ8]'l/qƪ6CcCֳ:ܐ9Y0N`tt!+n q6V!鄧O(pssGgy}7=@x 't(̔(IC`5+rsd aT@t $pmTXzkJA ݯ 6]mʉç³޼ٱ8/m'=zOn[<}r>zǏo no7nnp6cNKR{h+BK6r.>gdHk[weKDoseӽ3t5rG ?)(%RjfF"}Pr٢luYW^wBҶJ/t̍!yؤBW[n&u Og.Mo63l;4rujk̤ eX\!Q NXiՂwd7*Fz: <|s{'7k+</_g{Уoiͣ O'On-<9#>N÷+18=HB!|`JDEw\Vdis$bQjVl(%٧+@J  Q:~xIs1%4kܓ"pC\aaf6mQ!sfRC#n n}zƍeY_ݞ2OQYK:!Дl(^0=3}bN6bO><=ѭa٪񆛛 7N c`ia^Vߛ.|R5E=jB6mteJ4SsBl(@lYl',6 0ВdY*f9) C9JYWAS˜IZk(?ut/Yea0 軆qo"8RC;1C*-m"ch/.3(jA(l=5Ls4-k2 ˆJl79?L"%*ڞΥ-쵎6oj/dlct"f1p6nxwOvwg}ƤA86ܞN8N8n8nm03͋ċ8(ׯ9Q#F$C\G!!gR` yccQjǘ7Ž^~,)QN_^")ǻq'Q1p̱^oC)0|^hy~/h0) 5ʺRƾ!u;gSZǃV4 H@^ĉdx߷kjJH'] J+sBq3QPkQעUtvXZ]Bԩ<ƁYyױM($aF-7Ցvo\/=O%-nӎ-o6Pژ䠑3ѱNiSh*eшwrG&@LȌWj`"UĶeXp sVNX]r@c-ͽ-MNDȸA58֚k9/֧]Ӏg49msRn$=z^ S2?03cU*aRZTQ I3.SHn[ X,H2S:.uhb4Դ$/K/2i89nh_Qc{ڨT+nnD0▗"DQ,*m6{D߀Nt"Pku$j[Mr/iA٥,mF ;:WFO /u+v2\3Y(О|($u&C^b׭(2.^rlhuҋ $D8 |xAcIuW߶(L1%;*UqY{~D\@Lg]g=KeN^&Dݡ(\ [ohnr.ڗh7a VC[∜8~P3MqVb(A4=xiFsP)QI~_F4qyY^ْ;9t8x88/'ߦOpcJSgS3O]dԈh6p;dDU`YS(5B}YgȝYMM /+&AJ'dy|~oZhg-Ӭ_$pJə7Wt+|h*a[˪FKOc7Ed&}~s:M2= ^Ogrm8C !&r&nlȻ:3vfi~WSi4͐3?̩g2@ -Drzp^ w!`)`nV(L6ᖈrR86h#41_pxQc!r mOԚSWb%nLn` BE+M*xS@ёtV0Jem yE*NK!ʋSsY6>h+ͫ0"AH25:|Ѿ{^EEG3+9MwN@ q%9nsb)DNT*J( z0ŚO-= S _y4]սaTx `m[F.XaT ٫`UL\0,MгD JB}-aFUv٠bs.W~bRY]Ai Mg+gZ:| b6+aIۏ4aX$B. xmŖԔ mTZW.xP NLXXIe;y"8= կRE5&},7yZ`!Ip% ́)m4*m%xj#Iqsa }&3 t-:[ϴc4ޥPےd5k70\xu>T2fnA/^dD ǵ]+(#%k@7>p.IbӒdR}?mQB[R5+:9<=w9SY FvG1mW^KRvtӢjwfY%H^#R[N<\rH7OI0tw׮qpD XHy-x,Wb'N}'^:Vp8xx+L`&Z&F0Dq&WAk/#ܥ99N=0n5}psmtw8^6G0EG{k<ѥOxp$}!',e("vܥ% _`P4%ie WD1(5BDыYhTx.?<.7 צS{ɸG]T?kq̆#ڐ` 3'򘭲-'gӪ/" 86^fXI@9,#S Sދbqpu6|7 n7.hSj uIB+PBbA8٬М(I>,$ =m7X2-SE MJWWڀoZb&RLR@o\)m hM;fgUkeDx4LK ke.Ѧhyж+lT#b~`486PƠgaR)3m`&5ceK2zxrLqqt+-o)Fb64kH w.ad*%@,3#7JNBL 9 1yV-}_Y+SbTn_ETN4 $4R"5cv56HZٳYNuz _rXۜ 6o廿Kvn.L(Ki$XqMШQe4,Eӈ"G&]9LuGCۧi mS\שk5'ZmLatY14?^wZBm %1#+7 *? s$"!)'[3 A0#Otbt3U/+Ԇ/S]5}I$S/r]Mܞ7tZ \(QxudD*^gçZcEyQ!75M`蚎vƄ]&ާ`{JgjHZ?9k1\3l$eNRAI}iKۼWbe[e4TbTl#l(:+-ʤ"r0yUd%$W8!)| v!~p qiat(u-p#ğ):V=AniÊ0͓}nB\ToyuF&i \x-Bݛy#XRaauHm[Sgxv:b*4GʺeBo֢aQ^}bSXvϩGJYxvzjXp f6r!F(dCKlhTs Ni*Y`'Lar^Lv!(+"ʦ1hJڰ5P #?;)%uHSm@Ճ_ʽ!j.@(紨IfFb ,%nb2U@bռ<#1m5BZ+IԋteKs}ےj-% ے]F^.ޠWE]Bdjs4v-f Z]ї!!VO顪\]]0qwQ+z&D` GY:1鄩'}-Cvbopy)7 6lGn: 2#gZ: FX;لlZ"ъӀMrbA*KJf$e(U_k@dCb+ʼn0:h-C["|[4Ee +Õsp\#F.~ pwg,F=W:H3@͂Qjlx3Wĝ ]A8BimG0yFgK#fYDA A5G4.FFO)TIpgo:mvwzBT AD \UjcZ2W(f](CHǯ:Ry[isVk< S8]Zp3]Az`u.rmt";@kN2?(Azurh  +V`-^t RTW)E0i{y%7J@8՚C/\O0^xP\m[r:6'e2Ӱe%}\cĠu,Vz u1Ճg7%)AkLv0t7*$J6cF[xv'`+3Zwg#Jqv_vwAO)$~_g%lBZPRc+=Ƕpv*3ʼn{LkZȑ NKlum$Yz Z,HdETlá=ٳ,KJDK˪*`yܠ 8jo#l(mUj!֖Ttm^ psl6+YY< KQ00#pwtXci#K!>Pn:-PD,!֪̊uKmz n;k1QZ+woKph$msYՆʇ`d6)qU{k$uߍTpn8|UNmǼN`.: -\b֒z w^t= x/v`*;*t)_$"ֽ3-U0h"X]/[y4V@,[DșuiloO_C:ߪ{}GeN>3zm{K.a{BN΄Ǯ>tn ^cf,FkwۍFlZH=MPiN4fV&E" 6gdsqIJ"\f!.}UyX+3Cà+ՇѺ=֦V9 t F˓i]`5䐵 FU{jrjޖWudU'5*r&3ft2EZO%ƈ*&߳.ܺ ;wf $ڥ̱BH>8āV%/Xͦ:2ֶ*J|  T*SW6 Irt% I4K0M/Yi4 sf+Lɘ9/I/uK1`yTgk( %0=b>+8y5{w__f\ճVs~L˩LoXhCHNj D$k{<7kHH(M}+bR\^ 0DixR-2v0]A3H`L'>>2kq9DloM}$@ŸO10a S-< aCz"B[SgɁ[3Zꓫ `7bD QMXsCH#lzɴՠ =z+?s3 [!wBK)ɂ|q5b!((Y-Z%L#?5"S8ҟVVvzjmQ]XԼX(5k]1Z,'qRDt0iEf0ݼgC)ڹ#~a! kEHܰVy^S&Fsvn*Y"Wf 'N3lm3`FjSjosP%R] l +SSiC'P8-mCmo}RF/;k\?qi49- QwM:Y^PW)N8]GMÞk|a9JN )JGTt2jܧgA0| >AGhFOy(=i(s ?i4X֑]˞E[:IJcgصGG}*$=dpY:)IbEےv~;668TH1ӣ:+ _ej;JJ\s;v@[_{y$}1GtJeBW5g+]o a\պ4S`Q _/pk ^WP*-\̻eճHY,VNJl=^pb`d1 V"Zb;N e*]t m6cQXطh 5ϫj01=xEKTMOI( q|Jh:ծ?O%HWԢ MQ&9ኂ\MS3z@/_td‰ UPsOWz|u?I=\%ݏJ0mm M5PiE)2]wX5Q5zxK/a$r5|T@Xp'mnD'cֺ!N a[RT :YYdgfς_nUZ`yFݯe$CBX4ذq?Ysf4[̊0th4NTM ߨ~M~#lI #q Y`jqFl@#sBը5_Gb)j-Rg gլA J3ks<5%Es фQ)ۋ26dAT#"\ - ٌ{50|;Ch+F˨QM 1J'9jg^Wz+s4 |"2Pđ!S9\՚xzYjpc8i"ލ8s,銽[u [AuZv}%k?Rrx L/Тa,"|9٬ڮHczδ3GK=w98`/z$t RnKǰ|;^iq1?N5alA:+vncFR|zDƍft5 (EP\s4niX 36S=<7ɘS [I5do%KLU|*U/JtK/ "",Hfb1cC䨦eH]9 i埊˴v&ɮ b&:ON ./!sᕆOH:[EΝ>-A{>sh-Q Eb2ӖOO9 &73D(2v˫`6͐ܣe‹$.!PcB$Un3t{aԨ=p4&JMcR.9Xpط;aG)u؜m-ncʔ6i8H9M6$=7mÁcō>[1m39/TA^OFuo5=yWt*V.Tg^ 8MЋvOvjr^ Ej3+U훭pbnQHOE0F`OIs"*¼{݈7=Dl;yLl1YZ=I| 8 ^Qx)0</Ӗ2X6cۼΥ6D?Rl,ܮüej[W`Y e* >]up=#uRZ,ҩa447wE1$O mp[qV0iNiO4_< m /\5U0RO.V%so3 M3Ŕ¨8l7 9g.0d1P gEH% t[sQM6evlH1ExEb4 .`ʰ^B{Xk:땯׽*3贈kHbM-m}Ig8[hLG{H-e_WJg7Rp; &/97,E?32[q.NEHXV2Z!׿#lZ2Pl 0n6Tt9s},jv;7vX}lQml'i4b,ީF?i$mSU >&'hZB ;^@]1C)nܗ.s]vnnZET>Sx۶RkZ W\nRaEڄy)OḴ NZXÖeIh1eOtam//h92ړ|[.Z[mujfŸ'r]Ѷl 5r쓔>M%H!XsԎ QFm^,Zz Dc/$!e~I1x6<\ MIg2Z(u":an[`Pӡ['Y1)*) L5]EkCzQd&rijZ&x,,fi 5]ǬC˗`R=tӉAՅ$ef_d"XC&M 6qL34swtST˘>w}隓ڨ`@=p$ 3$[NjaBƻc^䶗TR4}q! 4uZJ)=0zXHwە[VmT6@Q\ NF zw(&ѹaPA e>4y;z;e E[bzޣKG۴5萞MKT!3UC JS*͝5[3]Rp2Qi/DVjBT/d >0*"(S"6geGCgÇFbTN]ee"bCDgjt+0o$@d-]̞WO `?7lXs$l2\Gsu'uVl[Y _2/) =NC!-JCӭAînbG'nZ[1RP"0_EŸe &)gݲ7zgQVp*n o~c1ʳs-{. by"4٩ ÏJ9{s֕G{ 㹝n٩4itJ<a+c`r˚*wp nA)kK#8Vk1J,+u6g )H$Ȁmejtk'<c!25TB ;TMDg0ܢDerc<,'yZgDo:kU 3Ha甌uw*.+ NlPȹ£6o.DsA$!nmɈf;0قjD2ܶŒF2>My!G?XCzdAI=T{"d70k84J?7_RՃYH3O U^٬tv% \S.2Ag6vX3l/@{oDz6ӫ6b)te, dfիa4J,O7y_-Sǐ)U\EgXi 癛A:N#EmNXl&Zg-Hv] rmٻqUe5Cv{7L^3(q]X"P;Wo_\%;̜E aѥْlm<(tQXo_>h^7Cf:iMy20Wt=*ՍOIg _܌$[ 6|/+Eny(yuZUJlJK%"RUEO/Zfմ7?]NW-XmKzme1Nn# 83U]k_;r@3l/|ӨkD˷gؼ@.J㻨H^YR59vn (yS$oN6pI6„{a6If^NOx)lLl v w0E Ve7ɥfU8g 9X@4rw;ٌy\'P`D<!%irP*ؽ{=+,FBͬVys1b5Dmw;Oj] tk`H(TiQ=zŷok w$?fOil  :HAx r%xLm?l, r7of11̔ :o2$]{GdB37)I-eSjI1][dq:Q|StӘL Ȓ"{*SYl^D{sM9[$r}uO$ o#ey&vd7F O?aW|Vcsy0#NIM>}JF]bzvDb+F3IBNIRQ m 7fd'>փ.a'ZxR+Xo/Qjeoriabxy iF z{/,\vuR^5$i󔞷 R_ƁULdsI B-`63ޔv`AmrK=0:jtks^@gЬd땯Eܾ\~q8&9eNjn(Q2- xfx#Hڍᩥ=͐rR@Lj3Sv:U 7b#2&G穲DZčMreEźNQeK]TV.chBk mɸm8~ ib5cE$_'77{/g x޼ 7QjdҏLZm䔶]Ľp;8rBʿ5Y;24@?:uS%PHW=ani^khHWNv4^V@j+6%ޒ4j.2׈su kLy "^0]@f~w;Y)]ޠf"c;Cs(o4mJ$`!7cѹc :|3C!O삈:lBx9exR;`4j v~iaHL"ś+i?m(/ɸH1J3oH+DЍ31 Lpr+˹8^Tze [HEMqFB6$ ϲy)`G$bP,Fܜ(a|LjXCFL )u}0ٶ )MأI!<Ŭ4$"1^s o^!ZƩi%`صP(:@jmjhdTٳGc7}5zdoLbP h# &#^(NY _܁ASGi8H*WDu e@ӛ[; ͢%š%%<% v"sDr4 P;wqkt0,/MƓple`Dt _Z!Q7ar RY_OLÄ*mׯ}/P4].i73S:uvtaEFۃ;t9 3Gzf?ŖLQ# qފE\հ)`Vʂ:~'aQu,["kTorU>y[#7#;=,ICH|)ɓafH,Ui*j9s!Ztc6"9Dr;Be*G&aK؆НR/wznj_Z+}q$[ Fń͏5B%4l—ivT:s׵w38^Ȋxz8S+Ok|6'9ٯC+bwo:zVwN\i>EeyO8\Jȍl%X&"%WjIa U0, Jkj Y)9ROg5URDLfDJlfSfJJ=L8%+F/P֎TajZelpX׈a)цsΥɉC3,A|zkH+Պ^-典"/Z}\A;0O7ƒ `$FMzY P)Hl[nEiK\#k0F?Il:/!ǽ;њR~u=l9X8 ;SNs.z,\o?mwN"8R--fqs+;J?hIm|HkFOkbњ8R7_!r ͊ظ)7 &'tE+m[1/ܢG܌⮎WD ]1Z~O:?[T=Z3C|6n꺈ٹ vEy d7} ],"U}:6G%4δowKM؞;EgmFZ")I8+& 6%['i.%IUoo.Dꯊ5h=-Nᦉ]bá8zQlcq`cD'pܔH ͪ0142SAȂiAJ*_ִ 8Uim&w6aq]W?4F\*lފwlk/^0 %G oJS׿ Vֆ<ᆘ 3NhI- 6=H)["q0Oo30Ȉd3zlt~*ffn.\>.ѕL ݝ3: B$'Xi-@k$y;cDFNJZ 6jrV dO)`PX޷E K'{53UL*;y+=6S|"_5M .~MxY4/?BKCpDw{kŽꄎ/&͢vGdCł,4YlJxn=Cw9B!Cxr5=wQ{Eì)#'h{1DXO;i[.qH.#Oϔ`YY(u_aVzx$+9n)ԭo k'an'$:s;I zzڶw h[[mVoMs2'+Ek ƳغJKX ߢ\ >r\oa.u Ӟq]74oqKȀڐ8nrk7wv_֘ÙfQZ8$%5(9#c,ZI4[[q8iUzUֳp] B6ͅ$J$KaZ>I'O(]ɱ;>-/t 0m.WuUM+\ؘݑKײ/eĔƷku?iTpq$#iYKI Ej4p،sB3LY5z+:^*uX$]D|+oq ;dKz+q&W-$f'1p2{!]֠ܤ:r|bk?uh_ @ i-lYRKٌЙ' 5{\Tbjc kI?P,97j wP>iN 07V2?ܬ^ Z=lBͧðs "a(^xM<\Ջ >)3K=ɋmM#>iX8/Q9geP$dxI6T}~&'ߢ_ P|2|w ĭjs_?:˱FAHԱv˔icdgQ"EN̹o Hy+!ev+̐丨`Dn`r6#$ퟱ&)GF|\JF}bh͈=ʂb8W]f^`_Rl,_S¥)hhrTqw dqVVeB7{|a٪zpC˖Nz^,.rigNRs^syr{vqO&fW 7uͥρ{e彞b:res+2U}FBm`} & c3.S؉|JuyD$.g(cg֪mbMWڀmO#Y℄ #z<(6vv2Dk͉+3;qT\붹wKt/GF0Xz:$KӶEl؆[:FI!ݶ w2|x=gȼ/korϺfZI*iNN;+K~V76. BeH'R "Yp;fXW Rr.r!nd'$t9kef3#T$}u$ M<8(7\kaFJ& )݈v&zu񘙾v:SWXy- 4^muU<ҰG}D'Z'w8ZV:|F+#(jA%TSp__!@&-:3+>i冻$|;Cjk7'"ZP9/y3XY V[9똑WUm) nI=ev f'Ǒy=Q6٣cXqzRy{9Zm<-odz_޷bq:HV$-@$/3p@V>Bȋф/$1]9q= Gc6}4򂗖I,,o&Bu 6FاOӢL _WX\l~ovR*_>~=iN7φUiWPzhgҹIqٌQvq|=׬ق)U0vc痌M*LWT.RqpRE;}93~!6E8v7t8g W>2X_PPV j%br%D[5sdә4""Ĉb߮%+I \4,)*ܒQ"ba=Q6mFr59txMVxݍ@ 8j IDAT1?cH7-{|!.1eq:DK?kQF#PJE+*yPn`^~B4@F-{NEiAR:KK7>B-W=pi3ܒJuO~9H>4VzM-]b*O{ZqNēWSi3AI-׾̛|_l1E\l{jѩr'M)0`F7ڷ+$C#1E|69(ʹϰ51]7x8ڀ,Vo>'+/m2mJMHwo>̦/LQ [PXD/y* 8o@ߒ<$aT ZsA, zqT!8' sȵ'k'1ŐCv 63i)8\%G.g(nSV" jA Ө[/(yޚbOdN,+QQ!s;EJ3zU6W@mI]q\Ȣ>y(CJWi ,$V?2,/ɘ Wƫ`29Kki 5P1z-jR1 ZSSؤҚ,::`y49ZAJ6ªFK==m LiAKG"عE0ݛcV9IPu4.27mKs7Qפ ПgtPPDAwӭx0Î $ļX|Ϙ/8Qewc Y,ߎH,M=ev)z@d6C9U&ITס0EԧMM;_jK=`ʔԯ4--Q$:i3U e9‚2wC38yJyH%dX;8wzIܳ4&"!sZ70L\`_Ǟ;1.\0.uy ,GʩtTϺpvYj`yGz(<ݧYuo[7vYu"fZTn3ؙf#tQjUM۶^@9#ÚisY\;VQRY'gJN~AI~,lN~w0sN`֤XdAEfynQ^\%$}smyvM㜙I)2OJfk=>w\IĻHN&)+#<~?TxkǑMxx|q<@Ԛ:U|&tq`Ea-`iA-h|$I':Yւ9Rcqghy1A3tk?!kU,Yo<;l<5zHa[C- ^pN4k=9BalB KE ^l"^By{ VJMcF"ؕ fɦ 1*hekMs)hZ$Ii?ÙMH 5/-QnZr!eU:.\1~e.aӿ%: myͧN]\ÅʏȌ//v"ԑ O1ZH F(dK%\m/wVTidXmG͹@e>jyRqS`Qٵլ(ͻuwDK8B|A,MnNIyq A-o8u<|| E␁~3sGs7!/!5d)^2J+qt47"׵‹_Tpbxl>+G!OLѡ<Xm͆‹䞖ͷrfWeSztui.nv a"/wn}4­|IJSYby_n[+<ĸ(ͅc ;\?<>/b p]5Ĭ3Ik )bLL&AJc,4ř,NnWQ@u=nË %.U5g'-\K(ce5`mЬє@ݦeUZ3ؓR u7ѐ)FqLpREm(jCv[&SYoxW[SxZ̹S0Lkmp9\f?V9 KR4+Ӻ&DKٽ :e m0oJLOX.^N5 a:u?;?{E#V\x|oW<kPgn;腥dHu(g41p/jwE!Jl?ݞb-&7-2ԘBbū5͛=N5-TI N!L' SqlX&"Raga~@/&٧Mrb=n6} v^׼{y|n-d,PZaR{,[)F{X[;t_S9CT) b I3ՙfswA5wuv; eD0_Aʺ&6pKGJR[%V*./_yHaW@M'wMQuY-yr扡i($:I. # /cL#z@6/D䚞_{.fcb`%>pkjۅ =x=!j٭s=AukT1Ù-WEY_. ͋TB? p:d܉ЦNGVz'qJrԴ U8ސ.xxx P~o<$5'+S:ф|jު4NY9/7j2M2Zl]o{ |?VAW(o}s9Q{7! `z|gȓ7Q/-<;c%trH;DNWe`Iݰ*5ByvHv\b1W2vuSI!4?¾]n]cq*"Q@ImyJ7A_ _bnmLw2y\aPRilW$Ӿ7$qB1_h~ 0瓳9a+³Sr<Ʀ c^ob8 *)–}B]=l'%3Gl['{z =A>79@Sx)u0;RWo݅q8lw̰qyIhm/k(rѽ|XRrZ7b!MH)ZSZS8 Nj6@ X3h+%ѺҾ})+,;(z +EFۜd)=ٵg'mo|L%gq`nciuiȑ$ؠ$WuT=Ż`8~k )fߒiwWI :4g #~)n_7-ߑ ܾgIU|kry\>}O*<7IT4'\De6 lt8lamĞt[B8D:/:ͬu#}B!2Ȇ/6 =ӵ-\`gf2(+SZ« 8lw"U ݢ5MR”S7SaIu]j-˳d]unncэ @8f[  #Bv L8 ~gk "Yn,=#V l^ËL\ވ\p(0 ߭vGH;q4(mgIt֊OsRHxfjv\ kZ1/̝C();;/ڽ/C<ҶKh÷2 x wHhA!j{,V39:#.)hڼ @!"I+[eoԼסc[Bu{EX7䶖Jo~-6l5aTYpn:5m:g]П (??Og8n~xG[7 Lh5$iIRwь;4vQDmʜ:uRI⓺,mQSEEй{c Л.WzV(#3ľEmT;÷kҡH'8+JSzAtz:Yv'3.abM{yq4{*2r=y=EsƼ1e(Ma?Dm[[m_0/<'=c[ϮFDjT8Z4»w1>i(tLy?u` F-k Yy$Z)ݵRD-̪rd{=BjtaȵIWE|Xh`޶SSul4- t>[x4߽זxL߂Sipgp <'›BC{vXt+1c<= 3(>NkMW Y`b9:eV4yuLpWL?<2>8 2!:¬xY-0|H71wOL!0||ŗ/Jwf̉SI8T 3Vo,)٫ZYhlPԵ4$^kROb`= 6IE N. -6D G&MCeYԒl{gxi)[ȸbvƊxDu9 8gelXn 3)'qm>WNL[Zؒg2Ns'˓,M-ƒXic@e< ވJIXLU4vՁfP)~76cFƷVDY+U_uVX[nsRԧ,ll9~2b%RkljۛCL4QB-IE+|V<:PEȈ|݀?Ȼ38zVocTY^x)2 VuU:员T!P7DVrئ0lm%XB1Av](ʙn. nԫÃwDg<. I ĢX1K\&|}: x&x*X~XZP=b7X,T*@Dka({+ڕ .<\ 2.?p5J8-\p[[Pc#6PA؋#ti"M.btnҩI Qzv B@qtJTLvP(Zfd#VWvD%t$5Ba2.SNu=kYk{HPeT CGt!c\0. f<,LXCDZpſHIϏO  _ A6`y yRJ)@I VB_Jx`{ٔ@Z)7RB ju`JGz jM ӓo3'>/]J $ػB29gyQw -z^i_wU ѐ|*CuW:ԡqt1VW%4&< ITV\=kVqy=޽'\&f_6IJf8 lZ.q tVk|1 Zqx8mP:Fl&W"!v mDCH\^{GV5F-֚$e4ZѬ&=,7˶CR_?}>:[5Kl*9HU>}iD[dדO~4P#ʞ>6}e[Mx vs\a^uj$Nmy*S0m,Dt=%}QnsluۭG+UN '5mŸ&˨']DV֩ݜBn*5Gtݠ/u5r#=V'g'CPe`+Ȗg$9h_@t&r*kP~*/vgr> fd\[;!0|UڰxcD߻"Zezl1wqնZ!nLo*mFt\8 *B`ĄGr MIDAT?ӯ-?^H|x)\b.c.O)jg)]&J$ľR3kk^CZ=?=N@)K¾ڐ!ާs6hl#u9ƕ[{s2Z!;="l]z疮Nɉ{-n# N#{~dd[%db`>}TrQyBHYQ1%~oQR[MCu811~ p%ݵaо 嵻+FG߼;U A]N8Mзo| /mD8O<8eD;/`:?hʛA\Qg۱Neރ{-"rs.3-"7)Pn:M{|+S a?i*#|1OIbeBS l s1b~[NV՛m@)B){J;!϶m31^~'^O;HY^eAm<9w'wos|#6l~>3O7nU^CmNJ[&w>#Z045ǸǮ6`bi3;uS \|3n7wV=uԹ9I7]_-^9#p98ķQEvY F_\s_W!\}$6~>;5{൯? LIENDB`kraft-0.97/styles/pics/sticky_note.png000066400000000000000000000263671410616450300201200ustar00rootroot00000000000000PNG  IHDR/E!bKGD pHYs  tIME 0xiTXtCommentCreated with GIMPd.e IDATx}yeWY;{PS*y"1B2 Q\Цmn h+6.1`;D"KEW TU*U7ws?>ýJ kzN{M"qUxj@Sf|LBb|cQ{^`F`_ysк h\dKupOps5bFG]nx.Р-<`^5^' 3 ?,8; 'G׀x.<%;"b{pi>{! h=8?`n?pW |{;~0vvęұǿ}߯ ܼOt_bo[a?p ;x7Aݿ$ 7n~T'/|4<||7^|˵xۻz'ᝑu+?/3 oCԋoo8pomxow':N@"?>td1ț!nR >. L@ r v*n5M>v .`ؙwapr1j eCGҗb_c/e7!{G=Z#{w`8Gk+ c'wܚfVkǮ'Kw/pذ v4,im0s[JZg kH=]_E2t9͇=.؍ sO?X{D~ 7,| HY_ȭrsnƋ~Un[o~}ⷿpo?G{ׇÛ-0_|g=g\+G>i\fthiڎ`˺FߙsFDH۲9$)hYdMİ1r-˹aas2l sh?ie*yp~,U"KvFj6 RrJ ڪa+S RîdF:K 9Y U8ZцRR %DD,kPА"b@߇4x$x&D.wV`2=u4U\w͓+.@2 =_5x {Zw|'^kƓ}S7~?7sOmzf .o4^䋭AwllFn4WY&60F7Lmê͝i5ưѝKsMq.wvȇ6KQ'$,ܙsgI1RM& ˓Vj]é1Jդ\r' Ucu̝Ebֺ4W3 QQ5jM9ډU-8%iL  P(#ǩ0 &{<އL;.. (B[- W{x +,37욉5{o~]-wb0=.91V\Y7 [c!΁f^fPI) j,L!i $("ZkBzmK}[`p^_=):-5yssuq>)~ǿSb8VuZ75Dn8>6;\#gz0rFnxVɴavμ_Hf6hKTfgR48c8((j BԿgU_'˦G89ī_<LPJ&aBIN'Ϫ };>sHIrQBۉw|bsFMGbp8'? W`:X6'{I?jyKiTٽh~lޏ@kE/Ns6o}@#}Pyחs _}tI -rg;ե]M RFn N?~<۳6%ӈ/*Q3ג;>rxK%BLK DсH;M2; 4*آ cF=?'\z=sgZ?}HlFr9|omC$eD:$E"KS]Gh{m|6,8>iqj:hVo#1h锨jCD\" +:7rvB`sVD[F_H5{< y3ۧ)hXIRk[HD%D99_5F|{EA+)!99ܒbb@ -Xiv©qlx@lhC%k3<L*۫k\"o94с "xoJ]'Glf0-#v^.q[ĶkQek>- ^.b}>ƿ!dTEfN: |4vhOSdq4"$k_v㒢dHQ9%9eSę-Q$IQ |Bx™M_tΒW@A{=o ^[ V' awxtpԑV 9k!h3M}6h v`}pn 4ՙC|"'y]X3K"D _@Sqk|[;`v}Ոv~?EEfaK :$֙)جDyr~{+Xa|Ja=v_N$pn` emę ܹs ~xT=@1xb d:gto @Ufa Z;7R$O nbtv|pb#kNsWxj9J?q;4 Rю#Ery͋U NV|)G> ]ß 7<Mr"ҧ\ d1Js`.'i*;w.Ca~ r&60v^FzM/aMku9.L鵣]}Ԏ𿩓7w\{^`u9Ω?0-^sE|?>3=m.ד͈۶8(,< D^d?pqX#&C= #Ab\'L' }x3 uH[rhַܰ.C IqHnkZ; ;<%9ߛ{28\CO}}[qV)Y66+Z]Ͱ+aZ冽/g > &y@]ز2Ho@6FQ{πNFn}p0YT>k|r-]Ro^y=k nBŖU|{~\=+|#kF!0t 2A0#/4m""ۃrh>uv0S:f>raDrϐoZhn|޷mA𰫎lΘG.+'r>~B8?}2|j];np8-6K<1lX{jKF&:m\=xB{g_eDWכ`!=6{ G#ێ{S K -"N"D}Z EK/7^ ײ8~LkIc蓐W݄b3{FA%@woSpPĒlgC6H[KtZރs>p#',bO\-2Y\l':Q|qe}V:\rkqtkzD┚yV6p$Xv NؖLav'bG8/g_^ֳڤ\܄˰߉?0:FXnzؽwoYq*ɰuuB$cc^ VpUκ#t92F]5ڿ<$Agqh4wccy?FeSm~5W;|tV2J==<WsPi[إfOfg%]w-vg/,.d.Q*l&hjN]]6@\9O`0C5b@prұ T’_W  bn/z9N=JG&S[cBM#)$!Qό%Eqs0z$\w;*&MZ . sKW{rG<&}~4xm\TU 2DlY:f4/f=dSȆмkQ=0&<.t\h4ˆ]Ñ.a%rM$ "f螜ƤhC{t iB4о^8j~)Ui$4N~Hs87ipQB#Yl47rNB4}},G=""bv|ATA?lApU,+q1YDlt4gl'?SFѫmBto<"b1t6SE!O閩$\wJ)JJ 9ܸf$&M>?5'ŶQGD"rH4IxUma2M,/13h&YJʇw[*Z& ]FDDH:nWlCF(*MLEI Z&IL+m?NpEB&F#"fV*3⋮7=Ɋh GD,RlXI;-|p/ X䱅.>-li2"bLa7C_~~ rTZ`1shQdosE]}H5Yo}YTZ{XI.z#B\ JH(&3ab0/oCb*PBX,GGD($5=\-Ozkಔ; ]4Q{GD̤4l..9TMbE\5V|Q ]b[9s Tɇb5YlƈSs pmj5~2%U4ՋN3h;Z6s3S[QeCMؾ(R1[> M2U3bwVW| AH1]1C"5(f!>5]{V{-n2PUrm1Kp4Mڳ0݌I.ŠUe@bjDLŠr4lrhWe1}[a?hc W(E1s6:rF۳i̛ b$Te+]RVy!esYA2P|GD2gc[nAjK-7V1""b|pi6rJ;3 z-V=jYRvs$X>|tSy@)춈_~p|({5Ƙj%&c.zDlB_pLp{\IP)E|ؘ"5:wU3o/8f,FDDY1Ʊ^.(eO $&DD̤n6ngwڊAusBWP>xDLjp8wF1𒡪wb?DDD̖|n{vL'CcX6['8؞wE9| U#"fưuleWH[×{G9@M#];Y_=9o[T7=ckFD̜dKi \vu[\֋mP-K::)&̤VE$i5=x|F)P4ƀU7l d>s`m}e6gܨ[yDlɱ4eR5dhSm!9\iwwTӅSfL Ӽsc="b&}p]G.YM!{v x=mB)6dʢ*!DM T{,fy"'? Ȋ)2(Dը#"fRgQixrym ?\-w&#A3;"bF);:# v⽿vfa3٪3c R埜r$ֺ ,$xDČ83\oO~ZYݿVD 1{>fJM%J+Ld2D="bn~i qp?2OkkdSDĬ*6. |n3 Pþ"h30[O0 z $&K#"f͹9Wo^G!1Fш51ȓApQ$7<ܬ KHh쎈EzS0wqѹo|;Ա('7)"40ԣ1>xYiX\ʇx4S-K$U1$Wę7>癛 ^Y_mRJ "bF 1n$ 6S NHU+VP!""bаn~;G7\UypAXYI. ⣍1{ yN- +G9ȖcB2< }pT3Fo 59e|r<۬˜ڮ&"RB#"f.P l&xHs)ׂ%Tz&ygecCX&]Mt*" G 쎈Y#=Sy{vp4]P0CAyD̢mEI,Z n O8}5s#鴤c>2Jة^1k&:&U;DgUeE2 }jDDĬ[Quqq>y3~XDXmĆQ\O}[vUE}0OKLrY]2?OTl"Kp3`͇*N~,W3!F7=fuyoLpkl጗I1>xDL6omfK(*PZ4 OIDATTXƒMĥy6d4޷ħy-NSE|SUT9{&[<*O} y 8?$]Wu 'oB@gk=Rǟn@Þz@z9&Oٖ'on}a\4D ~R`40 @(HUВp4HhhHhHw0Z`0Dү_q[7܊ FkhHuQ@u&ydb2Ș [jD fNכ$$k~_e!>H k&Gš[gME_Ô;RZ;ߛ[CJbpKq'<~+Mqoi{-j3R_ "$ HRjTR\P| g(*UP)އvP2\:M0$+c-<9õ˫5 v2P) B <*FQH*EaFUP<DS*l/|x{w܁'48t$)j)I6_HϢ IG A`2rLXon`NC^{ NubDu("# ?0I4}wz!^쥬aPt(waudDp4~N4E\ho\vcP"2q5~LH& RQ-`^L1qFT& re"+JvJ5vVRn]xz[o!nșE=?R 䣲׫{6G bD&"W HD V[\֗}76k O 0d :x1RZR9RK5' H*!0’t"jTVZGCDƺMPE{J⥧!2g־dLޚ`'EAP2Cz"` }C(31ƑÑ6㓾gT }"T@ RH;ƅ:rvoq#` 8]Ndڰ|SºFv" Ԝ˪>JaN5fzRNl}H-[ Ax{x1fJJubdjnZTlP!;юc#h2hfψ9pr*D,f'C&RΨ~s`lƘh-ͤLi/U )h)\ Cx$@@IbUsCxᓀ|j5 9>uVH9&4h*=ͪ<>(B/I-ae-SY}Dotmd2Y5\h_PjKPS 1qRos| 69DBZ~V-ڵY8zxjCN6=Jn:W܍ 1[|^VpX\aOFԷ ;.MOy׸i<ʓAM:+ G$su%v.pLIrCX`f0G:  kvH!ʆT8JTl5k(yk,C*9js):\5Tb\:+cL"T}&"K_ jEK֏ԨR6I=,mV|Lu|yݥI\;N\1s du'I SbT] iւM9Ab$J(OurN)$XaYM *`'U+֕̄Ԧ1|:QIb8'"M@lHEN [Лnh.+h+ʸ5DuŽͳTؠ)Aq_AX͝v_I#\B Mt$Awc&R/dU1,Uy Ԥ.\ǫYV1b(T 1˷ih>j}`t"i56kIq:/@XmDZF !@!uz IbG6A`7S$˝H.b֍1J Ї %fe|- q[#9&L54BcTq/oXK I^uhB`,QP_HM3FoK`m޵Zƈ2? 㢘˄[RLMr7X^rPub꧜ UDYAO%˫+dI9'ȹ;fuD $cV̄rgftyJ]XbdžBJ)2qf" ݵ]?B 5ȼ"I 4KJBS4O2 Nfc.26ZԱ)*)w : GB0B7daV&?Oz7yέԚ  MUK{@訐 [@U1ͧ|T~U94+8u0&RZe $\ZL H iR,殺I x0Md(;N}օ7 #include #include #include #include "doctype.h" #include "kraftdb.h" #include "sql_states.h" void init_test_db() { const QString dbName("__test.db"); QFile::remove(dbName); KraftDB::self()->dbConnect("QSQLITE", dbName, QString(), QString(), QString()); SqlCommandList sqls = KraftDB::self()->parseCommandFile("5_dbmigrate.sql"); KraftDB::self()->processSqlCommands(sqls); // modify the initial attributes tables sqls = KraftDB::self()->parseCommandFile("10_dbmigrate.sql"); KraftDB::self()->processSqlCommands(sqls); // get the followers into the database sqls = KraftDB::self()->parseCommandFile("8_dbmigrate.sql"); KraftDB::self()->processSqlCommands(sqls); } class T_DocType : public QObject { Q_OBJECT private slots: void initTestCase() { init_test_db(); } void checkTablesExist() { const QStringList attribCols{"id", "hostObject", "hostId", "name", "valueIsList", "relationTable", "relationIDColumn", "relationStringColumn"}; QVERIFY(KraftDB::self()->checkTableExistsSqlite("attributes", attribCols)); const QStringList attribColsDt{"docTypeID", "name"}; QVERIFY(KraftDB::self()->checkTableExistsSqlite("DocTypes", attribColsDt)); const QStringList colsAttribValues{"id", "attributeId", "value"}; QVERIFY(KraftDB::self()->checkTableExistsSqlite("attributeValues", colsAttribValues)); } void checkAll() { QStringList allDts = DocType::allLocalised(); for( const QString& s : allDts ) { qDebug() << "** " << s; } QVERIFY(allDts.count() > 2); _docTypeName = allDts.at(1); } void loadADt() { qDebug() << "Loading doctype" << _docTypeName; DocType dt(_docTypeName); QVERIFY( dt.name() == _docTypeName); QVERIFY( dt.name() == QLatin1Literal("Angebot")); QVERIFY( dt.allowAlternative()); QVERIFY( dt.allowDemand()); } void checkFollowers() { DocType dt(_docTypeName); QStringList f = dt.follower(); QVERIFY(f.contains("Rechnung")); } void createNewDoctype() { QStringList f; f.append("Angebot"); f.append("Rechnung"); DocType dt( "Test" ); dt.setAttribute("myattrib", "Kraft"); dt.setMergeIdent("2"); dt.save(); int num = dt.setAllFollowers(f); QVERIFY(2 == num ); } void readNewDoctype() { DocType dt("Test"); QVERIFY(dt.mergeIdent() == "2"); QVERIFY(dt.name() == "Test"); QVERIFY(! dt.allowAlternative()); QStringList li = dt.follower(); QVERIFY( li.size() == 2 ); QVERIFY( li.contains("Angebot")); QVERIFY( li.indexOf("Angebot") == 0 ); QVERIFY( li.indexOf("Rechnung") == 1 ); } void addAFollower() { DocType dt("Test"); QStringList li = dt.follower(); QVERIFY(li.size() == 2); li.append("Offer"); int num = dt.setAllFollowers(li); qDebug() << "oo " << num; QVERIFY(1 == num); QVERIFY( li.contains("Angebot")); QVERIFY( li.contains("Rechnung")); QVERIFY( li.contains("Offer")); } void checkVariableReplacement() { DocType dt("Test"); dt.setIdentTemplate("FOO-%y-%w-%d-%i"); QString re = dt.generateDocumentIdent(QDate(2020, 01,23), "TestDoc", "addressUID", 122); QVERIFY(re.startsWith("FOO-2020-4-23-")); // the id can change dt.setIdentTemplate("FOO-%y-%ww-%d-%i"); re = dt.generateDocumentIdent(QDate(2020, 01,23), "TestDoc", "addressUID", 122); QVERIFY(re.startsWith("FOO-2020-04-23-")); // the id can change dt.setIdentTemplate("FOO-%y-%ww-%d-%iiiii"); re = dt.generateDocumentIdent(QDate(2020, 01,23), "TestDoc", "addressUID", 122); QCOMPARE(re, QStringLiteral("FOO-2020-04-23-00122")); // the id can change dt.setIdentTemplate("FOO-%y-%ww-%d-%iiii"); re = dt.generateDocumentIdent(QDate(2020, 01,23), "TestDoc", "addressUID", 122); QCOMPARE(re, QStringLiteral("FOO-2020-04-23-0122")); // the id can change dt.setIdentTemplate("FOO-%y-%ww-%d-%iii"); re = dt.generateDocumentIdent(QDate(2020, 01,23), "TestDoc", "addressUID", 122); QCOMPARE(re, QStringLiteral("FOO-2020-04-23-122")); // the id can change dt.setIdentTemplate("FOO-%y-%ww-%d-%ii"); re = dt.generateDocumentIdent(QDate(2020, 01,23), "TestDoc", "addressUID", 122); QCOMPARE(re, QStringLiteral("FOO-2020-04-23-122")); // the id can change } private: QString _docTypeName; }; QTEST_MAIN(T_DocType) #include "t_doctype.moc" kraft-0.97/tests/t_format.cpp000066400000000000000000000017611410616450300162460ustar00rootroot00000000000000#include #include #include "format.h" class T_Format : public QObject { Q_OBJECT private slots: void initTestCase() { } void t1() { double s = 1.0; const QString sstr = Format::localeDoubleToString(s, QLocale::c()); QCOMPARE(sstr, QStringLiteral("1")); double s1 = 1.2; const QString sstr1 = Format::localeDoubleToString(s1, QLocale::c()); QCOMPARE(sstr1, QStringLiteral("1.2")); double s2 = 1.43; const QString sstr2 = Format::localeDoubleToString(s2, QLocale::c()); QCOMPARE(sstr2, QStringLiteral("1.43")); double s3 = 1.334543; const QString sstr3 = Format::localeDoubleToString(s3, QLocale::c()); QCOMPARE(sstr3, QStringLiteral("1.335")); double s4 = 1.50; const QString sstr4 = Format::localeDoubleToString(s4, QLocale::c()); QCOMPARE(sstr4, QStringLiteral("1.5")); } private: }; QTEST_MAIN(T_Format) #include "t_format.moc" kraft-0.97/tests/t_metaparser.cpp000066400000000000000000000032441410616450300171170ustar00rootroot00000000000000#include #include #include "metaxmlparser.h" class T_MetaParser : public QObject { Q_OBJECT private slots: void initTestCase() { } void goodParser() { QByteArray xml = "\ \ \ Progress Payment Invoice\ default\ en\ \ PartialInvoice\ true\ \ \ RedRider\ true\ \ Final Invoice\ Invoice\ \ \ "; QBuffer buf( &xml); MetaXMLParser parser; QVERIFY(parser.parse(&buf)); QList list = parser.metaDocTypeAddList(); QVERIFY(list.size() == 1 ); MetaDocTypeAdd tadd = list.first(); QCOMPARE(tadd.name(), QLatin1String("Progress Payment Invoice") ); QCOMPARE(tadd.numbercycle(), QLatin1String("default")); QCOMPARE(tadd.lang(), QLatin1String("en")); QVERIFY(tadd._attribs.size() == 2); QVariant rr(tadd._attribs.value("RedRider")); QVERIFY(rr.toBool()); QVERIFY(tadd._follower.size() == 2); QCOMPARE(tadd._follower.at(0), QLatin1String("Final Invoice")); QCOMPARE(tadd._follower.at(1), QLatin1String("Invoice")); } private: }; QTEST_MAIN(T_MetaParser) #include "t_metaparser.moc" kraft-0.97/tests/t_unitman.cpp000066400000000000000000000021601410616450300164230ustar00rootroot00000000000000#include #include #include #include #include #include "kraftdb.h" #include "sql_states.h" #include "unitmanager.h" #include "einheit.h" void init_test_db() { const QString dbName("__test.db"); QFile::remove(dbName); KraftDB::self()->dbConnect("QSQLITE", dbName, QString(), QString(), QString()); SqlCommandList sqls = KraftDB::self()->parseCommandFile("create_schema.sql"); KraftDB::self()->processSqlCommands(sqls); sqls = KraftDB::self()->parseCommandFile("fill_schema_de.sql"); KraftDB::self()->processSqlCommands(sqls); } class T_UnitMan : public QObject { Q_OBJECT private slots: void initTestCase() { init_test_db(); } void checkUnitsLoaded() { QStringList units = UnitManager::self()->allUnits(); QVERIFY(units.count() > 0); qDebug() << "ALL Units:" << units; } void checkPauschUnit() { Einheit e = UnitManager::self()->getPauschUnit(); QCOMPARE(e.einheitSingular(), QStringLiteral("pausch.")); } }; QTEST_MAIN(T_UnitMan) #include "t_unitman.moc" kraft-0.97/tools/000077500000000000000000000000001410616450300137205ustar00rootroot00000000000000kraft-0.97/tools/CMakeLists.txt000066400000000000000000000014351410616450300164630ustar00rootroot00000000000000 set(findcontact_NAME findcontact) set(FINDCONTACT_SRC findcontact.cpp ../src/addressprovider.cpp ../src/addressprovider_akonadi.cpp) set(AUTOMOC ON) # # For now there is only the Akonadi based address backend, and thus # the findcontact tool is only built if akonadi is there. # If there are other backends, this must be FIXED. if(KF5Akonadi_FOUND) add_executable(${findcontact_NAME} ${FINDCONTACT_SRC}) target_link_libraries( ${findcontact_NAME} Qt5::Core Qt5::Widgets KF5::Contacts KF5::AkonadiCore KF5::AkonadiContact ) ########### install files ############### install(TARGETS ${findcontact_NAME} ${INSTALL_TARGETS_DEFAULT_ARGS}) endif() install(FILES erml2pdf.py watermarkpdf.py DESTINATION ${DATA_INSTALL_DIR}/kraft/tools ) kraft-0.97/tools/erml2pdf000077500000000000000000000002551410616450300153630ustar00rootroot00000000000000#!/bin/bash # (c) Klaas Freitag , 2010 # Start script for the python erml2pdf # to see error output, remove the 2>... part # python $0.py $* 2>/dev/null #kraft-0.97/tools/erml2pdf.py000066400000000000000000001145351410616450300160160ustar00rootroot00000000000000#!/usr/bin/python3 # -*- coding: utf-8 -*- # # erml2pdf - An RML to PDF converter with extended features # Copyright (C) 2003, Fabien Pinckaers, UCL, FSA # Contributors # Richard Waid # Klaas Freitag # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library 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 # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA import copy import io import sys import xml.dom.minidom import subprocess import shlex import tempfile import getopt import re # StringIO is not longer separate in python3, but in io try: from StringIO import StringIO except ImportError: from io import StringIO import reportlab from reportlab.pdfgen import canvas from reportlab import platypus from reportlab.lib import colors from reportlab.platypus.flowables import KeepTogether from six import text_type from PyPDF2 import PdfFileWriter, PdfFileReader # # Change this to UTF-8 if you plan tu use Reportlab's UTF-8 support # # encoding = 'latin1' # use utf8 for default encoding = 'UTF-8' # # from previous file util.py, imported for simplicity: class utils(object): @staticmethod def text_get(node): rc = '' for node in node.childNodes: if node.nodeType == node.TEXT_NODE: rc = rc + node.data return rc @staticmethod def unit_get(size): units = [ (re.compile('^(-?[0-9\.]+)\s*in$'), reportlab.lib.units.inch), (re.compile('^(-?[0-9\.]+)\s*cm$'), reportlab.lib.units.cm), (re.compile('^(-?[0-9\.]+)\s*mm$'), reportlab.lib.units.mm), (re.compile('^(-?[0-9\.]+)\s*$'), 1) ] for unit in units: res = unit[0].search(size, 0) if res: return unit[1]*float(res.group(1)) return False @staticmethod def tuple_int_get(node, attr_name, default=None): if not node.hasAttribute(attr_name): return default res = [int(x) for x in node.getAttribute(attr_name).split(',')] return res @staticmethod def bool_get(value): return (str(value)=="1") or (value.lower()=='yes') @staticmethod def attr_get(node, attrs, dict={}): res = {} for name in attrs: if node.hasAttribute(name): res[name] = utils.unit_get(node.getAttribute(name)) for key in dict: if node.hasAttribute(key): if dict[key]=='str': res[key] = str(node.getAttribute(key)) elif dict[key]=='bool': res[key] = utils.bool_get(node.getAttribute(key)) elif dict[key]=='int': res[key] = int(node.getAttribute(key)) return res # from previous file color.py, imported for simplicity: allcols = colors.getAllNamedColors() class color(object): @staticmethod def get(col_str): allcols = colors.getAllNamedColors() regex_t = re.compile('\(([0-9\.]*),([0-9\.]*),([0-9\.]*)\)') regex_h = re.compile('#([0-9a-zA-Z][0-9a-zA-Z])([0-9a-zA-Z][0-9a-zA-Z])([0-9a-zA-Z][0-9a-zA-Z])') if col_str in allcols.keys(): return allcols[col_str] res = regex_t.search(col_str, 0) if res: return (float(res.group(1)),float(res.group(2)),float(res.group(3))) res = regex_h.search(col_str, 0) if res: return tuple([ float(int(res.group(i),16))/255 for i in range(1,4)]) return colors.red # # original trml2pdf starts here: # def _child_get(node, childs): clds = [] for n in node.childNodes: if (n.nodeType == n.ELEMENT_NODE) and (n.localName == childs): clds.append(n) return clds class _rml_styles(object): def __init__(self, nodes): self.styles = {} self.names = {} self.table_styles = {} self.list_styles = {} for node in nodes: for style in node.getElementsByTagName('blockTableStyle'): self.table_styles[ style.getAttribute('id')] = self._table_style_get(style) for style in node.getElementsByTagName('listStyle'): self.list_styles[ style.getAttribute('name')] = self._list_style_get(style) for style in node.getElementsByTagName('paraStyle'): self.styles[ style.getAttribute('name')] = self._para_style_get(style) for variable in node.getElementsByTagName('initialize'): for name in variable.getElementsByTagName('name'): self.names[name.getAttribute('id')] = name.getAttribute( 'value') def _para_style_update(self, style, node): for attr in ['textColor', 'backColor', 'bulletColor']: if node.hasAttribute(attr): style.__dict__[attr] = color.get(node.getAttribute(attr)) for attr in ['fontName', 'bulletFontName', 'bulletText']: if node.hasAttribute(attr): style.__dict__[attr] = node.getAttribute(attr) for attr in ['fontSize', 'leftIndent', 'rightIndent', 'spaceBefore', 'spaceAfter', 'firstLineIndent', 'bulletIndent', 'bulletFontSize', 'leading']: if node.hasAttribute(attr): if attr == 'fontSize' and not node.hasAttribute('leading'): style.__dict__['leading'] = utils.unit_get( node.getAttribute(attr)) * 1.2 style.__dict__[attr] = utils.unit_get(node.getAttribute(attr)) if node.hasAttribute('alignment'): align = { 'right': reportlab.lib.enums.TA_RIGHT, 'center': reportlab.lib.enums.TA_CENTER, 'justify': reportlab.lib.enums.TA_JUSTIFY } style.alignment = align.get( node.getAttribute('alignment').lower(), reportlab.lib.enums.TA_LEFT) return style def _list_style_update(self, style, node): for attr in ['bulletColor']: if node.hasAttribute(attr): style.__dict__[attr] = color.get(node.getAttribute(attr)) for attr in ['bulletType', 'bulletFontName', 'bulletDetent', 'bulletDir', 'bulletFormat', 'start']: if node.hasAttribute(attr): style.__dict__[attr] = node.getAttribute(attr) for attr in ['leftIndent', 'rightIndent', 'bulletFontSize', 'bulletOffsetY']: if node.hasAttribute(attr): style.__dict__[attr] = utils.unit_get(node.getAttribute(attr)) if node.hasAttribute('bulletAlign'): align = { 'right': reportlab.lib.enums.TA_RIGHT, 'center': reportlab.lib.enums.TA_CENTER, 'justify': reportlab.lib.enums.TA_JUSTIFY } style.alignment = align.get( node.getAttribute('alignment').lower(), reportlab.lib.enums.TA_LEFT) return style def _table_style_get(self, style_node): styles = [] for node in style_node.childNodes: if node.nodeType == node.ELEMENT_NODE: start = utils.tuple_int_get(node, 'start', (0, 0)) stop = utils.tuple_int_get(node, 'stop', (-1, -1)) if node.localName == 'blockValign': styles.append( ('VALIGN', start, stop, str(node.getAttribute('value')))) elif node.localName == 'blockFont': styles.append( ('FONT', start, stop, str(node.getAttribute('name')))) elif node.localName == 'blockSpan': styles.append(('SPAN', start, stop)) elif node.localName == 'blockTextColor': styles.append( ('TEXTCOLOR', start, stop, color.get(str(node.getAttribute('colorName'))))) elif node.localName == 'blockLeading': styles.append( ('LEADING', start, stop, utils.unit_get(node.getAttribute('length')))) elif node.localName == 'blockAlignment': styles.append( ('ALIGNMENT', start, stop, str(node.getAttribute('value')))) elif node.localName == 'blockLeftPadding': styles.append( ('LEFTPADDING', start, stop, utils.unit_get(node.getAttribute('length')))) elif node.localName == 'blockRightPadding': styles.append( ('RIGHTPADDING', start, stop, utils.unit_get(node.getAttribute('length')))) elif node.localName == 'blockTopPadding': styles.append( ('TOPPADDING', start, stop, utils.unit_get(node.getAttribute('length')))) elif node.localName == 'blockBottomPadding': styles.append( ('BOTTOMPADDING', start, stop, utils.unit_get(node.getAttribute('length')))) elif node.localName == 'blockBackground': styles.append( ('BACKGROUND', start, stop, color.get(node.getAttribute('colorName')))) if node.hasAttribute('size'): styles.append( ('FONTSIZE', start, stop, utils.unit_get(node.getAttribute('size')))) elif node.localName == 'lineStyle': kind = node.getAttribute('kind') kind_list = ['GRID', 'BOX', 'OUTLINE', 'INNERGRID', 'LINEBELOW', 'LINEABOVE', 'LINEBEFORE', 'LINEAFTER'] assert kind in kind_list thick = 1 if node.hasAttribute('thickness'): thick = float(node.getAttribute('thickness')) styles.append( (kind, start, stop, thick, color.get(node.getAttribute('colorName')))) return platypus.tables.TableStyle(styles) def _list_style_get(self, node): style = reportlab.lib.styles.ListStyle('Default') if node.hasAttribute("parent"): parent = node.getAttribute("parent") parentStyle = self.styles.get(parent) if not parentStyle: raise Exception("parent style = '%s' not found" % parent) style.__dict__.update(parentStyle.__dict__) style.alignment = parentStyle.alignment self._list_style_update(style, node) return style def _para_style_get(self, node): styles = reportlab.lib.styles.getSampleStyleSheet() style = copy.deepcopy(styles["Normal"]) if node.hasAttribute("parent"): parent = node.getAttribute("parent") parentStyle = self.styles.get(parent) if not parentStyle: raise Exception("parent style = '%s' not found" % parent) style.__dict__.update(parentStyle.__dict__) style.alignment = parentStyle.alignment self._para_style_update(style, node) return style def para_style_get(self, node): style = False if node.hasAttribute('style'): if node.getAttribute('style') in self.styles: style = copy.deepcopy(self.styles[node.getAttribute('style')]) else: sys.stderr.write( 'Warning: style not found, %s - setting default!\n' % (node.getAttribute('style'),)) if not style: styles = reportlab.lib.styles.getSampleStyleSheet() style = copy.deepcopy(styles['Normal']) return self._para_style_update(style, node) class _rml_doc(object): def __init__(self, data): self.dom = xml.dom.minidom.parseString(data) self.filename = self.dom.documentElement.getAttribute('filename') def docinit(self, els): from reportlab.lib.fonts import addMapping from reportlab.pdfbase import pdfmetrics from reportlab.pdfbase.ttfonts import TTFont for node in els: for font in node.getElementsByTagName('registerFont'): name = font.getAttribute('fontName').encode('ascii') fname = font.getAttribute('fontFile').encode('ascii') pdfmetrics.registerFont(TTFont(name, fname)) addMapping(name, 0, 0, name) # normal addMapping(name, 0, 1, name) # italic addMapping(name, 1, 0, name) # bold addMapping(name, 1, 1, name) # italic and bold def render(self, out): el = self.dom.documentElement.getElementsByTagName('docinit') if el: self.docinit(el) el = self.dom.documentElement.getElementsByTagName('stylesheet') self.styles = _rml_styles(el) el = self.dom.documentElement.getElementsByTagName('template') if len(el): pt_obj = _rml_template(out, el[0], self) pt_obj.render( self.dom.documentElement.getElementsByTagName('story')[0]) else: self.canvas = canvas.Canvas(out) pd = self.dom.documentElement.getElementsByTagName( 'pageDrawing')[0] pd_obj = _rml_canvas(self.canvas, None, self) pd_obj.render(pd) self.canvas.showPage() self.canvas.save() class _rml_canvas(object): def __init__(self, canvas, doc_tmpl=None, doc=None): self.canvas = canvas self.styles = doc.styles self.doc_tmpl = doc_tmpl self.doc = doc def _textual(self, node): rc = '' for n in node.childNodes: if n.nodeType == n.ELEMENT_NODE: if n.localName == 'pageNumber': countingFrom = utils.tuple_int_get(n, 'countingFrom', default=[0])[0] rc += str(self.canvas.getPageNumber() + countingFrom) elif (n.nodeType == node.CDATA_SECTION_NODE): rc += n.data elif (n.nodeType == node.TEXT_NODE): rc += n.data return rc.encode(encoding) def _drawString(self, node): self.canvas.drawString( text=self._textual(node), **utils.attr_get(node, ['x', 'y'])) def _drawCenteredString(self, node): self.canvas.drawCentredString( text=self._textual(node), **utils.attr_get(node, ['x', 'y'])) def _drawRightString(self, node): self.canvas.drawRightString( text=self._textual(node), **utils.attr_get(node, ['x', 'y'])) def _rect(self, node): if node.hasAttribute('round'): self.canvas.roundRect(radius=utils.unit_get(node.getAttribute( 'round')), **utils.attr_get(node, ['x', 'y', 'width', 'height'], {'fill': 'bool', 'stroke': 'bool'})) else: self.canvas.rect( **utils.attr_get(node, ['x', 'y', 'width', 'height'], {'fill': 'bool', 'stroke': 'bool'})) def _ellipse(self, node): x1 = utils.unit_get(node.getAttribute('x')) x2 = utils.unit_get(node.getAttribute('width')) y1 = utils.unit_get(node.getAttribute('y')) y2 = utils.unit_get(node.getAttribute('height')) self.canvas.ellipse( x1, y1, x2, y2, **utils.attr_get(node, [], {'fill': 'bool', 'stroke': 'bool'})) def _curves(self, node): line_str = utils.text_get(node).split() while len(line_str) > 7: self.canvas.bezier(*[utils.unit_get(l) for l in line_str[0:8]]) line_str = line_str[8:] def _lines(self, node): line_str = utils.text_get(node).split() lines = [] while len(line_str) > 3: lines.append([utils.unit_get(l) for l in line_str[0:4]]) line_str = line_str[4:] self.canvas.lines(lines) def _grid(self, node): xlist = [utils.unit_get(s) for s in node.getAttribute('xs').split(',')] ylist = [utils.unit_get(s) for s in node.getAttribute('ys').split(',')] self.canvas.grid(xlist, ylist) def _translate(self, node): dx = 0 dy = 0 if node.hasAttribute('dx'): dx = utils.unit_get(node.getAttribute('dx')) if node.hasAttribute('dy'): dy = utils.unit_get(node.getAttribute('dy')) self.canvas.translate(dx, dy) def _circle(self, node): self.canvas.circle(x_cen=utils.unit_get(node.getAttribute('x')), y_cen=utils.unit_get(node.getAttribute( 'y')), r=utils.unit_get(node.getAttribute('radius')), **utils.attr_get(node, [], {'fill': 'bool', 'stroke': 'bool'})) def _place(self, node): flows = _rml_flowable(self.doc).render(node) infos = utils.attr_get(node, ['x', 'y', 'width', 'height']) infos['y'] += infos['height'] for flow in flows: w, h = flow.wrap(infos['width'], infos['height']) if w <= infos['width'] and h <= infos['height']: infos['y'] -= h flow.drawOn(self.canvas, infos['x'], infos['y']) infos['height'] -= h else: raise ValueError("Not enough space") def _line_mode(self, node): ljoin = {'round': 1, 'mitered': 0, 'bevelled': 2} lcap = {'default': 0, 'round': 1, 'square': 2} if node.hasAttribute('width'): self.canvas.setLineWidth( utils.unit_get(node.getAttribute('width'))) if node.hasAttribute('join'): self.canvas.setLineJoin(ljoin[node.getAttribute('join')]) if node.hasAttribute('cap'): self.canvas.setLineCap(lcap[node.getAttribute('cap')]) if node.hasAttribute('miterLimit'): self.canvas.setDash( utils.unit_get(node.getAttribute('miterLimit'))) if node.hasAttribute('dash'): dashes = node.getAttribute('dash').split(',') for x in range(len(dashes)): dashes[x] = utils.unit_get(dashes[x]) self.canvas.setDash(node.getAttribute('dash').split(',')) def _image(self, node): from six.moves import urllib from reportlab.lib.utils import ImageReader u = urllib.request.urlopen("file:" + str(node.getAttribute('file'))) s = io.BytesIO() s.write(u.read()) s.seek(0) img = ImageReader(s) (sx, sy) = img.getSize() args = {} for tag in ('width', 'height', 'x', 'y'): if node.hasAttribute(tag): # if not utils.unit_get(node.getAttribute(tag)): # continue args[tag] = utils.unit_get(node.getAttribute(tag)) if node.hasAttribute("preserveAspectRatio"): args["preserveAspectRatio"] = True if ('width' in args) and ('height' not in args): args['height'] = sy * args['width'] / sx elif ('height' in args) and ('width' not in args): args['width'] = sx * args['height'] / sy elif ('width' in args) and ('height' in args) and (not args.get("preserveAspectRatio", False)): if (float(args['width']) / args['height']) > (float(sx) > sy): args['width'] = sx * args['height'] / sy else: args['height'] = sy * args['width'] / sx self.canvas.drawImage(img, **args) def _barcode(self, node): from reportlab.graphics.barcode import code128, qr createargs = {} drawargs = {} code_type = node.getAttribute('code') for tag in ('x', 'y'): if node.hasAttribute(tag): drawargs[tag] = utils.unit_get(node.getAttribute(tag)) if code_type == 'Code128': for tag in ('barWidth', 'barHeight'): if node.hasAttribute(tag): createargs[tag] = utils.unit_get(node.getAttribute(tag)) barcode = code128.Code128(self._textual(node), **createargs) elif code_type == "QR": for tag in ('width', 'height'): if node.hasAttribute(tag): createargs[tag] = utils.unit_get(node.getAttribute(tag)) barcode = qr.QrCode(node.getAttribute('value'), **createargs) barcode.drawOn(self.canvas, **drawargs) def _path(self, node): self.path = self.canvas.beginPath() self.path.moveTo(**utils.attr_get(node, ['x', 'y'])) for n in node.childNodes: if n.nodeType == node.ELEMENT_NODE: if n.localName == 'moveto': vals = utils.text_get(n).split() self.path.moveTo( utils.unit_get(vals[0]), utils.unit_get(vals[1])) elif n.localName == 'curvesto': vals = utils.text_get(n).split() while len(vals) > 5: pos = [] while len(pos) < 6: pos.append(utils.unit_get(vals.pop(0))) self.path.curveTo(*pos) elif (n.nodeType == node.TEXT_NODE): # Not sure if I must merge all TEXT_NODE ? data = n.data.split() while len(data) > 1: x = utils.unit_get(data.pop(0)) y = utils.unit_get(data.pop(0)) self.path.lineTo(x, y) if (not node.hasAttribute('close')) or utils.bool_get(node.getAttribute('close')): self.path.close() self.canvas.drawPath( self.path, **utils.attr_get(node, [], {'fill': 'bool', 'stroke': 'bool'})) def render(self, node): tags = { 'drawCentredString': self._drawCenteredString, 'drawCenteredString': self._drawCenteredString, 'drawRightString': self._drawRightString, 'drawString': self._drawString, 'rect': self._rect, 'ellipse': self._ellipse, 'lines': self._lines, 'grid': self._grid, 'curves': self._curves, 'fill': lambda node: self.canvas.setFillColor(color.get(node.getAttribute('color'))), 'stroke': lambda node: self.canvas.setStrokeColor(color.get(node.getAttribute('color'))), 'setFont': lambda node: self.canvas.setFont(node.getAttribute('name'), utils.unit_get(node.getAttribute('size'))), 'place': self._place, 'circle': self._circle, 'lineMode': self._line_mode, 'path': self._path, 'rotate': lambda node: self.canvas.rotate(float(node.getAttribute('degrees'))), 'translate': self._translate, 'image': self._image, 'barCode': self._barcode, } for nd in node.childNodes: if nd.nodeType == nd.ELEMENT_NODE: for tag in tags: if nd.localName == tag: tags[tag](nd) break class _rml_draw(object): def __init__(self, node, styles): self.node = node self.styles = styles self.canvas = None def render(self, canvas, doc): canvas.saveState() cnv = _rml_canvas(canvas, doc, self.styles) cnv.render(self.node) canvas.restoreState() class _rml_flowable(object): def __init__(self, doc): self.doc = doc self.styles = doc.styles def _textual(self, node): rc = '' for n in node.childNodes: if n.nodeType == node.ELEMENT_NODE: if n.localName == 'getName': newNode = self.doc.dom.createTextNode( self.styles.names.get(n.getAttribute('id'), 'Unknown name')) node.insertBefore(newNode, n) node.removeChild(n) if n.localName == 'pageNumber': rc += '' # TODO: change this ! else: self._textual(n) rc += n.toxml() elif (n.nodeType == node.CDATA_SECTION_NODE): rc += n.data elif (n.nodeType == node.TEXT_NODE): rc += n.toxml() return text_type(rc) def _list(self, node): if node.hasAttribute('style'): list_style = self.styles.list_styles[node.getAttribute('style')] else: list_style = platypus.flowables.ListStyle('Default') list_items = [] for li in _child_get(node, 'li'): flow = [] for n in li.childNodes: if n.nodeType == node.ELEMENT_NODE: flow.append(self._flowable(n)) if not flow: if li.hasAttribute('style'): li_style = self.styles.styles[ li.getAttribute('style')] else: li_style = reportlab.lib.styles.getSampleStyleSheet()['Normal'] flow = platypus.paragraph.Paragraph(self._textual(li), li_style) list_item = platypus.ListItem(flow) list_items.append(list_item) return platypus.ListFlowable(list_items, style=list_style, start=list_style.__dict__.get('start')) def _table(self, node): length = 0 colwidths = None rowheights = None data = [] for tr in _child_get(node, 'tr'): data2 = [] for td in _child_get(tr, 'td'): flow = [] for n in td.childNodes: if n.nodeType == node.ELEMENT_NODE: flow.append(self._flowable(n)) if not len(flow): flow = self._textual(td) data2.append(flow) if len(data2) > length: length = len(data2) for ab in data: while len(ab) < length: ab.append('') while len(data2) < length: data2.append('') data.append(data2) if node.hasAttribute('colWidths'): assert length == len(node.getAttribute('colWidths').split(',')) colwidths = [ utils.unit_get(f.strip()) for f in node.getAttribute('colWidths').split(',')] if node.hasAttribute('rowHeights'): rowheights = [ utils.unit_get(f.strip()) for f in node.getAttribute('rowHeights').split(',')] table = platypus.Table(data=data, colWidths=colwidths, rowHeights=rowheights, **( utils.attr_get(node, ['splitByRow'], {'repeatRows': 'int', 'repeatCols': 'int'}))) if node.hasAttribute('style'): table.setStyle( self.styles.table_styles[node.getAttribute('style')]) return table def _illustration(self, node): class Illustration(platypus.flowables.Flowable): def __init__(self, node, styles): self.node = node self.styles = styles self.width = utils.unit_get(node.getAttribute('width')) self.height = utils.unit_get(node.getAttribute('height')) def wrap(self, *args): return (self.width, self.height) def draw(self): canvas = self.canv drw = _rml_draw(self.node, self.styles) drw.render(self.canv, None) return Illustration(node, self.styles) def _flowable(self, node): if node.localName == 'para': style = self.styles.para_style_get(node) return platypus.Paragraph(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText': 'str'}))) elif node.localName == 'name': self.styles.names[ node.getAttribute('id')] = node.getAttribute('value') return None elif node.localName == 'xpre': style = self.styles.para_style_get(node) return platypus.XPreformatted(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText': 'str', 'dedent': 'int', 'frags': 'int'}))) elif node.localName == 'pre': style = self.styles.para_style_get(node) return platypus.Preformatted(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText': 'str', 'dedent': 'int'}))) elif node.localName == 'illustration': return self._illustration(node) elif node.localName == 'blockTable': return self._table(node) # return KeepTogether(self._table(node)) elif node.localName == 'title': styles = reportlab.lib.styles.getSampleStyleSheet() style = styles['Title'] return platypus.Paragraph(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText': 'str'}))) elif node.localName == 'h1': styles = reportlab.lib.styles.getSampleStyleSheet() style = styles['Heading1'] return platypus.Paragraph(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText': 'str'}))) elif node.localName == 'h2': styles = reportlab.lib.styles.getSampleStyleSheet() style = styles['Heading2'] return platypus.Paragraph(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText': 'str'}))) elif node.localName == 'h3': styles = reportlab.lib.styles.getSampleStyleSheet() style = styles['Heading3'] return platypus.Paragraph(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText': 'str'}))) elif node.localName == 'image': return platypus.Image(node.getAttribute('file'), mask=(250, 255, 250, 255, 250, 255), **(utils.attr_get(node, ['width', 'height', 'preserveAspectRatio', 'anchor']))) elif node.localName == 'spacer': if node.hasAttribute('width'): width = utils.unit_get(node.getAttribute('width')) else: width = utils.unit_get('1cm') length = utils.unit_get(node.getAttribute('length')) return platypus.Spacer(width=width, height=length) elif node.localName == 'barCode': return code39.Extended39(self._textual(node)) elif node.localName == 'pageBreak': return platypus.PageBreak() elif node.localName == 'condPageBreak': return platypus.CondPageBreak(**(utils.attr_get(node, ['height']))) elif node.localName == 'setNextTemplate': return platypus.NextPageTemplate(str(node.getAttribute('name'))) elif node.localName == 'nextFrame': return platypus.FrameBreak() # return platypus.CondPageBreak(1000) # TODO: change the 1000 ! elif node.localName == 'ul': return self._list(node) elif node.localName == 'keepInFrame': substory = self.render(node) kwargs = { "maxWidth": 0, "maxHeight": 0, "content": substory, } mode = node.getAttribute("onOverflow") if mode: kwargs["mode"] = mode name = node.getAttribute("id") if name: kwargs["name"] = name kwargs.update( utils.attr_get(node, ['maxWidth','maxHeight', 'mergeSpace'], {'maxWidth': 'int','maxHeight': 'int'})) return platypus.KeepInFrame(**kwargs) else: sys.stderr.write( 'Warning: flowable not yet implemented: %s !\n' % (node.localName,)) return None def render(self, node_story): story = [] node = node_story.firstChild while node: if node.nodeType == node.ELEMENT_NODE: flow = self._flowable(node) if flow: story.append(flow) node = node.nextSibling return story class _rml_template(object): def __init__(self, out, node, doc): if not node.hasAttribute('pageSize'): pageSize = (utils.unit_get('21cm'), utils.unit_get('29.7cm')) else: ps = [x.strip() for x in node.getAttribute('pageSize').replace(')', '').replace( '(', '').split(',')] pageSize = (utils.unit_get(ps[0]), utils.unit_get(ps[1])) cm = reportlab.lib.units.cm self.doc_tmpl = platypus.BaseDocTemplate(out, pagesize=pageSize, **utils.attr_get(node, ['leftMargin', 'rightMargin', 'topMargin', 'bottomMargin'], { 'allowSplitting': 'int', 'showBoundary': 'bool', 'title': 'str', 'author': 'str', 'rotation': 'int'})) self.page_templates = [] self.styles = doc.styles self.doc = doc pts = node.getElementsByTagName('pageTemplate') for pt in pts: frames = [] for frame_el in pt.getElementsByTagName('frame'): frame = platypus.Frame( **(utils.attr_get(frame_el, ['x1', 'y1', 'width', 'height', 'leftPadding', 'rightPadding', 'bottomPadding', 'topPadding'], {'id': 'text', 'showBoundary': 'bool'}))) frames.append(frame) gr = pt.getElementsByTagName('pageGraphics') if len(gr): drw = _rml_draw(gr[0], self.doc) self.page_templates.append(platypus.PageTemplate( frames=frames, onPage=drw.render, **utils.attr_get(pt, [], {'id': 'str'}))) else: self.page_templates.append( platypus.PageTemplate(frames=frames, **utils.attr_get(pt, [], {'id': 'str'}))) self.doc_tmpl.addPageTemplates(self.page_templates) def render(self, node_story): r = _rml_flowable(self.doc) fis = r.render(node_story) self.doc_tmpl.build(fis) class Mark: "Enum Values for the watermark style: No watermark, first page, all pages." NOTHING = "0" FIRST_PAGE = "1" ALL_PAGES = "2" class PdfWatermark: "A class that converts a rml file to pdf" def __init__( self, outFile = None ): self.outputFile = outFile def watermark( self, pdfStr, watermarkFile, spec ): # Read the watermark- and document pdf file watermark = PdfFileReader(watermarkFile) watermark_page = watermark.getPage(0) pdfStr.seek(0) inputPdf = PdfFileReader( pdfStr ) outputPdf = PdfFileWriter() # flag for the first page of the source file firstPage = True # Loop over source document pages and merge with the first page of the watermark # file. for page in range(inputPdf.getNumPages()): pdf_page = inputPdf.getPage(page) if (spec == Mark.FIRST_PAGE and firstPage) or spec == Mark.ALL_PAGES: # deep copy the watermark page here, otherwise the watermark page # gets merged over and over because p would only be a reference pdf_page.mergePage(watermark_page) outputPdf.addPage( pdf_page ) firstPage = False else: outputPdf.addPage(pdf_page) if self.outputFile: with open(self.outputFile, 'wb') as fh: outputPdf.write(fh) return self.outputFile else: bytesIO = io.BytesIO(); outputPdf.write(bytesIO) return bytesIO.getvalue() def parseString(data): r = _rml_doc(data.strip()) fp = io.BytesIO() r.render(fp) return fp.getvalue() def erml2pdf_help(): print( 'Usage: erml2pdf [options] input.rml > output.pdf') print( '') print( 'Tool to render a file of the xml based markup language RML to PDF') print( 'with option to merge another PDF file as watermark.') print( '') print( 'Options:') print( '-o, --output output file, instead of standard out') print( '-m, --watermark-mode set the watermark mode with ') print( ' 0 = no watermark (default)') print( ' 1 = watermark on first page') print( ' 2 = watermark on all pages') print( ' Note: a watermark file must be specified for 1, 2') print( '-w, --watermark-file watermark file, the first page is used.') print( '') sys.exit(0) if __name__=="__main__": try: opts, args = getopt.getopt(sys.argv[1:], "ho:w:m:", ["help", "output=", "watermark-file=", "watermark-mode="]) except(getopt.GetoptError, err): # print help information and exit: print( str(err)) # will print something like "option -a not recognized" erml2pdf_help() sys.exit(2) output = None watermarkFile = None watermarkMode = Mark.NOTHING for o, a in opts: if o in ("-h", "--help"): erml2pdf_help() sys.exit() elif o in ("-o", "--output"): output = a elif o in ("-w", "--watermark-file"): watermarkFile = a elif o in ("-m", "--watermark-mode"): watermarkMode = a else: assert False, "unhandled option" # if len(args) == 0: # a input file needs to be there erml2pdf_help() else: # print ("Args:" + args[0]) infile = args[0] # create the PDF with the help of reportlab content = open(infile, 'r').read() pdf = parseString( content ) # apply the watermark if required # print "############ Watermark-Mode: " + watermarkMode if watermarkMode != Mark.NOTHING: wm = PdfWatermark() pdfStringFile = io.BytesIO() pdfStringFile.write( pdf ) pdf = wm.watermark( pdfStringFile, watermarkFile, watermarkMode ) # handle output option, either file or stdout if output: outfile = open(output, 'wb') outfile.write( pdf ) outfile.close() else: if sys.version_info[0] < 3: sys.stdout.write(pdf) else: sys.stdout.buffer.write(pdf) kraft-0.97/tools/findcontact.cpp000066400000000000000000000163511410616450300167260ustar00rootroot00000000000000/* * Copyright (C) 2014 by Klaas Freitag * * 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. */ #include #include #include #include #include #include #include #include #include "addressprovider.h" #include class FindContact : public QObject { Q_OBJECT signals: void quitLoop(); public slots: void slotAddresseeFound( const QString&, const KContacts::Addressee& contact ) { dumpContact(contact, _options._outputType); emit quitLoop(); } void run() { if( parseOptions() ) { search(); } else { help(); emit quitLoop(); } } public: typedef enum { VCard, Pretty, Template } OutputType; struct CmdOptions { QString uid; QString outputfile; OutputType _outputType; QString outTemplate; }; // Constructor, called to initialize object FindContact( const QStringList& args ) : QObject(), _args(args) { _addressProvider.reset( new AddressProvider(this) ); connect( _addressProvider.data(), SIGNAL(lookupResult(QString,KContacts::Addressee)), this, SLOT(slotAddresseeFound(QString, KContacts::Addressee))); } void help() { std::cout << std::endl; std::cout << " findcontact - search for contact data." << std::endl; std::cout << " Usage: findcontact [-o filename] uid" << std::endl; std::cout << std::endl; std::cout << " -o : dump output to filename" << std::endl; std::cout << " -c: Output format VCard." << std::endl; std::cout << " -t