Пример #1
0
def insert_adoption_from_form(dbo, username, data, creating=[]):
    """
    Inserts a movement from the workflow adopt an animal screen.
    Returns the new movement id
    creating is an ongoing list of animals we're already going to
    create adoptions for. It prevents a never ending recursive loop
    of animal1 being bonded to animal2 that's bonded to animal1, etc.
    """
    l = dbo.locale
    # Validate that we have a movement date before doing anthing
    if None == utils.df_kd(data, "movementdate", l):
        raise utils.ASMValidationError(
            i18n._("Adoption movements must have a valid adoption date.", l))
    # Get the animal record for this adoption
    a = animal.get_animal(dbo, utils.df_ki(data, "animal"))
    if a is None:
        raise utils.ASMValidationError(
            "Adoption POST has an invalid animal ID: %d" %
            utils.df_ki(data, "animal"))
    al.debug(
        "Creating adoption for %d (%s - %s)" %
        (a["ID"], a["SHELTERCODE"], a["ANIMALNAME"]),
        "movement.insert_adoption_from_form", dbo)
    creating.append(a["ID"])
    # If the animal is bonded to other animals, we call this function
    # again with a copy of the data and the bonded animal substituted
    # so we can create their adoption records too.
    if a["BONDEDANIMALID"] is not None and a["BONDEDANIMALID"] != 0 and a[
            "BONDEDANIMALID"] not in creating:
        al.debug(
            "Found bond to animal %d, creating adoption..." %
            a["BONDEDANIMALID"], "movement.insert_adoption_from_form", dbo)
        newdata = dict(data)
        newdata["animal"] = str(a["BONDEDANIMALID"])
        insert_adoption_from_form(dbo, username, newdata, creating)
    if a["BONDEDANIMAL2ID"] is not None and a["BONDEDANIMAL2ID"] != 0 and a[
            "BONDEDANIMAL2ID"] not in creating:
        al.debug(
            "Found bond to animal %d, creating adoption..." %
            a["BONDEDANIMAL2ID"], "movement.insert_adoption_from_form", dbo)
        newdata = dict(data)
        newdata["animal"] = str(a["BONDEDANIMAL2ID"])
        insert_adoption_from_form(dbo, username, newdata, creating)
    cancel_reserves = configuration.cancel_reserves_on_adoption(dbo)
    # Prepare a dictionary of data for the movement table via insert_movement_from_form
    move_dict = {
        "person": utils.df_ks(data, "person"),
        "animal": utils.df_ks(data, "animal"),
        "adoptionno": utils.df_ks(data, "movementnumber"),
        "movementdate": utils.df_ks(data, "movementdate"),
        "type": str(ADOPTION),
        "donation": utils.df_ks(data, "amount"),
        "insurance": utils.df_ks(data, "insurance"),
        "returncategory": configuration.default_return_reason(dbo),
        "trial": utils.df_ks(data, "trial"),
        "trialenddate": utils.df_ks(data, "trialenddate")
    }
    # Is this animal currently on foster? If so, return the foster
    fm = get_animal_movements(dbo, utils.df_ki(data, "animal"))
    for m in fm:
        if m["MOVEMENTTYPE"] == FOSTER and m["RETURNDATE"] is None:
            return_movement(dbo, m["ID"], utils.df_ki(data, "animal"),
                            utils.df_kd(data, "movementdate", l))
    # Is this animal current at a retailer? If so, return it from the
    # retailer and set the originalretailermovement and retailerid fields
    # on our new adoption movement so it can be linked back
    for m in fm:
        if m["MOVEMENTTYPE"] == RETAILER and m["RETURNDATE"] is None:
            return_movement(dbo, m["ID"], utils.df_ki(data, "animal"),
                            utils.df_kd(data, "movementdate", l))
            move_dict["originalretailermovement"] = str(m["ID"])
            move_dict["retailer"] = str(m["OWNERID"])
    # Did we say we'd like to flag the owner as homechecked?
    if utils.df_kc(data, "homechecked") == 1:
        db.execute(dbo, "UPDATE owner SET IDCheck = 1, DateLastHomeChecked = %s WHERE ID = %d" % \
            ( db.dd(i18n.now(dbo.timezone)), utils.df_ki(data, "person")))
    # If the animal was flagged as not available for adoption, then it
    # shouldn't be since we've just adopted it.
    db.execute(
        dbo, "UPDATE animal SET IsNotAvailableForAdoption = 0 WHERE ID = %s" %
        utils.df_ks(data, "animal"))
    # Is the animal reserved to the person adopting?
    movementid = 0
    for m in fm:
        if m["MOVEMENTTYPE"] == NO_MOVEMENT and m["RESERVATIONDATE"] is not None \
            and m["RESERVATIONCANCELLEDDATE"] is None and m["ANIMALID"] == utils.df_ki(data, "animal") \
            and m["OWNERID"] == utils.df_ki(data, "person"):
            # yes - update the existing movement
            movementid = m["ID"]
            move_dict["movementid"] = str(movementid)
            move_dict["adoptionno"] = utils.padleft(movementid, 6)
            move_dict["reservationdate"] = str(
                i18n.python2display(l, m["RESERVATIONDATE"]))
            move_dict["comments"] = utils.nulltostr(m["COMMENTS"])
            break
        elif cancel_reserves and m["MOVEMENTTYPE"] == NO_MOVEMENT and m["RESERVATIONDATE"] is not None \
            and m["RESERVATIONCANCELLEDDATE"] is None:
            # no, but it's reserved to someone else and we're cancelling
            # reserves on adoption
            db.execute(dbo, "UPDATE adoption SET ReservationCancelledDate = %s WHERE ID = %d" % \
                ( utils.df_d(data, "movementdate", l), m["ID"] ))
    if movementid != 0:
        update_movement_from_form(dbo, username, move_dict)
    else:
        movementid = insert_movement_from_form(dbo, username, move_dict)
    # Create the donation if there is one
    donation_amount = int(utils.df_m(data, "amount", l))
    if donation_amount > 0:
        due = ""
        received = utils.df_ks(data, "movementdate")
        if configuration.movement_donations_default_due(dbo):
            due = utils.df_ks(data, "movementdate")
            received = ""
        don_dict = {
            "person": utils.df_ks(data, "person"),
            "animal": utils.df_ks(data, "animal"),
            "movement": str(movementid),
            "type": utils.df_ks(data, "donationtype"),
            "payment": utils.df_ks(data, "payment"),
            "frequency": "0",
            "amount": utils.df_ks(data, "amount"),
            "due": due,
            "received": received,
            "giftaid": utils.df_ks(data, "giftaid")
        }
        financial.insert_donation_from_form(dbo, username, don_dict)
    # And a second donation if there is one
    donation_amount = int(utils.df_m(data, "amount2", l))
    if donation_amount > 0:
        due = ""
        received = utils.df_ks(data, "movementdate")
        if configuration.movement_donations_default_due(dbo):
            due = utils.df_ks(data, "movementdate")
            received = ""
        don_dict = {
            "person": utils.df_ks(data, "person"),
            "animal": utils.df_ks(data, "animal"),
            "movement": str(movementid),
            "type": utils.df_ks(data, "donationtype2"),
            "payment": utils.df_ks(data, "payment2"),
            "frequency": "0",
            "amount": utils.df_ks(data, "amount2"),
            "due": due,
            "received": received,
            "giftaid": utils.df_ks(data, "giftaid")
        }
        financial.insert_donation_from_form(dbo, username, don_dict)
    # Then any boarding cost record
    cost_amount = int(utils.df_m(data, "costamount", l))
    cost_type = utils.df_ks(data, "costtype")
    cost_create = utils.df_ki(data, "costcreate")
    if cost_amount > 0 and cost_type != "" and cost_create == 1:
        boc_dict = {
            "animalid": utils.df_ks(data, "animal"),
            "type": cost_type,
            "costdate": utils.df_ks(data, "movementdate"),
            "cost": utils.df_ks(data, "costamount")
        }
        animal.insert_cost_from_form(dbo, username, boc_dict)
    return movementid
