Te paso un ejemplo bien completo de cómo lo hago yo:

** creacion de las tablas en sqlserver
** en la vida real tienen más campos, pero estos son los mínimos que deberían tener

CREATE TABLE [dbo].[DETALLE](
    [FECHA] [date] NOT NULL,
    [TIPODOC] [char](3) NOT NULL,
    [DOCNUM] [char](8) NOT NULL,
    [CODIGO] [char](30) NOT NULL,
    [CANTIDAD] [int] NOT NULL,
    [PRECIO] [numeric](10, 4) NOT NULL,
    [DT] [datetime] NOT NULL,
    [IDETALLE] [int] IDENTITY(1,1) NOT NULL,
    [IDCABECERA] [int] NOT NULL,
 CONSTRAINT [PK_DETALLE] PRIMARY KEY CLUSTERED
(
    [IDDETALLE] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY],
 CONSTRAINT [CN_IDDETALLE] UNIQUE NONCLUSTERED
(
    [IDDETALLE] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

CREATE TABLE [dbo].[CABECERA](
    [FECHA] [date] NOT NULL,
    [CUENTA] [char](4) NOT NULL,
    [TIPODOC] [char](3) NOT NULL,
    [DOCNUM] [char](8) NOT NULL,
    [IVA] [numeric](10, 2) NOT NULL,
    [TOTAL] [numeric](10, 2) NOT NULL,
    [IDCABECERA] [int] IDENTITY(1,1) NOT NULL,
    [DT] [datetime] NOT NULL,
 CONSTRAINT [PK_IDCABECERA] PRIMARY KEY CLUSTERED
(
    [IDCABECERA] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]


** lo siguiente se hace en Visual FoxPro

** generación de un cursor adapter para la tabla CABECERA

Local cCmd,cSch,cUFL,cUNL,lOK

Text to cCmd noshow pretext 15
SELECT
    FECHA ,
    CUENTA ,
    TIPODOC ,
    DOCNUM ,
    IVA ,
    TOTAL ,
    IDCABECERA ,
    DT
 FROM CABECERA WHERE 1=0
EndText

Text to cSch noshow pretext 15
    FECHA D,
    CUENTA C(4),
    TIPODOC C(3),
    DOCNUM C(8),
    IVA N(10,2),
    TOTAL N(10,2),
    IDCABECERA I,
    DT T
EndText


Text to cUFL noshow pretext 15
    FECHA ,
    CUENTA ,
    TIPODOC ,
    DOCNUM ,
    IVA ,
    TOTAL ,
    IDCABECERA ,
    DT
EndText

Text to cUNL noshow pretext 15
    FECHA        CABECERA.FECHA,
    CUENTA       CABECERA.CUENTA,
    TIPODOC      CABECERA.TIPODOC,
    DOCNUM       CABECERA.DOCNUM,
    IVA          CABECERA.IVA,
    TOTAL        CABECERA.TOTAL,
    IDCABECERA   CABECERA.IDCABECERA,
    DT           CABECERA.DT
EndText

cUFL        = Chrtran( cUFL, Chr(13) + Chr(10), " " )
cUNL       = Chrtran( cUNL, Chr(13) + Chr(10), " " )
cSch        = Chrtran( cSch, Chr(13) + Chr(10), " " )
cCmd      = Chrtran( cCmd, Chr(13) + Chr(10), " " )

Use in Select("")

lOK = .t.

If !PemStatus(thisform,'oCA',5)
   thisform.AddProperty('oCA')
EndIf

Try

    Thisform.oCA = Createobject("CursorAdapter")

    lOK = .t.

Catch to oErrores

    MessageBox("No se pudo generar el objeto CursorAdapter"+;
    Chr(13)+oErrores.message,16,"Atención",2000)

    lOK = .f.

Finally
EndTry


If lOK = .t.
   try
       With thisform.oCA
            .DataSourceType     = "ODBC"
            .DataSource         = thisform.nHandle
            .alias                 = "curCabecera"
            .tables             = "cabecera"
            .BufferModeOverride = 5
            .keyfieldlist        = "idcabecera"
            .sendupdates         = .T.
.usetransactions = .f. && si están en falso, no usa transacciones y funciona entonces ponerlas en manual en SQL Server
            .selectcmd             = cCmd
            .updatablefieldlist = cUFL
            .updatenamelist     = cUNL
            .cursorschema       = cSch
            .cursorfill()
       EndWith

       Select curCabecera

   Catch to oErrores

MessageBox("No se pudo generar el Cursor "+Chr(13)+oErrores.message;
        ,16,"Atención",2000)

        lOK = .f.

   endtry
endif

Return lOK

**(hacer otro cursor adapter para la tabla DETALLE y obtener el cursor curDetalle)

** la sentencia where 1=0 genera un cursor vacío. Esto es para que llenes el cursor adapter con los datos necesarios antes de
** grabar. Estos datos salen de textboxes, grillas, cálculos, etc.

** rutina de grabación

Local lOK,nPK,cCmd,nResults,lBatchMode

SQLExec(thisform.nHandle,'BEGIN TRANSACTION')
SQLSetProp(thisform.nHandle, "TRANSACTIONS", 2 ) && pone las transacciones en manuales

Select curCabecera
Scatter name oCab blank fields except idcabecera
With oCab
    .fecha    = Date()
    .cuenta   = '1234'
    .tipodoc  = 'FRA'
    .docnum   = '12345678'
    .iva      = 210.00
    .total    = 1210.00
    .dt       = Datetime()
EndWith

** grabación de la cabecera y obtención de la clave primaria generada
** el campo idcabecera es autoincremental

Insert into curCabecera from name oCab

lOK = TableUpdate(.t.,.t.,'curCabecera')

If lOK = .t.

    nPK      = 0
    nResults = 0
    cCmd     = [select @@IDENTITY as pk ]

    lBatchMode = SQLGetProp(thisform.nHandle,"BatchMode" )

    SQLSetProp(thisform.nHandle,"BatchMode" ,.f.)

    SQLExec(thisform.nHandle,cCmd,'curPK')

    Do while nResults <> 2
       nResults = SQLMoreResults(thisform.nHandle)
    enddo

    SQLSetProp(thisform.nHandle,"BatchMode" ,lBatchMode)

    nPK = curPK.pk
    Use in Select('curPK')

Select curFactura && cursor auxiliar usado en el form para insertar las lineas de detalle de la factura && tiene la misma estructura que la tabla detalle del sql server

    Scan all
         Scatter name oDet fields except iddetalle
         With oDet
              .fecha      = Date()
              .tipodoc    = 'FRA'
              .docnum     = '12345678'
              .idcabecera = nPK       && aqui pasa a ser la clave foránea
              .dt         = Datetime()
*** los campos codigo, cantidad y precio ya están en el objeto, obtenidos con SCATTER NAME
         endwith
         Insert into curDetalle from name oDet
    EndScan
    lOK = TableUpdate(.t.,.t.,'curDetalle')

endif

If lOK = .t.

   SQLCommit(thisform.nHandle)

Else

   SQLRollback(thisform.nHandle)

endif

SQLSetProp(thisform.nHandle,'Transactions',1 ) && transacciones automáticas

A los cursor adapters les ponés la propiedad usetransactions en falso, para que no maneje las transacciones directamente. Acordate que en SQL Server, las transacciones son automáticas a menos que las pongas en manual. Esto hace que cada vez que insertás,borrás o modificás un registro, se abra y cierre una transacción automáticamente. No te sirve para el caso de una factura, porque si te da error alguna de las tablas, no podés cancelar una transacción completa. En cambio, si las transacciones las ponés en manual, solamente se completa la transacción cuando le mandás un SQLCommit (o SQLRollback si falla) Tenés que poner las transacciones del SQLServer en manual antes de comenzar a grabar y luego de cerrar la transacción las ponés nuevamente en automáticas,para que el SQL Server siga funcionando normalmente

Fijate en estos artículos

http://www.universalthread.com/ViewPageArticle.aspx?ID=667

http://www.universalthread.com/ViewPageArticle.aspx?ID=817

Rafael Copquin


Responder a