|
GnuCash 2.4.99
|
00001 /********************************************************************\ 00002 * gncOwner.c -- Business Interface: Object OWNERs * 00003 * * 00004 * This program is free software; you can redistribute it and/or * 00005 * modify it under the terms of the GNU General Public License as * 00006 * published by the Free Software Foundation; either version 2 of * 00007 * the License, or (at your option) any later version. * 00008 * * 00009 * This program is distributed in the hope that it will be useful, * 00010 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 00011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 00012 * GNU General Public License for more details. * 00013 * * 00014 * You should have received a copy of the GNU General Public License* 00015 * along with this program; if not, contact: * 00016 * * 00017 * Free Software Foundation Voice: +1-617-542-5942 * 00018 * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * 00019 * Boston, MA 02110-1301, USA gnu@gnu.org * 00020 * * 00021 \********************************************************************/ 00022 00023 /* 00024 * Copyright (C) 2001, 2002 Derek Atkins 00025 * Copyright (C) 2003 Linas Vepstas <linas@linas.org> 00026 * Copyright (c) 2005 Neil Williams <linux@codehelp.co.uk> 00027 * Copyright (c) 2006 David Hampton <hampton@employees.org> 00028 * Copyright (c) 2011 Geert Janssens <geert@kobaltwit.be> 00029 * Author: Derek Atkins <warlord@MIT.EDU> 00030 */ 00031 00032 #include "config.h" 00033 00034 #include <glib.h> 00035 #include <glib/gi18n.h> 00036 #include <string.h> /* for memcpy() */ 00037 00038 #include "gncCustomerP.h" 00039 #include "gncEmployeeP.h" 00040 #include "gncJobP.h" 00041 #include "gncOwner.h" 00042 #include "gncOwnerP.h" 00043 #include "gncVendorP.h" 00044 #include "gncInvoice.h" 00045 #include "gnc-commodity.h" 00046 #include "Transaction.h" 00047 #include "Split.h" 00048 00049 #define _GNC_MOD_NAME GNC_ID_OWNER 00050 00051 #define GNC_OWNER_ID "gncOwner" 00052 #define GNC_OWNER_TYPE "owner-type" 00053 #define GNC_OWNER_GUID "owner-guid" 00054 00055 GncOwner * gncOwnerNew (void) 00056 { 00057 GncOwner *o; 00058 00059 o = g_new0 (GncOwner, 1); 00060 o->type = GNC_OWNER_NONE; 00061 return o; 00062 } 00063 00064 void gncOwnerFree (GncOwner *owner) 00065 { 00066 if (!owner) return; 00067 g_free (owner); 00068 } 00069 00070 void gncOwnerBeginEdit (GncOwner *owner) 00071 { 00072 if (!owner) return; 00073 switch (owner->type) 00074 { 00075 case GNC_OWNER_NONE : 00076 case GNC_OWNER_UNDEFINED : 00077 break; 00078 case GNC_OWNER_CUSTOMER : 00079 { 00080 gncCustomerBeginEdit(owner->owner.customer); 00081 break; 00082 } 00083 case GNC_OWNER_JOB : 00084 { 00085 gncJobBeginEdit(owner->owner.job); 00086 break; 00087 } 00088 case GNC_OWNER_VENDOR : 00089 { 00090 gncVendorBeginEdit(owner->owner.vendor); 00091 break; 00092 } 00093 case GNC_OWNER_EMPLOYEE : 00094 { 00095 gncEmployeeBeginEdit(owner->owner.employee); 00096 break; 00097 } 00098 } 00099 } 00100 00101 00102 void gncOwnerDestroy (GncOwner *owner) 00103 { 00104 if (!owner) return; 00105 switch (owner->type) 00106 { 00107 case GNC_OWNER_NONE : 00108 case GNC_OWNER_UNDEFINED : 00109 break; 00110 case GNC_OWNER_CUSTOMER : 00111 { 00112 gncCustomerDestroy(owner->owner.customer); 00113 break; 00114 } 00115 case GNC_OWNER_JOB : 00116 { 00117 gncJobDestroy(owner->owner.job); 00118 break; 00119 } 00120 case GNC_OWNER_VENDOR : 00121 { 00122 gncVendorDestroy(owner->owner.vendor); 00123 break; 00124 } 00125 case GNC_OWNER_EMPLOYEE : 00126 { 00127 gncEmployeeDestroy(owner->owner.employee); 00128 break; 00129 } 00130 } 00131 } 00132 00133 void gncOwnerInitUndefined (GncOwner *owner, gpointer obj) 00134 { 00135 if (!owner) return; 00136 owner->type = GNC_OWNER_UNDEFINED; 00137 owner->owner.undefined = obj; 00138 } 00139 00140 void gncOwnerInitCustomer (GncOwner *owner, GncCustomer *customer) 00141 { 00142 if (!owner) return; 00143 owner->type = GNC_OWNER_CUSTOMER; 00144 owner->owner.customer = customer; 00145 } 00146 00147 void gncOwnerInitJob (GncOwner *owner, GncJob *job) 00148 { 00149 if (!owner) return; 00150 owner->type = GNC_OWNER_JOB; 00151 owner->owner.job = job; 00152 } 00153 00154 void gncOwnerInitVendor (GncOwner *owner, GncVendor *vendor) 00155 { 00156 if (!owner) return; 00157 owner->type = GNC_OWNER_VENDOR; 00158 owner->owner.vendor = vendor; 00159 } 00160 00161 void gncOwnerInitEmployee (GncOwner *owner, GncEmployee *employee) 00162 { 00163 if (!owner) return; 00164 owner->type = GNC_OWNER_EMPLOYEE; 00165 owner->owner.employee = employee; 00166 } 00167 00168 GncOwnerType gncOwnerGetType (const GncOwner *owner) 00169 { 00170 if (!owner) return GNC_OWNER_NONE; 00171 return owner->type; 00172 } 00173 00174 QofIdTypeConst 00175 qofOwnerGetType(const GncOwner *owner) 00176 { 00177 return gncOwnerTypeToQofIdType(owner->type); 00178 } 00179 00180 QofIdTypeConst gncOwnerTypeToQofIdType(GncOwnerType t) 00181 { 00182 QofIdTypeConst type = NULL; 00183 switch (t) 00184 { 00185 case GNC_OWNER_NONE : 00186 { 00187 type = NULL; 00188 break; 00189 } 00190 case GNC_OWNER_UNDEFINED : 00191 { 00192 type = NULL; 00193 break; 00194 } 00195 case GNC_OWNER_CUSTOMER : 00196 { 00197 type = GNC_ID_CUSTOMER; 00198 break; 00199 } 00200 case GNC_OWNER_JOB : 00201 { 00202 type = GNC_ID_JOB; 00203 break; 00204 } 00205 case GNC_OWNER_VENDOR : 00206 { 00207 type = GNC_ID_VENDOR; 00208 break; 00209 } 00210 case GNC_OWNER_EMPLOYEE : 00211 { 00212 type = GNC_ID_EMPLOYEE; 00213 break; 00214 } 00215 } 00216 return type; 00217 } 00218 00219 QofInstance* 00220 qofOwnerGetOwner (const GncOwner *owner) 00221 { 00222 QofInstance *ent; 00223 00224 if (!owner) 00225 { 00226 return NULL; 00227 } 00228 ent = NULL; 00229 switch (owner->type) 00230 { 00231 case GNC_OWNER_NONE : 00232 { 00233 break; 00234 } 00235 case GNC_OWNER_UNDEFINED : 00236 { 00237 break; 00238 } 00239 case GNC_OWNER_CUSTOMER : 00240 { 00241 ent = QOF_INSTANCE(owner->owner.customer); 00242 break; 00243 } 00244 case GNC_OWNER_JOB : 00245 { 00246 ent = QOF_INSTANCE(owner->owner.job); 00247 break; 00248 } 00249 case GNC_OWNER_VENDOR : 00250 { 00251 ent = QOF_INSTANCE(owner->owner.vendor); 00252 break; 00253 } 00254 case GNC_OWNER_EMPLOYEE : 00255 { 00256 ent = QOF_INSTANCE(owner->owner.employee); 00257 break; 00258 } 00259 } 00260 return ent; 00261 } 00262 00263 void 00264 qofOwnerSetEntity (GncOwner *owner, QofInstance *ent) 00265 { 00266 if (!owner || !ent) 00267 { 00268 return; 00269 } 00270 if (0 == safe_strcmp(ent->e_type, GNC_ID_CUSTOMER)) 00271 { 00272 owner->type = GNC_OWNER_CUSTOMER; 00273 gncOwnerInitCustomer(owner, (GncCustomer*)ent); 00274 } 00275 else if (0 == safe_strcmp(ent->e_type, GNC_ID_JOB)) 00276 { 00277 owner->type = GNC_OWNER_JOB; 00278 gncOwnerInitJob(owner, (GncJob*)ent); 00279 } 00280 else if (0 == safe_strcmp(ent->e_type, GNC_ID_VENDOR)) 00281 { 00282 owner->type = GNC_OWNER_VENDOR; 00283 gncOwnerInitVendor(owner, (GncVendor*)ent); 00284 } 00285 else if (0 == safe_strcmp(ent->e_type, GNC_ID_EMPLOYEE)) 00286 { 00287 owner->type = GNC_OWNER_EMPLOYEE; 00288 gncOwnerInitEmployee(owner, (GncEmployee*)ent); 00289 } 00290 else 00291 { 00292 owner->type = GNC_OWNER_NONE; 00293 owner->owner.undefined = NULL; 00294 } 00295 } 00296 00297 gboolean GNC_IS_OWNER (QofInstance *ent) 00298 { 00299 if (!ent) 00300 return FALSE; 00301 00302 return (GNC_IS_VENDOR(ent) || 00303 GNC_IS_CUSTOMER(ent) || 00304 GNC_IS_EMPLOYEE(ent) || 00305 GNC_IS_JOB(ent)); 00306 } 00307 gpointer gncOwnerGetUndefined (const GncOwner *owner) 00308 { 00309 if (!owner) return NULL; 00310 if (owner->type != GNC_OWNER_UNDEFINED) return NULL; 00311 return owner->owner.undefined; 00312 } 00313 00314 GncCustomer * gncOwnerGetCustomer (const GncOwner *owner) 00315 { 00316 if (!owner) return NULL; 00317 if (owner->type != GNC_OWNER_CUSTOMER) return NULL; 00318 return owner->owner.customer; 00319 } 00320 00321 GncJob * gncOwnerGetJob (const GncOwner *owner) 00322 { 00323 if (!owner) return NULL; 00324 if (owner->type != GNC_OWNER_JOB) return NULL; 00325 return owner->owner.job; 00326 } 00327 00328 GncVendor * gncOwnerGetVendor (const GncOwner *owner) 00329 { 00330 if (!owner) return NULL; 00331 if (owner->type != GNC_OWNER_VENDOR) return NULL; 00332 return owner->owner.vendor; 00333 } 00334 00335 GncEmployee * gncOwnerGetEmployee (const GncOwner *owner) 00336 { 00337 if (!owner) return NULL; 00338 if (owner->type != GNC_OWNER_EMPLOYEE) return NULL; 00339 return owner->owner.employee; 00340 } 00341 00342 void gncOwnerCopy (const GncOwner *src, GncOwner *dest) 00343 { 00344 if (!src || !dest) return; 00345 if (src == dest) return; 00346 memcpy (dest, src, sizeof (*dest)); 00347 } 00348 00349 gboolean gncOwnerEqual (const GncOwner *a, const GncOwner *b) 00350 { 00351 if (!a || !b) return FALSE; 00352 if (gncOwnerGetType (a) != gncOwnerGetType (b)) return FALSE; 00353 return (a->owner.undefined == b->owner.undefined); 00354 } 00355 00356 int gncOwnerGCompareFunc (const GncOwner *a, const GncOwner *b) 00357 { 00358 if (gncOwnerEqual (a, b)) 00359 return 0; 00360 else 00361 return 1; 00362 } 00363 00364 const char * gncOwnerGetID (const GncOwner *owner) 00365 { 00366 if (!owner) return NULL; 00367 switch (owner->type) 00368 { 00369 case GNC_OWNER_NONE: 00370 case GNC_OWNER_UNDEFINED: 00371 default: 00372 return NULL; 00373 case GNC_OWNER_CUSTOMER: 00374 return gncCustomerGetID (owner->owner.customer); 00375 case GNC_OWNER_JOB: 00376 return gncJobGetID (owner->owner.job); 00377 case GNC_OWNER_VENDOR: 00378 return gncVendorGetID (owner->owner.vendor); 00379 case GNC_OWNER_EMPLOYEE: 00380 return gncEmployeeGetID (owner->owner.employee); 00381 } 00382 } 00383 00384 const char * gncOwnerGetName (const GncOwner *owner) 00385 { 00386 if (!owner) return NULL; 00387 switch (owner->type) 00388 { 00389 case GNC_OWNER_NONE: 00390 case GNC_OWNER_UNDEFINED: 00391 default: 00392 return NULL; 00393 case GNC_OWNER_CUSTOMER: 00394 return gncCustomerGetName (owner->owner.customer); 00395 case GNC_OWNER_JOB: 00396 return gncJobGetName (owner->owner.job); 00397 case GNC_OWNER_VENDOR: 00398 return gncVendorGetName (owner->owner.vendor); 00399 case GNC_OWNER_EMPLOYEE: 00400 return gncEmployeeGetName (owner->owner.employee); 00401 } 00402 } 00403 00404 GncAddress * gncOwnerGetAddr (const GncOwner *owner) 00405 { 00406 if (!owner) return NULL; 00407 switch (owner->type) 00408 { 00409 case GNC_OWNER_NONE: 00410 case GNC_OWNER_UNDEFINED: 00411 case GNC_OWNER_JOB: 00412 default: 00413 return NULL; 00414 case GNC_OWNER_CUSTOMER: 00415 return gncCustomerGetAddr (owner->owner.customer); 00416 case GNC_OWNER_VENDOR: 00417 return gncVendorGetAddr (owner->owner.vendor); 00418 case GNC_OWNER_EMPLOYEE: 00419 return gncEmployeeGetAddr (owner->owner.employee); 00420 } 00421 } 00422 00423 gnc_commodity * gncOwnerGetCurrency (const GncOwner *owner) 00424 { 00425 if (!owner) return NULL; 00426 switch (owner->type) 00427 { 00428 case GNC_OWNER_NONE: 00429 case GNC_OWNER_UNDEFINED: 00430 default: 00431 return NULL; 00432 case GNC_OWNER_CUSTOMER: 00433 return gncCustomerGetCurrency (owner->owner.customer); 00434 case GNC_OWNER_VENDOR: 00435 return gncVendorGetCurrency (owner->owner.vendor); 00436 case GNC_OWNER_EMPLOYEE: 00437 return gncEmployeeGetCurrency (owner->owner.employee); 00438 case GNC_OWNER_JOB: 00439 return gncOwnerGetCurrency (gncJobGetOwner (owner->owner.job)); 00440 } 00441 } 00442 00443 gboolean gncOwnerGetActive (const GncOwner *owner) 00444 { 00445 if (!owner) return FALSE; 00446 switch (owner->type) 00447 { 00448 case GNC_OWNER_NONE: 00449 case GNC_OWNER_UNDEFINED: 00450 default: 00451 return FALSE; 00452 case GNC_OWNER_CUSTOMER: 00453 return gncCustomerGetActive (owner->owner.customer); 00454 case GNC_OWNER_VENDOR: 00455 return gncVendorGetActive (owner->owner.vendor); 00456 case GNC_OWNER_EMPLOYEE: 00457 return gncEmployeeGetActive (owner->owner.employee); 00458 case GNC_OWNER_JOB: 00459 return gncJobGetActive (owner->owner.job); 00460 } 00461 } 00462 00463 const GncGUID * gncOwnerGetGUID (const GncOwner *owner) 00464 { 00465 if (!owner) return NULL; 00466 00467 switch (owner->type) 00468 { 00469 case GNC_OWNER_NONE: 00470 case GNC_OWNER_UNDEFINED: 00471 default: 00472 return NULL; 00473 case GNC_OWNER_CUSTOMER: 00474 return qof_instance_get_guid (QOF_INSTANCE(owner->owner.customer)); 00475 case GNC_OWNER_JOB: 00476 return qof_instance_get_guid (QOF_INSTANCE(owner->owner.job)); 00477 case GNC_OWNER_VENDOR: 00478 return qof_instance_get_guid (QOF_INSTANCE(owner->owner.vendor)); 00479 case GNC_OWNER_EMPLOYEE: 00480 return qof_instance_get_guid (QOF_INSTANCE(owner->owner.employee)); 00481 } 00482 } 00483 00484 void 00485 gncOwnerSetActive (const GncOwner *owner, gboolean active) 00486 { 00487 if (!owner) return; 00488 switch (owner->type) 00489 { 00490 case GNC_OWNER_CUSTOMER: 00491 gncCustomerSetActive (owner->owner.customer, active); 00492 break; 00493 case GNC_OWNER_VENDOR: 00494 gncVendorSetActive (owner->owner.vendor, active); 00495 break; 00496 case GNC_OWNER_EMPLOYEE: 00497 gncEmployeeSetActive (owner->owner.employee, active); 00498 break; 00499 case GNC_OWNER_JOB: 00500 gncJobSetActive (owner->owner.job, active); 00501 break; 00502 case GNC_OWNER_NONE: 00503 case GNC_OWNER_UNDEFINED: 00504 default: 00505 break; 00506 } 00507 } 00508 00509 GncGUID gncOwnerRetGUID (GncOwner *owner) 00510 { 00511 const GncGUID *guid = gncOwnerGetGUID (owner); 00512 if (guid) 00513 return *guid; 00514 return *guid_null (); 00515 } 00516 00517 const GncOwner * gncOwnerGetEndOwner (const GncOwner *owner) 00518 { 00519 if (!owner) return NULL; 00520 switch (owner->type) 00521 { 00522 case GNC_OWNER_NONE: 00523 case GNC_OWNER_UNDEFINED: 00524 default: 00525 return NULL; 00526 case GNC_OWNER_CUSTOMER: 00527 case GNC_OWNER_VENDOR: 00528 case GNC_OWNER_EMPLOYEE: 00529 return owner; 00530 case GNC_OWNER_JOB: 00531 return gncJobGetOwner (owner->owner.job); 00532 } 00533 } 00534 00535 int gncOwnerCompare (const GncOwner *a, const GncOwner *b) 00536 { 00537 if (!a && !b) return 0; 00538 if (!a && b) return 1; 00539 if (a && !b) return -1; 00540 00541 if (a->type != b->type) 00542 return (a->type - b->type); 00543 00544 switch (a->type) 00545 { 00546 case GNC_OWNER_NONE: 00547 case GNC_OWNER_UNDEFINED: 00548 default: 00549 return 0; 00550 case GNC_OWNER_CUSTOMER: 00551 return gncCustomerCompare (a->owner.customer, b->owner.customer); 00552 case GNC_OWNER_VENDOR: 00553 return gncVendorCompare (a->owner.vendor, b->owner.vendor); 00554 case GNC_OWNER_EMPLOYEE: 00555 return gncEmployeeCompare (a->owner.employee, b->owner.employee); 00556 case GNC_OWNER_JOB: 00557 return gncJobCompare (a->owner.job, b->owner.job); 00558 } 00559 } 00560 00561 const GncGUID * gncOwnerGetEndGUID (const GncOwner *owner) 00562 { 00563 if (!owner) return NULL; 00564 return gncOwnerGetGUID (gncOwnerGetEndOwner (owner)); 00565 } 00566 00567 void gncOwnerAttachToLot (const GncOwner *owner, GNCLot *lot) 00568 { 00569 KvpFrame *kvp; 00570 KvpValue *value; 00571 00572 if (!owner || !lot) 00573 return; 00574 00575 kvp = gnc_lot_get_slots (lot); 00576 gnc_lot_begin_edit (lot); 00577 00578 value = kvp_value_new_gint64 (gncOwnerGetType (owner)); 00579 kvp_frame_set_slot_path (kvp, value, GNC_OWNER_ID, GNC_OWNER_TYPE, NULL); 00580 kvp_value_delete (value); 00581 00582 value = kvp_value_new_guid (gncOwnerGetGUID (owner)); 00583 kvp_frame_set_slot_path (kvp, value, GNC_OWNER_ID, GNC_OWNER_GUID, NULL); 00584 qof_instance_set_dirty (QOF_INSTANCE (lot)); 00585 gnc_lot_commit_edit (lot); 00586 kvp_value_delete (value); 00587 00588 } 00589 00590 gboolean gncOwnerGetOwnerFromLot (GNCLot *lot, GncOwner *owner) 00591 { 00592 KvpFrame *kvp; 00593 KvpValue *value; 00594 GncGUID *guid; 00595 QofBook *book; 00596 GncOwnerType type; 00597 00598 if (!lot || !owner) return FALSE; 00599 00600 book = gnc_lot_get_book (lot); 00601 kvp = gnc_lot_get_slots (lot); 00602 00603 value = kvp_frame_get_slot_path (kvp, GNC_OWNER_ID, GNC_OWNER_TYPE, NULL); 00604 if (!value) return FALSE; 00605 00606 type = kvp_value_get_gint64 (value); 00607 00608 value = kvp_frame_get_slot_path (kvp, GNC_OWNER_ID, GNC_OWNER_GUID, NULL); 00609 if (!value) return FALSE; 00610 00611 guid = kvp_value_get_guid (value); 00612 if (!guid) 00613 return FALSE; 00614 00615 switch (type) 00616 { 00617 case GNC_OWNER_CUSTOMER: 00618 gncOwnerInitCustomer (owner, gncCustomerLookup (book, guid)); 00619 break; 00620 case GNC_OWNER_VENDOR: 00621 gncOwnerInitVendor (owner, gncVendorLookup (book, guid)); 00622 break; 00623 case GNC_OWNER_EMPLOYEE: 00624 gncOwnerInitEmployee (owner, gncEmployeeLookup (book, guid)); 00625 break; 00626 case GNC_OWNER_JOB: 00627 gncOwnerInitJob (owner, gncJobLookup (book, guid)); 00628 break; 00629 default: 00630 return FALSE; 00631 } 00632 00633 return (owner->owner.undefined != NULL); 00634 } 00635 00636 gboolean gncOwnerIsValid (const GncOwner *owner) 00637 { 00638 if (!owner) return FALSE; 00639 return (owner->owner.undefined != NULL); 00640 } 00641 00642 KvpFrame* gncOwnerGetSlots(GncOwner* owner) 00643 { 00644 if (!owner) return NULL; 00645 00646 switch (gncOwnerGetType(owner)) 00647 { 00648 case GNC_OWNER_CUSTOMER: 00649 case GNC_OWNER_VENDOR: 00650 case GNC_OWNER_EMPLOYEE: 00651 return qof_instance_get_slots(QOF_INSTANCE(owner->owner.undefined)); 00652 case GNC_OWNER_JOB: 00653 return gncOwnerGetSlots(gncJobGetOwner(gncOwnerGetJob(owner))); 00654 default: 00655 return NULL; 00656 } 00657 } 00658 00659 gboolean 00660 gncOwnerLotMatchOwnerFunc (GNCLot *lot, gpointer user_data) 00661 { 00662 const GncOwner *req_owner = user_data; 00663 GncOwner lot_owner; 00664 const GncOwner *end_owner; 00665 GncInvoice *invoice = gncInvoiceGetInvoiceFromLot (lot); 00666 00667 /* Determine the owner associated to the lot */ 00668 if (invoice) 00669 /* Invoice lots */ 00670 end_owner = gncOwnerGetEndOwner (gncInvoiceGetOwner (invoice)); 00671 else if (gncOwnerGetOwnerFromLot (lot, &lot_owner)) 00672 /* Pre-payment lots */ 00673 end_owner = gncOwnerGetEndOwner (&lot_owner); 00674 else 00675 return FALSE; 00676 00677 /* Is this a lot for the requested owner ? */ 00678 return gncOwnerEqual (end_owner, req_owner); 00679 } 00680 00681 gint 00682 gncOwnerLotsSortFunc (GNCLot *lotA, GNCLot *lotB) 00683 { 00684 GncInvoice *ia, *ib; 00685 Timespec da, db; 00686 00687 ia = gncInvoiceGetInvoiceFromLot (lotA); 00688 ib = gncInvoiceGetInvoiceFromLot (lotB); 00689 00690 if (ia) 00691 da = gncInvoiceGetDateDue (ia); 00692 else 00693 da = xaccTransRetDatePostedTS (xaccSplitGetParent (gnc_lot_get_earliest_split (lotA))); 00694 00695 if (ib) 00696 db = gncInvoiceGetDateDue (ib); 00697 else 00698 db = xaccTransRetDatePostedTS (xaccSplitGetParent (gnc_lot_get_earliest_split (lotB))); 00699 00700 return timespec_cmp (&da, &db); 00701 } 00702 00703 static gboolean use_reversed_payment_amounts(const GncOwner *owner) 00704 { 00705 g_assert(owner); 00706 return (gncOwnerGetType (owner) == GNC_OWNER_CUSTOMER); 00707 } 00708 00709 00710 /* 00711 * Create a payment of "amount" for the owner and return 00712 * the new lot associated with this payment. 00713 */ 00714 GNCLot * 00715 gncOwnerCreatePaymentLot (const GncOwner *owner, Transaction *txn, 00716 Account *posted_acc, Account *xfer_acc, 00717 gnc_numeric amount, gnc_numeric exch, Timespec date, 00718 const char *memo, const char *num) 00719 { 00720 QofBook *book; 00721 Split *split; 00722 const char *name; 00723 gnc_commodity *commodity; 00724 gboolean reverse; 00725 gnc_numeric payment_value = amount; 00726 Split *xfer_split = NULL; 00727 GNCLot *payment_lot; 00728 00729 /* Verify our arguments */ 00730 if (!owner || !posted_acc || !xfer_acc) return NULL; 00731 g_return_val_if_fail (owner->owner.undefined != NULL, NULL); 00732 00733 /* Compute the ancillary data */ 00734 book = gnc_account_get_book (posted_acc); 00735 name = gncOwnerGetName (gncOwnerGetEndOwner ((GncOwner*)owner)); 00736 commodity = gncOwnerGetCurrency (owner); 00737 reverse = use_reversed_payment_amounts(owner); 00738 00739 if (txn) 00740 { 00741 /* Pre-existing transaction was specified. We completely clear it, 00742 * except for the split in the transfer account, unless the 00743 * transaction can't be reused (wrong currency, wrong transfer account). 00744 * In that case, the transaction is simply removed and an new 00745 * one created. */ 00746 00747 xfer_split = xaccTransFindSplitByAccount(txn, xfer_acc); 00748 00749 if (xaccTransGetCurrency(txn) != gncOwnerGetCurrency (owner)) 00750 { 00751 g_message("Uh oh, mismatching currency/commodity between selected transaction and owner. We fall back to manual creation of a new transaction."); 00752 xfer_split = NULL; 00753 } 00754 00755 if (!xfer_split) 00756 { 00757 g_message("Huh? Asset account not found anymore. Fully deleting old txn and now creating a new one."); 00758 00759 xaccTransBeginEdit (txn); 00760 xaccTransDestroy (txn); 00761 xaccTransCommitEdit (txn); 00762 00763 txn = NULL; 00764 } 00765 else 00766 { 00767 int i = 0; 00768 xaccTransBeginEdit (txn); 00769 while (i < xaccTransCountSplits(txn)) 00770 { 00771 Split *split = xaccTransGetSplit (txn, i); 00772 if (split == xfer_split) 00773 { 00774 ++i; 00775 } 00776 else 00777 { 00778 xaccSplitDestroy(split); 00779 } 00780 } 00781 xaccTransCommitEdit (txn); 00782 } 00783 } 00784 00785 /* Create the transaction if we don't have one yet */ 00786 if (!txn) 00787 txn = xaccMallocTransaction (book); 00788 00789 /* Insert a split for the transfer account if we don't have one yet */ 00790 if (!xfer_split) 00791 { 00792 xaccTransBeginEdit (txn); 00793 00794 /* Set up the transaction */ 00795 xaccTransSetDescription (txn, name ? name : ""); 00796 xaccTransSetNum (txn, num); 00797 xaccTransSetCurrency (txn, commodity); 00798 xaccTransSetDateEnteredSecs (txn, time(NULL)); 00799 xaccTransSetDatePostedTS (txn, &date); 00800 xaccTransSetTxnType (txn, TXN_TYPE_PAYMENT); 00801 00802 00803 /* The split for the transfer account */ 00804 split = xaccMallocSplit (book); 00805 xaccSplitSetMemo (split, memo); 00806 xaccSplitSetAction (split, _("Payment")); 00807 xaccAccountBeginEdit (xfer_acc); 00808 xaccAccountInsertSplit (xfer_acc, split); 00809 xaccAccountCommitEdit (xfer_acc); 00810 xaccTransAppendSplit (txn, split); 00811 00812 if (gnc_commodity_equal(xaccAccountGetCommodity(xfer_acc), commodity)) 00813 { 00814 xaccSplitSetBaseValue (split, reverse ? amount : 00815 gnc_numeric_neg (amount), commodity); 00816 } 00817 else 00818 { 00819 /* Need to value the payment in terms of the owner commodity */ 00820 xaccSplitSetAmount(split, reverse ? amount : gnc_numeric_neg (amount)); 00821 payment_value = gnc_numeric_mul(amount, exch, GNC_DENOM_AUTO, GNC_HOW_RND_ROUND_HALF_UP); 00822 xaccSplitSetValue(split, reverse ? payment_value : gnc_numeric_neg(payment_value)); 00823 } 00824 } 00825 00826 /* Add a split in the post account */ 00827 split = xaccMallocSplit (book); 00828 xaccSplitSetMemo (split, memo); 00829 xaccSplitSetAction (split, _("Payment")); 00830 xaccAccountBeginEdit (posted_acc); 00831 xaccAccountInsertSplit (posted_acc, split); 00832 xaccAccountCommitEdit (posted_acc); 00833 xaccTransAppendSplit (txn, split); 00834 xaccSplitSetBaseValue (split, reverse ? gnc_numeric_neg (amount) : amount, commodity); 00835 00836 /* Create a new lot for the payment */ 00837 payment_lot = gnc_lot_new (book); 00838 gncOwnerAttachToLot (owner, payment_lot); 00839 gnc_lot_add_split (payment_lot, split); 00840 00841 00842 /* Commit this new transaction */ 00843 xaccTransCommitEdit (txn); 00844 00845 return payment_lot; 00846 } 00847 00848 void gncOwnerAutoApplyPaymentsWithLots (const GncOwner *owner, GList *lots) 00849 { 00850 GList *base_iter; 00851 00852 /* General note: in the code below the term "payment" can 00853 * both mean a true payment or a document of 00854 * the opposite sign (invoice vs credit note) relative to 00855 * the lot being processed. In general this function will 00856 * perform a balancing action on a set of lots, so you 00857 * will also find frequent references to balancing instead. */ 00858 00859 /* Payments can only be applied when at least an owner 00860 * and a list of lots to use are given */ 00861 if (!owner) return; 00862 if (!lots) return; 00863 00864 for (base_iter = lots; base_iter; base_iter = base_iter->next) 00865 { 00866 GNCLot *base_lot = base_iter->data; 00867 QofBook *book; 00868 Account *acct; 00869 const gchar *name; 00870 GList *lot_list, *lot_iter; 00871 Transaction *txn = NULL; 00872 gnc_numeric base_lot_bal, val_to_pay, val_paid = { 0, 1 }; 00873 gboolean base_bal_is_pos; 00874 const gchar *action, *memo; 00875 00876 /* Only attempt to apply payments to open lots. 00877 * Note that due to the iterative nature of this function lots 00878 * in the list may become closed before they are evaluated as 00879 * base lot, so we should check this for each lot. */ 00880 base_lot_bal = gnc_lot_get_balance (base_lot); 00881 if (gnc_numeric_zero_p (base_lot_bal)) 00882 continue; 00883 00884 book = gnc_lot_get_book (base_lot); 00885 acct = gnc_lot_get_account (base_lot); 00886 name = gncOwnerGetName (gncOwnerGetEndOwner (owner)); 00887 lot_list = base_iter->next; 00888 00889 /* Strings used when creating splits later on. */ 00890 action = _("Lot Link"); 00891 memo = _("Internal link between invoice and payment lots"); 00892 00893 /* Note: to balance the lot the payment to assign 00894 * must have the opposite sign of the existing lot balance */ 00895 val_to_pay = gnc_numeric_neg (base_lot_bal); 00896 base_bal_is_pos = gnc_numeric_positive_p (base_lot_bal); 00897 00898 00899 /* Create splits in a linking transaction between lots until 00900 * - either the invoice lot is balanced 00901 * - or there are no more balancing lots. 00902 */ 00903 for (lot_iter = lot_list; lot_iter; lot_iter = lot_iter->next) 00904 { 00905 gnc_numeric payment_lot_balance; 00906 Split *split; 00907 Account *bal_acct; 00908 gnc_numeric split_amt; 00909 00910 GNCLot *balancing_lot = lot_iter->data; 00911 00912 /* Only attempt to use open lots to balance the base lot. 00913 * Note that due to the iterative nature of this function lots 00914 * in the list may become closed before they are evaluated as 00915 * base lot, so we should check this for each lot. */ 00916 if (gnc_lot_is_closed (balancing_lot)) 00917 continue; 00918 00919 /* Balancing transactions for invoice/payments can only happen 00920 * in the same account. */ 00921 bal_acct = gnc_lot_get_account (balancing_lot); 00922 if (acct != bal_acct) 00923 continue; 00924 00925 payment_lot_balance = gnc_lot_get_balance (balancing_lot); 00926 00927 /* Only attempt to balance if the base lot and balancing lot are 00928 * of the opposite sign. (Otherwise we would increase the balance 00929 * of the lot - Duh */ 00930 if (base_bal_is_pos == gnc_numeric_positive_p (payment_lot_balance)) 00931 continue; 00932 00933 /* 00934 * If there is less to pay than there's open in the lot; we're done -- apply the base_lot_vale. 00935 * Note that payment_value and balance are opposite in sign, so we have to compare absolute values here 00936 * 00937 * Otherwise, apply the balance, subtract that from the payment_value, 00938 * and move on to the next one. 00939 */ 00940 if (gnc_numeric_compare (gnc_numeric_abs (val_to_pay), gnc_numeric_abs (payment_lot_balance)) <= 0) 00941 { 00942 /* abs(val_to_pay) <= abs(balance) */ 00943 split_amt = val_to_pay; 00944 } 00945 else 00946 { 00947 /* abs(val_to_pay) > abs(balance) 00948 * Remember payment_value and balance are opposite in sign, 00949 * and we want a payment to neutralize the current balance 00950 * so we need to negate here */ 00951 split_amt = payment_lot_balance; 00952 } 00953 00954 /* If not created yet, create a new transaction linking 00955 * the base lot and the balancing lot(s) */ 00956 if (!txn) 00957 { 00958 Timespec ts = xaccTransRetDatePostedTS (xaccSplitGetParent (gnc_lot_get_latest_split (base_lot))); 00959 00960 xaccAccountBeginEdit (acct); 00961 00962 txn = xaccMallocTransaction (book); 00963 xaccTransBeginEdit (txn); 00964 00965 xaccTransSetDescription (txn, name ? name : ""); 00966 xaccTransSetCurrency (txn, xaccAccountGetCommodity(acct)); 00967 xaccTransSetDateEnteredSecs (txn, time(NULL)); 00968 xaccTransSetDatePostedTS (txn, &ts); 00969 xaccTransSetTxnType (txn, TXN_TYPE_LINK); 00970 } 00971 00972 /* Create the split for this link in current balancing lot */ 00973 split = xaccMallocSplit (book); 00974 xaccSplitSetMemo (split, memo); 00975 xaccSplitSetAction (split, action); 00976 xaccAccountInsertSplit (acct, split); 00977 xaccTransAppendSplit (txn, split); 00978 xaccSplitSetBaseValue (split, gnc_numeric_neg (split_amt), xaccAccountGetCommodity(acct)); 00979 gnc_lot_add_split (balancing_lot, split); 00980 00981 /* If the balancing lot was linked to a document (invoice/credit note), 00982 * send an event for it as well so it gets potentially updated as paid */ 00983 { 00984 GncInvoice *this_invoice = gncInvoiceGetInvoiceFromLot(balancing_lot); 00985 if (this_invoice) 00986 qof_event_gen (QOF_INSTANCE(this_invoice), QOF_EVENT_MODIFY, NULL); 00987 } 00988 00989 val_paid = gnc_numeric_add (val_paid, split_amt, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); 00990 val_to_pay = gnc_numeric_sub (val_to_pay, split_amt, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); 00991 if (gnc_numeric_zero_p (val_to_pay)) 00992 break; 00993 } 00994 00995 00996 /* If the above loop managed to create a transaction and some balancing splits, 00997 * create the final split for the link transaction in the base lot */ 00998 if (txn) 00999 { 01000 GncInvoice *this_invoice; 01001 Split *split = xaccMallocSplit (book); 01002 01003 xaccSplitSetMemo (split, memo); 01004 xaccSplitSetAction (split, action); 01005 xaccAccountInsertSplit (acct, split); 01006 xaccTransAppendSplit (txn, split); 01007 xaccSplitSetBaseValue (split, val_paid, xaccAccountGetCommodity(acct)); 01008 gnc_lot_add_split (base_lot, split); 01009 01010 xaccTransCommitEdit (txn); 01011 xaccAccountCommitEdit (acct); 01012 01013 /* If the base lot was linked to a document (invoice/credit note), 01014 * send an event for it as well so it gets potentially updated as paid */ 01015 this_invoice = gncInvoiceGetInvoiceFromLot(base_lot); 01016 if (this_invoice) 01017 qof_event_gen (QOF_INSTANCE(this_invoice), QOF_EVENT_MODIFY, NULL); 01018 01019 } 01020 01021 } 01022 } 01023 01024 /* 01025 * Create a payment of "amount" for the owner and match it with 01026 * the set of lots passed in. If not lots were given all open 01027 * lots for the owner are considered. 01028 */ 01029 void 01030 gncOwnerApplyPayment (const GncOwner *owner, Transaction *txn, GList *lots, 01031 Account *posted_acc, Account *xfer_acc, 01032 gnc_numeric amount, gnc_numeric exch, Timespec date, 01033 const char *memo, const char *num) 01034 { 01035 GNCLot *payment_lot; 01036 GList *selected_lots; 01037 01038 /* Verify our arguments */ 01039 if (!owner || !posted_acc || !xfer_acc) return; 01040 g_return_if_fail (owner->owner.undefined); 01041 01042 /* Create a lot for this payment */ 01043 payment_lot = gncOwnerCreatePaymentLot (owner, txn, posted_acc, xfer_acc, 01044 amount, exch, date, memo, num); 01045 01046 if (lots) 01047 selected_lots = lots; 01048 else 01049 selected_lots = xaccAccountFindOpenLots (posted_acc, gncOwnerLotMatchOwnerFunc, 01050 (gpointer)owner, NULL); 01051 01052 /* And link the selected lots and the payment lot together as well as possible. 01053 * If the payment was bigger than the selected documents/overpayments, only 01054 * part of the payment will be used. Similarly if more documents were selected 01055 * than the payment value set, not all documents will be marked as paid. */ 01056 if (payment_lot) 01057 selected_lots = g_list_prepend (selected_lots, payment_lot); 01058 gncOwnerAutoApplyPaymentsWithLots (owner, selected_lots); 01059 } 01060 01061 GList * 01062 gncOwnerGetAccountTypesList (const GncOwner *owner) 01063 { 01064 g_return_val_if_fail (owner, NULL); 01065 01066 switch (gncOwnerGetType (owner)) 01067 { 01068 case GNC_OWNER_CUSTOMER: 01069 return (g_list_prepend (NULL, (gpointer)ACCT_TYPE_RECEIVABLE)); 01070 case GNC_OWNER_VENDOR: 01071 case GNC_OWNER_EMPLOYEE: 01072 return (g_list_prepend (NULL, (gpointer)ACCT_TYPE_PAYABLE)); 01073 break; 01074 default: 01075 return (g_list_prepend (NULL, (gpointer)ACCT_TYPE_NONE)); 01076 } 01077 } 01078 01079 GList * 01080 gncOwnerGetCommoditiesList (const GncOwner *owner) 01081 { 01082 g_return_val_if_fail (owner, NULL); 01083 g_return_val_if_fail (gncOwnerGetCurrency(owner), NULL); 01084 01085 return (g_list_prepend (NULL, gncOwnerGetCurrency(owner))); 01086 } 01087 01088 /*********************************************************************/ 01089 /* Owner balance calculation routines */ 01090 01091 /* 01092 * Given an owner, extract the open balance from the owner and then 01093 * convert it to the desired currency. 01094 */ 01095 gnc_numeric 01096 gncOwnerGetBalanceInCurrency (const GncOwner *owner, 01097 const gnc_commodity *report_currency) 01098 { 01099 gnc_numeric balance = gnc_numeric_zero (); 01100 GList *acct_list, *acct_node, *acct_types, *lot_list = NULL, *lot_node; 01101 QofBook *book; 01102 gnc_commodity *owner_currency; 01103 GNCPriceDB *pdb; 01104 01105 g_return_val_if_fail (owner, gnc_numeric_zero ()); 01106 01107 /* Get account list */ 01108 book = qof_instance_get_book (qofOwnerGetOwner (owner)); 01109 acct_list = gnc_account_get_descendants (gnc_book_get_root_account (book)); 01110 acct_types = gncOwnerGetAccountTypesList (owner); 01111 owner_currency = gncOwnerGetCurrency (owner); 01112 01113 /* For each account */ 01114 for (acct_node = acct_list; acct_node; acct_node = acct_node->next) 01115 { 01116 Account *account = acct_node->data; 01117 01118 /* Check if this account can have lots for the owner, otherwise skip to next */ 01119 if (g_list_index (acct_types, (gpointer)xaccAccountGetType (account)) 01120 == -1) 01121 continue; 01122 01123 01124 if (!gnc_commodity_equal (owner_currency, xaccAccountGetCommodity (account))) 01125 continue; 01126 01127 /* Get a list of open lots for this owner and account */ 01128 lot_list = xaccAccountFindOpenLots (account, gncOwnerLotMatchOwnerFunc, 01129 (gpointer)owner, NULL); 01130 /* For each lot */ 01131 for (lot_node = lot_list; lot_node; lot_node = lot_node->next) 01132 { 01133 GNCLot *lot = lot_node->data; 01134 gnc_numeric lot_balance = gnc_lot_get_balance (lot); 01135 balance = gnc_numeric_add (balance, lot_balance, 01136 gnc_commodity_get_fraction (owner_currency), GNC_HOW_RND_ROUND_HALF_UP); 01137 } 01138 } 01139 01140 pdb = gnc_pricedb_get_db (book); 01141 01142 if (report_currency) 01143 balance = gnc_pricedb_convert_balance_latest_price ( 01144 pdb, balance, owner_currency, report_currency); 01145 01146 return balance; 01147 } 01148 01149 01150 /* XXX: Yea, this is broken, but it should work fine for Queries. 01151 * We're single-threaded, right? 01152 */ 01153 static GncOwner * 01154 owner_from_lot (GNCLot *lot) 01155 { 01156 static GncOwner owner; 01157 01158 if (!lot) return NULL; 01159 if (gncOwnerGetOwnerFromLot (lot, &owner)) 01160 return &owner; 01161 01162 return NULL; 01163 } 01164 01165 static void 01166 reg_lot (void) 01167 { 01168 static QofParam params[] = 01169 { 01170 { OWNER_FROM_LOT, _GNC_MOD_NAME, (QofAccessFunc)owner_from_lot, NULL }, 01171 { NULL }, 01172 }; 01173 01174 qof_class_register (GNC_ID_LOT, NULL, params); 01175 } 01176 01177 gboolean gncOwnerGetOwnerFromTypeGuid (QofBook *book, GncOwner *owner, QofIdType type, GncGUID *guid) 01178 { 01179 if (!book || !owner || !type || !guid) return FALSE; 01180 01181 if (0 == safe_strcmp(type, GNC_ID_CUSTOMER)) 01182 { 01183 GncCustomer *customer = gncCustomerLookup(book, guid); 01184 gncOwnerInitCustomer(owner, customer); 01185 return (NULL != customer); 01186 } 01187 else if (0 == safe_strcmp(type, GNC_ID_JOB)) 01188 { 01189 GncJob *job = gncJobLookup(book, guid); 01190 gncOwnerInitJob(owner, job); 01191 return (NULL != job); 01192 } 01193 else if (0 == safe_strcmp(type, GNC_ID_VENDOR)) 01194 { 01195 GncVendor *vendor = gncVendorLookup(book, guid); 01196 gncOwnerInitVendor(owner, vendor); 01197 return (NULL != vendor); 01198 } 01199 else if (0 == safe_strcmp(type, GNC_ID_EMPLOYEE)) 01200 { 01201 GncEmployee *employee = gncEmployeeLookup(book, guid); 01202 gncOwnerInitEmployee(owner, employee); 01203 return (NULL != employee); 01204 } 01205 return 0; 01206 } 01207 01208 gboolean gncOwnerRegister (void) 01209 { 01210 static QofParam params[] = 01211 { 01212 { OWNER_TYPE, QOF_TYPE_INT64, (QofAccessFunc)gncOwnerGetType, NULL }, 01213 { OWNER_CUSTOMER, GNC_ID_CUSTOMER, (QofAccessFunc)gncOwnerGetCustomer, NULL }, 01214 { OWNER_JOB, GNC_ID_JOB, (QofAccessFunc)gncOwnerGetJob, NULL }, 01215 { OWNER_VENDOR, GNC_ID_VENDOR, (QofAccessFunc)gncOwnerGetVendor, NULL }, 01216 { OWNER_EMPLOYEE, GNC_ID_EMPLOYEE, (QofAccessFunc)gncOwnerGetEmployee, NULL }, 01217 { OWNER_PARENT, GNC_ID_OWNER, (QofAccessFunc)gncOwnerGetEndOwner, NULL }, 01218 { OWNER_PARENTG, QOF_TYPE_GUID, (QofAccessFunc)gncOwnerGetEndGUID, NULL }, 01219 { OWNER_NAME, QOF_TYPE_STRING, (QofAccessFunc)gncOwnerGetName, NULL }, 01220 { QOF_PARAM_GUID, QOF_TYPE_GUID, (QofAccessFunc)gncOwnerGetGUID, NULL }, 01221 { NULL }, 01222 }; 01223 01224 qof_class_register (GNC_ID_OWNER, (QofSortFunc)gncOwnerCompare, params); 01225 reg_lot (); 01226 01227 return TRUE; 01228 }
1.7.4