Пример #2
0
def insert_adoption_from_form(dbo, username, data, creating = []):
    """
    Inserts a movement from the workflow adopt an animal screen.
    Returns the new movement id
    creating is an ongoing list of animals we're already going to
    create adoptions for. It prevents a never ending recursive loop
    of animal1 being bonded to animal2 that's bonded to animal1, etc.
    """
    l = dbo.locale
    # Validate that we have a movement date before doing anthing
    if None == utils.df_kd(data, "movementdate", l):
        raise utils.ASMValidationError(i18n._("Adoption movements must have a valid adoption date.", l))
    # Get the animal record for this adoption
    a = animal.get_animal(dbo, utils.df_ki(data, "animal"))
    if a is None:
        raise utils.ASMValidationError("Adoption POST has an invalid animal ID: %d" % utils.df_ki(data, "animal"))
    al.debug("Creating adoption for %d (%s - %s)" % (a["ID"], a["SHELTERCODE"], a["ANIMALNAME"]), "movement.insert_adoption_from_form", dbo)
    creating.append(a["ID"])
    # If the animal is bonded to other animals, we call this function
    # again with a copy of the data and the bonded animal substituted
    # so we can create their adoption records too.
    if a["BONDEDANIMALID"] is not None and a["BONDEDANIMALID"] != 0 and a["BONDEDANIMALID"] not in creating:
        al.debug("Found bond to animal %d, creating adoption..." % a["BONDEDANIMALID"], "movement.insert_adoption_from_form", dbo)
        newdata = dict(data)
        newdata["animal"] = str(a["BONDEDANIMALID"])
        insert_adoption_from_form(dbo, username, newdata, creating)
    if a["BONDEDANIMAL2ID"] is not None and a["BONDEDANIMAL2ID"] != 0 and a["BONDEDANIMAL2ID"] not in creating:
        al.debug("Found bond to animal %d, creating adoption..." % a["BONDEDANIMAL2ID"], "movement.insert_adoption_from_form", dbo)
        newdata = dict(data)
        newdata["animal"] = str(a["BONDEDANIMAL2ID"])
        insert_adoption_from_form(dbo, username, newdata, creating)
    cancel_reserves = configuration.cancel_reserves_on_adoption(dbo)
    # Prepare a dictionary of data for the movement table via insert_movement_from_form
    move_dict = {
        "person"                : utils.df_ks(data, "person"),
        "animal"                : utils.df_ks(data, "animal"),
        "adoptionno"            : utils.df_ks(data, "movementnumber"),
        "movementdate"          : utils.df_ks(data, "movementdate"),
        "type"                  : str(ADOPTION),
        "donation"              : utils.df_ks(data, "amount"),
        "insurance"             : utils.df_ks(data, "insurance"),
        "returncategory"        : configuration.default_return_reason(dbo),
        "trial"                 : utils.df_ks(data, "trial"),
        "trialenddate"          : utils.df_ks(data, "trialenddate")
    }
    # Is this animal currently on foster? If so, return the foster
    fm = get_animal_movements(dbo, utils.df_ki(data, "animal"))
    for m in fm:
        if m["MOVEMENTTYPE"] == FOSTER and m["RETURNDATE"] is None:
            return_movement(dbo, m["ID"], utils.df_ki(data, "animal"), utils.df_kd(data, "movementdate", l))
    # Is this animal current at a retailer? If so, return it from the
    # retailer and set the originalretailermovement and retailerid fields
    # on our new adoption movement so it can be linked back
    for m in fm:
        if m["MOVEMENTTYPE"] == RETAILER and m["RETURNDATE"] is None:
            return_movement(dbo, m["ID"], utils.df_ki(data, "animal"), utils.df_kd(data, "movementdate", l))
            move_dict["originalretailermovement"] = str(m["ID"])
            move_dict["retailer"] = str(m["OWNERID"])
    # Did we say we'd like to flag the owner as homechecked?
    if utils.df_kc(data, "homechecked") == 1:
        db.execute(dbo, "UPDATE owner SET IDCheck = 1, DateLastHomeChecked = %s WHERE ID = %d" % \
            ( db.dd(i18n.now(dbo.timezone)), utils.df_ki(data, "person")))
    # If the animal was flagged as not available for adoption, then it
    # shouldn't be since we've just adopted it.
    db.execute(dbo, "UPDATE animal SET IsNotAvailableForAdoption = 0 WHERE ID = %s" % utils.df_ks(data, "animal"))
    # Is the animal reserved to the person adopting? 
    movementid = 0
    for m in fm:
        if m["MOVEMENTTYPE"] == NO_MOVEMENT and m["RESERVATIONDATE"] is not None \
            and m["RESERVATIONCANCELLEDDATE"] is None and m["ANIMALID"] == utils.df_ki(data, "animal") \
            and m["OWNERID"] == utils.df_ki(data, "person"):
            # yes - update the existing movement
            movementid = m["ID"]
            move_dict["movementid"] = str(movementid)
            move_dict["adoptionno"] = utils.padleft(movementid, 6)
            move_dict["reservationdate"] = str(i18n.python2display(l, m["RESERVATIONDATE"]))
            move_dict["comments"] = utils.nulltostr(m["COMMENTS"])
            break
        elif cancel_reserves and m["MOVEMENTTYPE"] == NO_MOVEMENT and m["RESERVATIONDATE"] is not None \
            and m["RESERVATIONCANCELLEDDATE"] is None:
            # no, but it's reserved to someone else and we're cancelling
            # reserves on adoption
            db.execute(dbo, "UPDATE adoption SET ReservationCancelledDate = %s WHERE ID = %d" % \
                ( utils.df_d(data, "movementdate", l), m["ID"] ))
    if movementid != 0:
        update_movement_from_form(dbo, username, move_dict)
    else:
        movementid = insert_movement_from_form(dbo, username, move_dict)
    # Create the donation if there is one
    donation_amount = int(utils.df_m(data, "amount", l))
    if donation_amount > 0:
        due = ""
        received = utils.df_ks(data, "movementdate")
        if configuration.movement_donations_default_due(dbo):
            due = utils.df_ks(data, "movementdate")
            received = ""
        don_dict = {
            "person"                : utils.df_ks(data, "person"),
            "animal"                : utils.df_ks(data, "animal"),
            "movement"              : str(movementid),
            "type"                  : utils.df_ks(data, "donationtype"),
            "payment"               : utils.df_ks(data, "payment"),
            "frequency"             : "0",
            "amount"                : utils.df_ks(data, "amount"),
            "due"                   : due,
            "received"              : received,
            "giftaid"               : utils.df_ks(data, "giftaid")
        }
        financial.insert_donation_from_form(dbo, username, don_dict)
    # And a second donation if there is one
    donation_amount = int(utils.df_m(data, "amount2", l))
    if donation_amount > 0:
        due = ""
        received = utils.df_ks(data, "movementdate")
        if configuration.movement_donations_default_due(dbo):
            due = utils.df_ks(data, "movementdate")
            received = ""
        don_dict = {
            "person"                : utils.df_ks(data, "person"),
            "animal"                : utils.df_ks(data, "animal"),
            "movement"              : str(movementid),
            "type"                  : utils.df_ks(data, "donationtype2"),
            "payment"               : utils.df_ks(data, "payment2"),
            "frequency"             : "0",
            "amount"                : utils.df_ks(data, "amount2"),
            "due"                   : due,
            "received"              : received,
            "giftaid"               : utils.df_ks(data, "giftaid")
        }
        financial.insert_donation_from_form(dbo, username, don_dict)
    # Then any boarding cost record
    cost_amount = int(utils.df_m(data, "costamount", l))
    cost_type = utils.df_ks(data, "costtype")
    cost_create = utils.df_ki(data, "costcreate")
    if cost_amount > 0 and cost_type != "" and cost_create == 1:
        boc_dict = {
            "animalid"          : utils.df_ks(data, "animal"),
            "type"              : cost_type,
            "costdate"          : utils.df_ks(data, "movementdate"),
            "cost"              : utils.df_ks(data, "costamount")
        }
        animal.insert_cost_from_form(dbo, username, boc_dict)
    return movementid
Пример #3
0
def validate_movement_form_data(dbo, data):
    """
    Verifies that form data is valid for a movement
    """
    l = dbo.locale
    movementid = utils.df_ki(data, "movementid")
    movement = None
    if movementid != 0:
        movement = db.query(
            dbo, "SELECT * FROM adoption WHERE ID = %d" % movementid)[0]
    adoptionno = utils.df_ks(data, "adoptionno")
    movementtype = utils.df_ki(data, "type")
    movementdate = utils.df_kd(data, "movementdate", l)
    returndate = utils.df_kd(data, "returndate", l)
    reservationdate = utils.df_kd(data, "reservationdate", l)
    reservationcancelled = utils.df_kd(data, "reservationcancelled", l)
    personid = utils.df_ki(data, "person")
    animalid = utils.df_ki(data, "animal")
    retailerid = utils.df_ki(data, "retailer")
    al.debug(
        "validating saved movement %d for animal %d" % (movementid, animalid),
        "movement.validate_movement_form_data", dbo)
    # If we have a date but no type, get rid of it
    if movementdate is None and movementtype == 0:
        data["movementdate"] = ""
        al.debug("blank date and type", "movement.validate_movement_form_data",
                 dbo)
    # If we've got a type, but no date, default today
    if movementtype > 0 and movementdate is None:
        movementdate = i18n.now()
        data["movementdate"] = i18n.python2display(l, movementdate)
        al.debug("type set and no date, defaulting today",
                 "movement.validate_movement_form_data", dbo)
    # If we've got a reserve cancellation without a reserve, remove it
    if reservationdate is None and reservationcancelled is not None:
        data["reservationdate"] = ""
        al.debug("movement has no reserve or cancelled date",
                 "movement.validate_movement_form_data", dbo)
    # Animals are always required
    if animalid == 0:
        al.debug("movement has no animal",
                 "movement.validate_movement_form_data", dbo)
        raise utils.ASMValidationError(i18n._("Movements require an animal",
                                              l))
    # Owners are required unless type is escaped, stolen or released
    if personid == 0 and movementtype != ESCAPED and movementtype != STOLEN and movementtype != RELEASED:
        al.debug("movement has no person and is not ESCAPED|STOLEN|RELEASED",
                 "movement.validate_movement_form_data", dbo)
        raise utils.ASMValidationError(
            i18n._("A person is required for this movement type.", l))
    # Is the movement number unique?
    if 0 != db.query_int(
            dbo,
            "SELECT COUNT(*) FROM adoption WHERE AdoptionNumber LIKE '%s' AND ID <> %d"
            % (adoptionno, movementid)):
        raise utils.ASMValidationError(
            i18n._("Movement numbers must be unique.", l))
    # If we're updating an existing record, we only need to continue validation
    # if one of the important fields has changed (movement date/type, return date, reservation, animal)
    if movement is not None:
        if movementtype == movement[
                "MOVEMENTTYPE"] and movementdate == movement[
                    "MOVEMENTDATE"] and returndate == movement[
                        "RETURNDATE"] and reservationdate == movement[
                            "RESERVATIONDATE"] and animalid == movement[
                                "ANIMALID"]:
            al.debug(
                "movement type, dates and animalid have not changed. Abandoning further validation",
                "movement.validate_movement_form_data", dbo)
            return
    # If the animal is held in case of reclaim, it can't be adopted
    if movementtype == ADOPTION:
        if 1 == db.query_int(
                dbo, "SELECT IsHold FROM animal WHERE ID = %d" % animalid):
            al.debug("movement is adoption and the animal is on hold",
                     "movement.validate_movement_form_data", dbo)
            raise utils.ASMValidationError(
                i18n._("This animal is currently held and cannot be adopted.",
                       l))
    # If it's a foster movement, make sure the owner is a fosterer
    if movementtype == FOSTER:
        if 0 == db.query_int(
                dbo, "SELECT IsFosterer FROM owner WHERE ID = %d" % personid):
            al.debug("movement is a foster and the person is not a fosterer.",
                     "movement.validate_movement_form_data", dbo)
            raise utils.ASMValidationError(
                i18n._(
                    "This person is not flagged as a fosterer and cannot foster animals.",
                    l))
    # If it's a retailer movement, make sure the owner is a retailer
    if movementtype == RETAILER:
        if 0 == db.query_int(
                dbo, "SELECT IsRetailer FROM owner WHERE ID = %d" % personid):
            al.debug(
                "movement is a retailer and the person is not a retailer.",
                "movement.validate_movement_form_data", dbo)
            raise utils.ASMValidationError(
                i18n._(
                    "This person is not flagged as a retailer and cannot handle retailer movements.",
                    l))
    # If a retailer is selected, make sure it's an adoption
    if retailerid != 0 and movementtype != ADOPTION:
        al.debug("movement has a retailerid set and this is not an adoption.",
                 "movement.validate_movement_form_data", dbo)
        raise utils.ASMValidationError(
            i18n._("From retailer is only valid on adoption movements.", l))
    # If a retailer is selected, make sure there's been a retailer movement in this animal's history
    if retailerid != 0:
        if 0 == db.query_int(
                dbo,
                "SELECT COUNT(*) FROM adoption WHERE AnimalID = %d AND MovementType = %d"
                % (animalid, RETAILER)):
            al.debug(
                "movement has a retailerid set but has never been to a retailer.",
                "movement.validate_movement_form_data", dbo)
            raise utils.ASMValidationError(
                i18n._(
                    "This movement cannot be from a retailer when the animal has no prior retailer movements.",
                    l))
    # You can't have a return without a movement
    if movementdate is None and returndate is not None:
        al.debug("movement is returned without a movement date.",
                 "movement.validate_movement_form_data", dbo)
        raise utils.ASMValidationError(
            i18n._("You can't have a return without a movement.", l))
    # Return should be after or same day as movement
    if movementdate is not None and returndate != None and movementdate > returndate:
        al.debug("movement return date is before the movement date.",
                 "movement.validate_movement_form_data", dbo)
        raise utils.ASMValidationError(
            i18n._("Return date cannot be before the movement date.", l))
    # Can't have multiple open movements
    if movementdate is not None:
        existingopen = db.query_int(dbo, "SELECT COUNT(*) FROM adoption WHERE MovementDate Is Not Null AND " \
            "ReturnDate Is Null AND AnimalID = %d AND ID <> %d" % (animalid, movementid))
        if existingopen > 0:
            al.debug(
                "movement is open and animal already has another open movement.",
                "movement.validate_movement_form_data", dbo)
            raise utils.ASMValidationError(
                i18n._("An animal cannot have multiple open movements.", l))
    # If we have a movement and return, is there another movement with a
    # movementdate between the movement and return date on this one?
    if movementdate is not None and returndate != None:
        clash = db.query_int(dbo, "SELECT COUNT(*) FROM adoption WHERE " \
        "AnimalID = %d AND ID <> %d AND ((ReturnDate > %s AND ReturnDate < %s) " \
        "OR (MovementDate < %s AND MovementDate > %s))" % ( animalid, movementid,
        db.dd(movementdate), db.dd(returndate), db.dd(returndate), db.dd(movementdate) ))
        if clash > 0:
            al.debug("movement dates overlap an existing movement.",
                     "movement.validate_movement_form_data", dbo)
            raise utils.ASMValidationError(
                i18n._("Movement dates clash with an existing movement.", l))
    # Does this movement date fall within the date range of an already
    # returned movement for the same animal?
    if movementdate is not None and returndate is None:
        clash = db.query_int(dbo, "SELECT COUNT(*) FROM adoption WHERE AnimalID = %d AND ID <> %d AND " \
        "MovementDate Is Not Null AND ReturnDate Is Not Null AND " \
        "%s > MovementDate AND %s < ReturnDate" % ( animalid, movementid, db.dd(movementdate), db.dd(movementdate)))
        if clash > 0:
            al.debug("movement dates overlap an existing movement.",
                     "movement.validate_movement_form_data", dbo)
            raise utils.ASMValidationError(
                i18n._("Movement dates clash with an existing movement.", l))
    # If there's a cancelled reservation, make sure it's after the reserve date
    if reservationdate is not None and reservationcancelled != None and reservationcancelled < reservationdate:
        al.debug("reserve date is after cancelled date.",
                 "movement.validate_movement_form_data", dbo)
        raise utils.ASMValidationError(
            i18n._("Reservation date cannot be after cancellation date.", l))
    # If this is a new reservation, make sure there's no open movement (fosters do not count)
    if movementid == 0 and movementtype == 0 and movementdate is None and reservationdate is not None:
        om = db.query_int(dbo, "SELECT COUNT(*) FROM adoption WHERE AnimalID = %d AND " \
            "MovementDate Is Not Null AND ReturnDate Is Null AND MovementType <> 2" % animalid)
        if om > 0:
            al.debug(
                "movement is a reservation but animal has active movement.",
                "movement.validate_movement_form_data", dbo)
            raise utils.ASMValidationError(
                i18n._("Can't reserve an animal that has an active movement.",
                       l))
    # Make sure the adoption number is unique
    an = db.query_int(dbo, "SELECT COUNT(*) FROM adoption WHERE ID <> %d AND " \
        "AdoptionNumber LIKE %s" % ( movementid, utils.df_t(data, "adoptionno" )))
    if an > 0:
        al.debug("movement number is not unique.",
                 "movement.validate_movement_form_data", dbo)
        raise utils.ASMValidationError(
            i18n._("The movement number '{0}' is not unique.",
                   l).format(utils.df_ks(data, "adoptionno")))
    # If this is an adoption and the owner had some criteria, expire them
    if movementtype == ADOPTION and personid > 0:
        al.debug("movement is an adoption, expiring person criteria.",
                 "movement.validate_movement_form_data", dbo)
        sql = "UPDATE owner SET MatchActive = 0, MatchExpires = %s WHERE ID = %d" % (
            db.dd(i18n.now(dbo.timezone)), int(personid))
        db.execute(dbo, sql)
    # If the option to cancel reserves on adoption is set, cancel any outstanding reserves for the animal
    if movementtype == ADOPTION and configuration.cancel_reserves_on_adoption(
            dbo):
        al.debug("movement is an adoption, cancelling outstanding reserves.",
                 "movement.validate_movement_form_data", dbo)
        sql = "UPDATE adoption SET ReservationCancelledDate = %s " \
            "WHERE ReservationCancelledDate Is Null AND MovementDate Is Null " \
            "AND AnimalID = %d AND ID <> %d" % ( db.dd(i18n.now(dbo.timezone)), animalid, int(movementid) )
        db.execute(dbo, sql)
Пример #4
0
def validate_movement_form_data(dbo, data):
    """
    Verifies that form data is valid for a movement
    """
    l = dbo.locale
    movementid = utils.df_ki(data, "movementid")
    movement = None
    if movementid != 0: movement = db.query(dbo, "SELECT * FROM adoption WHERE ID = %d" % movementid)[0]
    adoptionno = utils.df_ks(data, "adoptionno")
    movementtype = utils.df_ki(data, "type")
    movementdate = utils.df_kd(data, "movementdate", l)
    returndate = utils.df_kd(data, "returndate", l)
    reservationdate = utils.df_kd(data, "reservationdate", l)
    reservationcancelled = utils.df_kd(data, "reservationcancelled", l)
    personid = utils.df_ki(data, "person")
    animalid = utils.df_ki(data, "animal")
    retailerid = utils.df_ki(data, "retailer")
    al.debug("validating saved movement %d for animal %d" % (movementid, animalid), "movement.validate_movement_form_data", dbo)
    # If we have a date but no type, get rid of it
    if movementdate is None and movementtype == 0:
        data["movementdate"] = ""
        al.debug("blank date and type", "movement.validate_movement_form_data", dbo)
    # If we've got a type, but no date, default today
    if movementtype > 0 and movementdate is None:
        movementdate = i18n.now()
        data["movementdate"] = i18n.python2display(l, movementdate)
        al.debug("type set and no date, defaulting today", "movement.validate_movement_form_data", dbo)
    # If we've got a reserve cancellation without a reserve, remove it
    if reservationdate is None and reservationcancelled is not None:
        data["reservationdate"] = ""
        al.debug("movement has no reserve or cancelled date", "movement.validate_movement_form_data", dbo)
    # Animals are always required
    if animalid == 0:
        al.debug("movement has no animal", "movement.validate_movement_form_data", dbo)
        raise utils.ASMValidationError(i18n._("Movements require an animal", l))
    # Owners are required unless type is escaped, stolen or released
    if personid == 0 and movementtype != ESCAPED and movementtype != STOLEN and movementtype != RELEASED:
        al.debug("movement has no person and is not ESCAPED|STOLEN|RELEASED", "movement.validate_movement_form_data", dbo)
        raise utils.ASMValidationError(i18n._("A person is required for this movement type.", l))
    # Is the movement number unique?
    if 0 != db.query_int(dbo, "SELECT COUNT(*) FROM adoption WHERE AdoptionNumber LIKE '%s' AND ID <> %d" % (adoptionno, movementid)):
        raise utils.ASMValidationError(i18n._("Movement numbers must be unique.", l))
    # If we're updating an existing record, we only need to continue validation
    # if one of the important fields has changed (movement date/type, return date, reservation, animal)
    if movement is not None:
        if movementtype == movement["MOVEMENTTYPE"] and movementdate == movement["MOVEMENTDATE"] and returndate == movement["RETURNDATE"] and reservationdate == movement["RESERVATIONDATE"] and animalid == movement["ANIMALID"]:
            al.debug("movement type, dates and animalid have not changed. Abandoning further validation", "movement.validate_movement_form_data", dbo)
            return
    # If the animal is held in case of reclaim, it can't be adopted
    if movementtype == ADOPTION:
        if 1 == db.query_int(dbo, "SELECT IsHold FROM animal WHERE ID = %d" % animalid):
            al.debug("movement is adoption and the animal is on hold", "movement.validate_movement_form_data", dbo)
            raise utils.ASMValidationError(i18n._("This animal is currently held and cannot be adopted.", l))
    # If it's a foster movement, make sure the owner is a fosterer
    if movementtype == FOSTER:
        if 0 == db.query_int(dbo, "SELECT IsFosterer FROM owner WHERE ID = %d" % personid):
            al.debug("movement is a foster and the person is not a fosterer.", "movement.validate_movement_form_data", dbo)
            raise utils.ASMValidationError(i18n._("This person is not flagged as a fosterer and cannot foster animals.", l))
    # If it's a retailer movement, make sure the owner is a retailer
    if movementtype == RETAILER:
        if 0 == db.query_int(dbo, "SELECT IsRetailer FROM owner WHERE ID = %d" % personid):
            al.debug("movement is a retailer and the person is not a retailer.", "movement.validate_movement_form_data", dbo)
            raise utils.ASMValidationError(i18n._("This person is not flagged as a retailer and cannot handle retailer movements.", l))
    # If a retailer is selected, make sure it's an adoption
    if retailerid != 0 and movementtype != ADOPTION:
        al.debug("movement has a retailerid set and this is not an adoption.", "movement.validate_movement_form_data", dbo)
        raise utils.ASMValidationError(i18n._("From retailer is only valid on adoption movements.", l))
    # If a retailer is selected, make sure there's been a retailer movement in this animal's history
    if retailerid != 0:
        if 0 == db.query_int(dbo, "SELECT COUNT(*) FROM adoption WHERE AnimalID = %d AND MovementType = %d" % ( animalid, RETAILER )):
            al.debug("movement has a retailerid set but has never been to a retailer.", "movement.validate_movement_form_data", dbo)
            raise utils.ASMValidationError(i18n._("This movement cannot be from a retailer when the animal has no prior retailer movements.", l))
    # You can't have a return without a movement
    if movementdate is None and returndate is not None:
        al.debug("movement is returned without a movement date.", "movement.validate_movement_form_data", dbo)
        raise utils.ASMValidationError(i18n._("You can't have a return without a movement.", l))
    # Return should be after or same day as movement
    if movementdate is not None and returndate != None and movementdate > returndate:
        al.debug("movement return date is before the movement date.", "movement.validate_movement_form_data", dbo)
        raise utils.ASMValidationError(i18n._("Return date cannot be before the movement date.", l))
    # Can't have multiple open movements
    if movementdate is not None:
        existingopen = db.query_int(dbo, "SELECT COUNT(*) FROM adoption WHERE MovementDate Is Not Null AND " \
            "ReturnDate Is Null AND AnimalID = %d AND ID <> %d" % (animalid, movementid))
        if existingopen > 0:
            al.debug("movement is open and animal already has another open movement.", "movement.validate_movement_form_data", dbo)
            raise utils.ASMValidationError(i18n._("An animal cannot have multiple open movements.", l))
    # If we have a movement and return, is there another movement with a 
    # movementdate between the movement and return date on this one?
    if movementdate is not None and returndate != None:
        clash = db.query_int(dbo, "SELECT COUNT(*) FROM adoption WHERE " \
        "AnimalID = %d AND ID <> %d AND ((ReturnDate > %s AND ReturnDate < %s) " \
        "OR (MovementDate < %s AND MovementDate > %s))" % ( animalid, movementid, 
        db.dd(movementdate), db.dd(returndate), db.dd(returndate), db.dd(movementdate) ))
        if clash > 0:
            al.debug("movement dates overlap an existing movement.", "movement.validate_movement_form_data", dbo)
            raise utils.ASMValidationError(i18n._("Movement dates clash with an existing movement.", l))
    # Does this movement date fall within the date range of an already
    # returned movement for the same animal?
    if movementdate is not None and returndate is None:
        clash = db.query_int(dbo, "SELECT COUNT(*) FROM adoption WHERE AnimalID = %d AND ID <> %d AND " \
        "MovementDate Is Not Null AND ReturnDate Is Not Null AND " \
        "%s > MovementDate AND %s < ReturnDate" % ( animalid, movementid, db.dd(movementdate), db.dd(movementdate)))
        if clash > 0:
            al.debug("movement dates overlap an existing movement.", "movement.validate_movement_form_data", dbo)
            raise utils.ASMValidationError(i18n._("Movement dates clash with an existing movement.", l))
    # If there's a cancelled reservation, make sure it's after the reserve date
    if reservationdate is not None and reservationcancelled != None and reservationcancelled < reservationdate:
        al.debug("reserve date is after cancelled date.", "movement.validate_movement_form_data", dbo)
        raise utils.ASMValidationError(i18n._("Reservation date cannot be after cancellation date.", l))
    # If this is a new reservation, make sure there's no open movement (fosters do not count)
    if movementid == 0 and movementtype == 0 and movementdate is None and reservationdate is not None:
        om = db.query_int(dbo, "SELECT COUNT(*) FROM adoption WHERE AnimalID = %d AND " \
            "MovementDate Is Not Null AND ReturnDate Is Null AND MovementType <> 2" % animalid)
        if om > 0:
            al.debug("movement is a reservation but animal has active movement.", "movement.validate_movement_form_data", dbo)
            raise utils.ASMValidationError(i18n._("Can't reserve an animal that has an active movement.", l))
    # Make sure the adoption number is unique
    an = db.query_int(dbo, "SELECT COUNT(*) FROM adoption WHERE ID <> %d AND " \
        "AdoptionNumber LIKE %s" % ( movementid, utils.df_t(data, "adoptionno" )))
    if an > 0:
        al.debug("movement number is not unique.", "movement.validate_movement_form_data", dbo)
        raise utils.ASMValidationError(i18n._("The movement number '{0}' is not unique.", l).format(utils.df_ks(data, "adoptionno")))
    # If this is an adoption and the owner had some criteria, expire them
    if movementtype == ADOPTION and personid > 0:
        al.debug("movement is an adoption, expiring person criteria.", "movement.validate_movement_form_data", dbo)
        sql = "UPDATE owner SET MatchActive = 0, MatchExpires = %s WHERE ID = %d" % ( db.dd(i18n.now(dbo.timezone)), int(personid) )
        db.execute(dbo, sql)
    # If the option to cancel reserves on adoption is set, cancel any outstanding reserves for the animal
    if movementtype == ADOPTION and configuration.cancel_reserves_on_adoption(dbo):
        al.debug("movement is an adoption, cancelling outstanding reserves.", "movement.validate_movement_form_data", dbo)
        sql = "UPDATE adoption SET ReservationCancelledDate = %s " \
            "WHERE ReservationCancelledDate Is Null AND MovementDate Is Null " \
            "AND AnimalID = %d AND ID <> %d" % ( db.dd(i18n.now(dbo.timezone)), animalid, int(movementid) )
        db.execute(dbo, sql)
Пример #5
0
def insert_reclaim_from_form(dbo, username, post):
    """f
    Inserts a movement from the workflow adopt an animal screen.
    Returns the new movement id
    """
    l = dbo.locale
    # Validate that we have a movement date before doing anthing
    if None is post.date("movementdate"):
        raise utils.ASMValidationError(i18n._("Reclaim movements must have a valid reclaim date.", l))
    # Get the animal record for this reclaim
    a = animal.get_animal(dbo, post.integer("animal"))
    if a is None:
        raise utils.ASMValidationError("Reclaim POST has an invalid animal ID: %d" % post.integer("animal"))
    al.debug("Creating reclaim for %d (%s - %s)" % (a.ID, a.SHELTERCODE, a.ANIMALNAME), "movement.insert_reclaim_from_form", dbo)
    # Prepare a dictionary of data for the movement table via insert_movement_from_form
    move_dict = {
        "person"                : post["person"],
        "animal"                : post["animal"],
        "adoptionno"            : post["movementnumber"],
        "movementdate"          : post["movementdate"],
        "type"                  : str(RECLAIMED),
        "donation"              : post["amount"],
        "returncategory"        : configuration.default_return_reason(dbo)
    }
    # Is this animal currently on foster? If so, return the foster
    fm = get_animal_movements(dbo, post.integer("animal"))
    for m in fm:
        if m.MOVEMENTTYPE == FOSTER and m.RETURNDATE is None:
            return_movement(dbo, m.ID, post.integer("animal"), post.date("movementdate"))
    # Is this animal current at a retailer? If so, return it from the
    # retailer and set the originalretailermovement and retailerid fields
    # on our new adoption movement so it can be linked back
    for m in fm:
        if m.MOVEMENTTYPE == RETAILER and m.RETURNDATE is None:
            return_movement(dbo, m["ID"], post.integer("animal"), post.date("movementdate"))
            move_dict["originalretailermovement"] = str(m.ID)
            move_dict["retailer"] = str(m.OWNERID)
    # If the animal was flagged as not available for adoption, then it
    # shouldn't be since we've just reclaimed it.
    dbo.update("animal", post.integer("animal"), { "IsNotAvailableForAdoption": 0 })
    # Is the animal reserved? Should clear it if so
    cancel_reserves = configuration.cancel_reserves_on_adoption(dbo)
    for m in fm:
        if cancel_reserves and m.MOVEMENTTYPE == NO_MOVEMENT and m.RESERVATIONDATE is not None \
            and m.RESERVATIONCANCELLEDDATE is None:
            dbo.update("adoption", m.ID, { "ReservationCancelledDate": post.date("movementdate") }, username)
    movementid = insert_movement_from_form(dbo, username, utils.PostedData(move_dict, l))
    # Create any payments
    financial.insert_donations_from_form(dbo, username, post, post["movementdate"], False, post["person"], post["animal"], movementid) 
    # Then any boarding cost record
    cost_amount = post.integer("costamount")
    cost_type = post["costtype"]
    cost_create = post.integer("costcreate")
    if cost_amount > 0 and cost_type != "" and cost_create == 1:
        boc_dict = {
            "animalid"          : post["animal"],
            "type"              : cost_type,
            "costdate"          : post["movementdate"],
            "costpaid"          : post["movementdate"],
            "cost"              : post["costamount"]
        }
        animal.insert_cost_from_form(dbo, username, utils.PostedData(boc_dict, l))
    return movementid
Пример #6
0
def insert_adoption_from_form(dbo, username, post, creating = [], create_payments = True):
    """
    Inserts a movement from the workflow adopt an animal screen.
    Returns the new movement id
    creating is an ongoing list of animals we're already going to
    create adoptions for. It prevents a never ending recursive loop
    of animal1 being bonded to animal2 that's bonded to animal1, etc.
    create_payments is True if we should create payments - don't do this
    for bonded animals or we'll double up all the payments.
    """
    l = dbo.locale
    # Validate that we have a movement date before doing anthing
    if None is post.date("movementdate"):
        raise utils.ASMValidationError(i18n._("Adoption movements must have a valid adoption date.", l))
    # Get the animal record for this adoption
    a = animal.get_animal(dbo, post.integer("animal"))
    if a is None:
        raise utils.ASMValidationError("Adoption POST has an invalid animal ID: %d" % post.integer("animal"))
    al.debug("Creating adoption for %d (%s - %s)" % (a["ID"], a["SHELTERCODE"], a["ANIMALNAME"]), "movement.insert_adoption_from_form", dbo)
    creating.append(a.ID)
    # If the animal is bonded to other animals, we call this function
    # again with a copy of the data and the bonded animal substituted
    # so we can create their adoption records too. We only do this if
    # the other animals are still on shelter (therefore alive).
    if a.BONDEDANIMALID is not None and a.BONDEDANIMALID != 0 and a.BONDEDANIMAL1ARCHIVED == 0 and a.BONDEDANIMALID not in creating:
        al.debug("Found bond to animal %d, creating adoption..." % a.BONDEDANIMALID, "movement.insert_adoption_from_form", dbo)
        newdata = dict(post.data)
        newdata["animal"] = str(a.BONDEDANIMALID)
        insert_adoption_from_form(dbo, username, utils.PostedData(newdata, dbo.locale), creating, create_payments = False)
    if a.BONDEDANIMAL2ID is not None and a.BONDEDANIMAL2ID != 0 and a.BONDEDANIMAL2ARCHIVED == 0 and a.BONDEDANIMAL2ID not in creating:
        al.debug("Found bond to animal %d, creating adoption..." % a.BONDEDANIMAL2ID, "movement.insert_adoption_from_form", dbo)
        newdata = dict(post.data)
        newdata["animal"] = str(a.BONDEDANIMAL2ID)
        insert_adoption_from_form(dbo, username, utils.PostedData(newdata, dbo.locale), creating, create_payments = False)
    cancel_reserves = configuration.cancel_reserves_on_adoption(dbo)
    # Prepare a dictionary of data for the movement table via insert_movement_from_form
    move_dict = {
        "person"                : post["person"],
        "animal"                : post["animal"],
        "adoptionno"            : post["movementnumber"],
        "movementdate"          : post["movementdate"],
        "type"                  : str(ADOPTION),
        "donation"              : post["amount"],
        "insurance"             : post["insurance"],
        "returncategory"        : configuration.default_return_reason(dbo),
        "trial"                 : post["trial"],
        "trialenddate"          : post["trialenddate"]
    }
    # Is this animal currently on foster? If so, return the foster
    fm = get_animal_movements(dbo, post.integer("animal"))
    for m in fm:
        if m.MOVEMENTTYPE == FOSTER and m.RETURNDATE is None:
            return_movement(dbo, m["ID"], post.integer("animal"), post.date("movementdate"))
    # Is this animal current at a retailer? If so, return it from the
    # retailer and set the originalretailermovement and retailerid fields
    # on our new adoption movement so it can be linked back
    for m in fm:
        if m.MOVEMENTTYPE == RETAILER and m.RETURNDATE is None:
            return_movement(dbo, m.ID, post.integer("animal"), post.date("movementdate"))
            move_dict["originalretailermovement"] = str(m.ID)
            move_dict["retailer"] = str(m["OWNERID"])
    # Did we say we'd like to flag the owner as homechecked?
    if post.boolean("homechecked") == 1:
        dbo.update("owner", post.integer("person"), { "IDCheck": 1, "DateLastHomeChecked": dbo.today() }, username)
    # If the animal was flagged as not available for adoption, then it
    # shouldn't be since we've just adopted it.
    dbo.update("animal", a.ID, { "IsNotAvailableForAdoption": 0 })
    # Is the animal reserved to the person adopting? 
    movementid = 0
    for m in fm:
        if m.MOVEMENTTYPE == NO_MOVEMENT and m.RESERVATIONDATE is not None \
            and m.RESERVATIONCANCELLEDDATE is None and m.ANIMALID == post.integer("animal") \
            and m.OWNERID == post.integer("person"):
            # yes - update the existing movement
            movementid = m.ID
            move_dict["movementid"] = str(movementid)
            move_dict["adoptionno"] = utils.padleft(movementid, 6)
            move_dict["reservationdate"] = str(i18n.python2display(l, m.RESERVATIONDATE))
            move_dict["comments"] = utils.nulltostr(m.COMMENTS)
            break
        elif cancel_reserves and m.MOVEMENTTYPE == NO_MOVEMENT and m.RESERVATIONDATE is not None \
            and m.RESERVATIONCANCELLEDDATE is None:
            # no, but it's reserved to someone else and we're cancelling
            # reserves on adoption
            dbo.update("adoption", m.ID, { "ReservationCancelledDate": post.date("movementdate") }, username)
    if movementid != 0:
        update_movement_from_form(dbo, username, utils.PostedData(move_dict, l))
    else:
        movementid = insert_movement_from_form(dbo, username, utils.PostedData(move_dict, l))
    # Create any payments
    if create_payments:
        financial.insert_donations_from_form(dbo, username, post, post["movementdate"], False, post["person"], post["animal"], movementid) 
    # Then any boarding cost record
    cost_amount = post.integer("costamount")
    cost_type = post["costtype"]
    cost_create = post.integer("costcreate")
    if cost_amount > 0 and cost_type != "" and cost_create == 1:
        boc_dict = {
            "animalid"          : post["animal"],
            "type"              : cost_type,
            "costdate"          : post["movementdate"],
            "costpaid"          : post["movementdate"],
            "cost"              : post["costamount"]
        }
        animal.insert_cost_from_form(dbo, username, utils.PostedData(boc_dict, l))
    return movementid
def insert_reclaim_from_form(dbo, username, post):
    """
    Inserts a movement from the workflow adopt an animal screen.
    Returns the new movement id
    """
    l = dbo.locale
    # Validate that we have a movement date before doing anthing
    if None == post.date("movementdate"):
        raise utils.ASMValidationError(
            i18n._("Reclaim movements must have a valid reclaim date.", l))
    # Get the animal record for this reclaim
    a = animal.get_animal(dbo, post.integer("animal"))
    if a is None:
        raise utils.ASMValidationError(
            "Reclaim POST has an invalid animal ID: %d" %
            post.integer("animal"))
    al.debug(
        "Creating reclaim for %d (%s - %s)" %
        (a["ID"], a["SHELTERCODE"], a["ANIMALNAME"]),
        "movement.insert_reclaim_from_form", dbo)
    # Prepare a dictionary of data for the movement table via insert_movement_from_form
    move_dict = {
        "person": post["person"],
        "animal": post["animal"],
        "adoptionno": post["movementnumber"],
        "movementdate": post["movementdate"],
        "type": str(RECLAIMED),
        "donation": post["amount"],
        "returncategory": configuration.default_return_reason(dbo)
    }
    # Is this animal currently on foster? If so, return the foster
    fm = get_animal_movements(dbo, post.integer("animal"))
    for m in fm:
        if m["MOVEMENTTYPE"] == FOSTER and m["RETURNDATE"] is None:
            return_movement(dbo, m["ID"], post.integer("animal"),
                            post.date("movementdate"))
    # Is this animal current at a retailer? If so, return it from the
    # retailer and set the originalretailermovement and retailerid fields
    # on our new adoption movement so it can be linked back
    for m in fm:
        if m["MOVEMENTTYPE"] == RETAILER and m["RETURNDATE"] is None:
            return_movement(dbo, m["ID"], post.integer("animal"),
                            post.date("movementdate"))
            move_dict["originalretailermovement"] = str(m["ID"])
            move_dict["retailer"] = str(m["OWNERID"])
    # If the animal was flagged as not available for adoption, then it
    # shouldn't be since we've just reclaimed it.
    db.execute(
        dbo, "UPDATE animal SET IsNotAvailableForAdoption = 0 WHERE ID = %s" %
        post["animal"])
    # Is the animal reserved? Should clear it if so
    cancel_reserves = configuration.cancel_reserves_on_adoption(dbo)
    for m in fm:
        if cancel_reserves and m["MOVEMENTTYPE"] == NO_MOVEMENT and m["RESERVATIONDATE"] is not None \
            and m["RESERVATIONCANCELLEDDATE"] is None:
            db.execute(dbo, "UPDATE adoption SET ReservationCancelledDate = %s WHERE ID = %d" % \
                ( post.db_date("movementdate"), m["ID"] ))
    movementid = insert_movement_from_form(dbo, username,
                                           utils.PostedData(move_dict, l))
    # Create the donation if there is one
    donation_amount = post.integer("amount")
    if donation_amount > 0:
        due = ""
        received = post["movementdate"]
        if configuration.movement_donations_default_due(dbo):
            due = post["movementdate"]
            received = ""
        don_dict = {
            "person": post["person"],
            "animal": post["animal"],
            "movement": str(movementid),
            "type": post["donationtype"],
            "payment": post["payment"],
            "destaccount": post["destaccount"],
            "frequency": "0",
            "amount": post["amount"],
            "due": due,
            "received": received,
            "giftaid": post["giftaid"]
        }
        financial.insert_donation_from_form(dbo, username,
                                            utils.PostedData(don_dict, l))
    # Then any boarding cost record
    cost_amount = post.integer("costamount")
    cost_type = post["costtype"]
    cost_create = post.integer("costcreate")
    if cost_amount > 0 and cost_type != "" and cost_create == 1:
        boc_dict = {
            "animalid": post["animal"],
            "type": cost_type,
            "costdate": post["movementdate"],
            "costpaid": post["movementdate"],
            "cost": post["costamount"]
        }
        animal.insert_cost_from_form(dbo, username,
                                     utils.PostedData(boc_dict, l))
    return movementid
Пример #8
0
def insert_reclaim_from_form(dbo, username, post):
    """f
    Inserts a movement from the workflow adopt an animal screen.
    Returns the new movement id
    """
    l = dbo.locale
    # Validate that we have a movement date before doing anthing
    if None == post.date("movementdate"):
        raise utils.ASMValidationError(i18n._("Reclaim movements must have a valid reclaim date.", l))
    # Get the animal record for this reclaim
    a = animal.get_animal(dbo, post.integer("animal"))
    if a is None:
        raise utils.ASMValidationError("Reclaim POST has an invalid animal ID: %d" % post.integer("animal"))
    al.debug("Creating reclaim for %d (%s - %s)" % (a["ID"], a["SHELTERCODE"], a["ANIMALNAME"]), "movement.insert_reclaim_from_form", dbo)
    # Prepare a dictionary of data for the movement table via insert_movement_from_form
    move_dict = {
        "person"                : post["person"],
        "animal"                : post["animal"],
        "adoptionno"            : post["movementnumber"],
        "movementdate"          : post["movementdate"],
        "type"                  : str(RECLAIMED),
        "donation"              : post["amount"],
        "returncategory"        : configuration.default_return_reason(dbo)
    }
    # Is this animal currently on foster? If so, return the foster
    fm = get_animal_movements(dbo, post.integer("animal"))
    for m in fm:
        if m["MOVEMENTTYPE"] == FOSTER and m["RETURNDATE"] is None:
            return_movement(dbo, m["ID"], post.integer("animal"), post.date("movementdate"))
    # Is this animal current at a retailer? If so, return it from the
    # retailer and set the originalretailermovement and retailerid fields
    # on our new adoption movement so it can be linked back
    for m in fm:
        if m["MOVEMENTTYPE"] == RETAILER and m["RETURNDATE"] is None:
            return_movement(dbo, m["ID"], post.integer("animal"), post.date("movementdate"))
            move_dict["originalretailermovement"] = str(m["ID"])
            move_dict["retailer"] = str(m["OWNERID"])
    # If the animal was flagged as not available for adoption, then it
    # shouldn't be since we've just reclaimed it.
    db.execute(dbo, "UPDATE animal SET IsNotAvailableForAdoption = 0 WHERE ID = %s" % post["animal"])
    # Is the animal reserved? Should clear it if so
    cancel_reserves = configuration.cancel_reserves_on_adoption(dbo)
    for m in fm:
        if cancel_reserves and m["MOVEMENTTYPE"] == NO_MOVEMENT and m["RESERVATIONDATE"] is not None \
            and m["RESERVATIONCANCELLEDDATE"] is None:
            db.execute(dbo, "UPDATE adoption SET ReservationCancelledDate = %s WHERE ID = %d" % \
                ( post.db_date("movementdate"), m["ID"] ))
    movementid = insert_movement_from_form(dbo, username, utils.PostedData(move_dict, l))
    # Create any payments
    financial.insert_donations_from_form(dbo, username, post, post["movementdate"], False, post["person"], post["animal"], movementid) 
    # Then any boarding cost record
    cost_amount = post.integer("costamount")
    cost_type = post["costtype"]
    cost_create = post.integer("costcreate")
    if cost_amount > 0 and cost_type != "" and cost_create == 1:
        boc_dict = {
            "animalid"          : post["animal"],
            "type"              : cost_type,
            "costdate"          : post["movementdate"],
            "costpaid"          : post["movementdate"],
            "cost"              : post["costamount"]
        }
        animal.insert_cost_from_form(dbo, username, utils.PostedData(boc_dict, l))
    return movementid